
# 상태

Pitmaster는 세 가지 상태, 즉 HEAD 커밋의 트리, 인덱스(스테이징 영역), 작업 트리(디스크의 실제 파일)를 비교해 작업 트리 상태를 계산합니다. CLI는 이를 바탕으로 `git status` 형식의 요약을 출력하고, `statusPorcelainV2()`는 git의 porcelain v2 형식을 출력합니다.

## 기본 사용법

```php
$entries = $repo->status();

foreach ($entries as $entry) {
    echo $entry->shortFormat() . "\n";
}
```

`shortFormat()`는 git의 짧은 형식을 출력합니다:

```
M  src/Modified.php          (staged modification)
 M src/WorktreeModified.php  (unstaged modification)
A  src/NewFile.php           (staged new file)
 D src/Deleted.php           (deleted in worktree)
?? untracked.txt             (untracked file)
```

## StatusEntry

각 파일의 상태는 `StatusEntry`로 표시됩니다:

```php
use Pitmaster\Status\StatusEntry;

$entry->path;       // File path relative to repo root
$entry->index;      // FileStatus: HEAD-to-index state
$entry->worktree;   // FileStatus: index-to-worktree state
$entry->origPath;   // Original path for renamed entries, otherwise null
$entry->renameScore; // Similarity score for renamed entries, otherwise null
$entry->shortFormat(); // "XY path", "?? path", or "R  old -> new"
```

## FileStatus 열거형

```php
use Pitmaster\Status\FileStatus;

FileStatus::Added;      // 'A' - new file
FileStatus::Modified;   // 'M' - content changed
FileStatus::Deleted;    // 'D' - file removed
FileStatus::Renamed;    // 'R' - file renamed
FileStatus::Copied;     // 'C' - file copied
FileStatus::Unmerged;   // 'U' - unresolved merge conflict
FileStatus::Untracked;  // '?' - not in index
FileStatus::Ignored;    // '!' - matched by .gitignore
FileStatus::Unmodified; // ' ' - no change
```

`status()`는 무시된 파일을 보고하지 않으므로 결과에 `Ignored`가 나타나지 않습니다.

## Porcelain v2 출력

`git status --porcelain=v2`와 일치하는 기계 판독 가능 상태 출력을 얻으려면:

```php
echo $repo->statusPorcelainV2();
```

출력:

```
1 .M N... 100644 100644 100644 ce013625030ba8dba906f756967f9e9ca394464a ce013625030ba8dba906f756967f9e9ca394464a modified-file.txt
? untracked-file.txt
```

추적 중인 항목이 먼저 나오고 추적되지 않는 항목이 그 뒤에 나옵니다. 이름이 변경된 항목은 `2` 줄을, 충돌은 `u` 줄을 사용합니다. `git status --porcelain=v2 --branch`의 `# branch` 헤더는 출력되지 않으며, 무시된 파일은 목록에 포함되지 않습니다.

## 상태 계산 방식

`WorkingTreeStatus` 클래스는 세 단계 비교를 수행합니다:

```php
use Pitmaster\Status\WorkingTreeStatus;

$status = new WorkingTreeStatus($repo->objectDatabase(), $repo->workDir(), $repo->gitDir());
$entries = $status->compute($repo->index(), $repo->resolve('HEAD'));
```

### 1단계: HEAD 트리 맵 작성

HEAD 커밋의 트리를 `path => blob hash` 맵으로 평탄화합니다. 이는 마지막으로 커밋된 상태를 나타냅니다.

### 2단계: HEAD와 인덱스 비교

HEAD 트리와 인덱스의 각 파일에 대해:
- HEAD에는 있지만 인덱스에는 없음: 인덱스 열에 `Deleted`
- 인덱스에는 있지만 HEAD에는 없음: 인덱스 열에 `Added`
- 양쪽에 있지만 해시가 다름: 인덱스 열에 `Modified`
- 양쪽에 있고 해시가 같음: 인덱스 열에 `Unmodified`

### 3단계: 인덱스와 작업 트리 비교

인덱스와 작업 트리의 각 파일에 대해:
- 인덱스에는 있지만 디스크에는 없음: 작업 트리 열에 `Deleted`
- 양쪽에 있지만 내용이 다름: 작업 트리 열에 `Modified`
- 양쪽에 있고 내용이 같음: 작업 트리 열에 `Unmodified`

### 4단계: 추적되지 않는 파일 감지

작업 트리 디렉터리를 탐색하고 `.git/`와 무시된 경로를 건너뛴 다음, 인덱스에 없는 파일을 양쪽 열에 `Untracked`로 보고합니다.

## .gitignore 지원

Pitmaster는 `.gitignore` 파일을 파싱해 추적되지 않는 파일 중 무시된 파일로 보고할 항목을 결정합니다.

```php
use Pitmaster\Status\GitIgnore;
```

`GitIgnore` 클래스는 다음 위치에서 gitignore 패턴을 파싱합니다:
- 저장소 루트의 `.gitignore`
- 하위 디렉터리의 `.gitignore`
- `.git/info/exclude`

지원되는 패턴 구문:
- Glob 패턴(`*.log`, `build/`)
- 부정(`!important.log`)
- 디렉터리 전용 패턴(끝에 `/` 사용)
- 저장소 루트 기준 패턴을 위한 선행 `/`
- 재귀 매칭을 위한 `**`

## 상태 항목 분류

UI 표시의 일반적인 패턴:

```php
$staged = [];
$unstaged = [];
$untracked = [];

foreach ($repo->status() as $entry) {
    if ($entry->index === FileStatus::Untracked) {
        $untracked[] = $entry;
    } elseif ($entry->index !== FileStatus::Unmodified) {
        $staged[] = $entry;
    }

    if ($entry->worktree !== FileStatus::Unmodified
        && $entry->index !== FileStatus::Untracked
    ) {
        $unstaged[] = $entry;
    }
}
```

## CLI 사용

```bash
./vendor/bin/pitmaster status
```

출력:

```
On branch main

Changes to be committed:
	new file:   src/Feature.php

Changes not staged for commit:
	modified:   src/Existing.php

Untracked files:
	notes.txt
```

## 파일 시스템 모니터

Pitmaster에는 git의 fsmonitor 프로토콜을 위한 헬퍼가 포함되어 있습니다:

```php
use Pitmaster\Status\Fsmonitor;
```

`Fsmonitor` 클래스는 `core.fsmonitor` 훅을 조회해 토큰 이후 변경된 파일을 확인할 수 있습니다(실패하면 수정 시간 스캔으로 대체). `['processes' => false]`로 연 핸들에서는 이 기능이 자동으로 꺼집니다. 현재 `status()`는 이를 호출하지 않고 stat 데이터와 콘텐츠를 직접 비교합니다.
