
# PHP API

## `Pitmaster\Pitmaster`

`Pitmaster\Pitmaster` 클래스는 정적 진입점입니다. 저장소를 열고, 만들고, 복제하는 팩토리 메서드와 감지 헬퍼를 제공합니다.

```php
use Pitmaster\Pitmaster;
```

### `open`

```php
public static function open(string $path, array $options = []): Repository
```

`$path`에 있는 기존 저장소를 엽니다. 경로는 작업 트리 루트(`.git/`를 포함하는 위치)여야 합니다. 연결된 worktree(`.git`가 `gitdir: <path>`를 포함하는 파일인 경우)와 베어 저장소(`$path`가 `.git` 디렉터리 자체인 경우)도 지원합니다.

`$options`는 기본값이 모두 `true`인 불리언 두 개를 받습니다:

- `hooks`: `.git/hooks` 스크립트를 실행합니다. 이 핸들에서 스크립트를 건너뛰려면 `false`를 전달하세요.
- `processes`: 프로세스를 시작하거나 네트워크 전송을 여는 작업을 허용합니다. 후크, fsmonitor 후크, clone, fetch, push 및 SSH 프로세스 전송을 비활성화하려면 `false`를 전달하세요. 로컬 읽기와 쓰기는 계속 작동합니다.

경로가 git 저장소가 아니면 `InvalidArgumentException`을 발생시킵니다.

```php
$repo = Pitmaster::open('/home/user/project');
$repo = Pitmaster::open('/home/user/project', ['hooks' => false]);
$repo = Pitmaster::open('/home/user/project', Pitmaster::processFreeOptions());
```

### `init`

```php
public static function init(string $path, string $objectFormat = 'sha1', array $options = []): Repository
```

`$path`에 새 저장소를 초기화합니다. `.git` 디렉터리 구조를 만들고 `objects/`, `refs/heads/`, `refs/tags/`, `hooks/`, `info/`, `HEAD`(`refs/heads/main`를 가리킴) 및 기본 `config`를 생성합니다.

`$objectFormat`는 `'sha1'` 또는 `'sha256'`입니다. SHA-256 저장소는 설정에 `repositoryformatversion = 1`와 `extensions.objectformat = sha256`를 포함합니다. 다른 값을 지정하면 `InvalidArgumentException`을 발생시킵니다. `$options`에는 `open()`와 같은 키를 사용합니다.

경로에 저장소가 이미 있으면 `RuntimeException`을 발생시킵니다.

```php
$repo = Pitmaster::init('/home/user/new-project');
$repo = Pitmaster::init('/home/user/sha256-project', 'sha256');
```

### `clone`

```php
public static function clone(string $url, string $path, ?int $depth = null, array $options = []): Repository
```

원격 저장소를 복제합니다. 새 저장소를 초기화하고, 원격 ref를 찾고, 팩 파일을 가져오고, 원격 추적 브랜치를 설정한 다음 작업 트리를 구성합니다. SSH URL(`ssh://...` 또는 `user@host:path`)에는 SSH 전송을 사용합니다. 다른 URL에는 스마트 HTTP를 사용하며, 스마트 교환에 실패하면 덤 HTTP로 대체합니다. 얕은 복제에는 `$depth`을 전달하세요.

`$options['processes']`가 `false`이면 `RuntimeException`을 발생시킵니다. 복제에 실패하고 이전에 `$path`가 없었다면, 부분적으로 생성된 디렉터리를 제거합니다.

```php
$repo = Pitmaster::clone('https://github.com/user/repo.git', '/tmp/repo');
$repo = Pitmaster::clone('https://github.com/user/repo.git', '/tmp/shallow', 1);
$repo = Pitmaster::clone('https://github.com/user/repo.git', '/tmp/repo', null, ['hooks' => false]);
```

### `processFreeOptions`

```php
public static function processFreeOptions(): array
```

호스트 프로세스를 시작해서는 안 되는 런타임을 위해 `['processes' => false, 'hooks' => false]`을 반환합니다. 이를 `open()` 또는 `init()`에 전달하세요.

### `isRepository`

```php
public static function isRepository(string $path): bool
```

경로가 git 저장소인지 확인합니다. 일반 저장소(`.git/` 디렉터리), 연결된 worktree(`.git` 파일), 베어 저장소(`HEAD` 파일이 있음)인 경우 `true`을 반환합니다.

```php
if (Pitmaster::isRepository('/path/to/check')) {
    // It's a git repo
}
```

### `isWorktree`

```php
public static function isWorktree(string $path): bool
```

경로가 연결된 worktree(주 저장소가 아님)인지 확인합니다. `.git`가 `gitdir:` 참조를 포함하는 파일일 때만 `true`을 반환합니다.

```php
if (Pitmaster::isWorktree('/path/to/worktree')) {
    // It's a linked worktree
}
```

### `commonGitDir`

```php
public static function commonGitDir(string $path): ?string
```

체크아웃 경로에서 공통 git 디렉터리를 확인합니다. 일반 저장소에서는 `.git` 디렉터리입니다. 연결된 worktree에서는 객체, 설정, packed-refs가 들어 있는 공유 git 디렉터리입니다. 경로가 저장소가 아니면 `null`을 반환합니다.

```php
$common = Pitmaster::commonGitDir('/path/to/worktree');
// '/path/to/main-repo/.git'
```

---

## `Pitmaster\Repository`

`Pitmaster\Repository` 클래스는 모든 git 작업의 주요 핸들입니다. `Pitmaster::open()`, `Pitmaster::init()` 또는 `Pitmaster::clone()`을 통해 가져옵니다.

```php
use Pitmaster\Repository;
```

### 경로 접근자

#### `gitDir`

```php
public function gitDir(): string
```

worktree별 git 디렉터리를 반환합니다. 일반 저장소에서는 `.git/`입니다. 연결된 worktree에서는 `.git/worktrees/<name>/`입니다.

#### `commonGitDir`

```php
public function commonGitDir(): string
```

공유 git 디렉터리를 반환합니다. 일반 저장소에서는 `gitDir()`와 같습니다. 연결된 worktree에서는 객체, 설정, packed-refs가 있는 주 저장소의 `.git/` 디렉터리입니다.

#### `workDir`

```php
public function workDir(): string
```

작업 트리의 루트 경로를 반환합니다.

#### `isLinkedWorktree`

```php
public function isLinkedWorktree(): bool
```

이 저장소 핸들이 연결된 worktree이면 `true`을 반환합니다.

#### `isBare`, `hooksEnabled`, `processesEnabled`

```php
public function isBare(): bool
public function hooksEnabled(): bool
public function processesEnabled(): bool
```

저장소에 작업 트리가 없는지, 그리고 이 핸들에서 `hooks` / `processes` 옵션 중 무엇이 적용되는지 알려줍니다. 프로세스가 비활성화된 경우 `hooksEnabled()`는 항상 `false`입니다.

---

### 객체

#### `readObject`

```php
public function readObject(string $hash): GitObject
```

전체 16진수 해시(40자 또는 SHA-256 저장소에서는 64자)로 객체를 읽습니다. 축약 해시는 허용되지 않습니다. `Blob`, `Tree`, `Commit` 또는 `Tag` 인스턴스를 반환합니다. 느슨한 객체를 먼저 검색한 뒤 팩 파일을 검색합니다.

해시를 찾을 수 없으면 `ObjectNotFoundException`을 발생시킵니다.

```php
$object = $repo->readObject('a1b2c3d4e5f6...');
```

#### `writeObject`

```php
public function writeObject(GitObject $object): ObjectId
```

느슨한 객체 저장소에 객체를 씁니다. 계산된 `ObjectId`을 반환합니다.

```php
use Pitmaster\Object\Blob;

$blob = Blob::fromContent('Hello, world!');
$id = $repo->writeObject($blob);
```

#### `catFile`

```php
public function catFile(string $hash): string
```

객체의 원시 콘텐츠를 반환합니다. `git cat-file -p`와 같습니다.

```php
echo $repo->catFile('a1b2c3d4...');
```

#### `objectExists`

```php
public function objectExists(string $hash): bool
```

저장소에 객체가 있는지(느슨한 객체 또는 팩 객체) 확인합니다.

#### `listObjects`

```php
public function listObjects(): array
```

저장소의 모든 객체 해시를 나열합니다. 16진수 문자열 배열을 반환합니다.

---

### 참조

#### `head`

```php
public function head(): Commit
```

현재 HEAD 커밋을 반환합니다. 기호 참조(HEAD -> refs/heads/main -> 커밋 해시)를 따라갑니다.

HEAD가 유효한 커밋을 가리키지 않으면 `RuntimeException`을 발생시킵니다(e.g., 빈 저장소).

#### `branch`

```php
public function branch(?string $name = null): ?string
```

인수가 없으면 현재 브랜치 이름(e.g., `'main'`)을 반환하거나 HEAD가 분리된 경우 `null`를 반환합니다.

브랜치 이름을 지정하면 해당 이름을 커밋 해시(16진수 문자열)로 확인하거나, 브랜치가 없으면 `null`을 반환합니다.

```php
$current = $repo->branch();        // 'main'
$hash = $repo->branch('feature');   // 'abc123...' or null
```

#### `branches`

```php
public function branches(): array
```

모든 브랜치 이름을 정렬된 배열로 반환합니다(`refs/heads/` 접두사 제외).

```php
$branches = $repo->branches();
// ['bugfix/typo', 'feature/login', 'main']
```

#### `tags`

```php
public function tags(): array
```

모든 태그 이름을 정렬된 배열로 반환합니다(`refs/tags/` 접두사 제외).

#### `resolve`

```php
public function resolve(string $revision): ObjectId
```

리비전 표현식을 `ObjectId`으로 확인합니다. 지원 항목:
- 전체 16진수 해시(40자 또는 SHA-256의 경우 64자). `abc1234` 같은 축약 해시는 확인되지 않습니다.
- `HEAD`
- 브랜치 이름(`main`, `feature/login`)
- 태그 이름(`v1.0.0`)
- 전체 ref 경로(`refs/heads/main`)
- 리비전 표현식(`HEAD~3`, `main^2`)

리비전을 확인할 수 없으면 `RuntimeException`을 발생시킵니다.

```php
$id = $repo->resolve('HEAD~3');
$id = $repo->resolve('v1.0.0');
$id = $repo->resolve('feature/login');
```

#### `allRefs`

```php
public function allRefs(): array
```

모든 ref를 ref 이름에서 16진수 해시로 연결하는 연관 배열로 반환합니다.

```php
$refs = $repo->allRefs();
// ['refs/heads/main' => 'abc123...', 'refs/tags/v1.0.0' => 'def456...']
```

#### `updateRef`

```php
public function updateRef(string $name, ObjectId $target): void
```

ref가 새 대상을 가리키도록 업데이트합니다.

```php
$repo->updateRef('refs/heads/main', $commitId);
```

#### `createBranch`

```php
public function createBranch(string $name, ?ObjectId $from = null): void
```

새 브랜치를 만듭니다. `$from`가 null이면 현재 HEAD에서 브랜치를 만듭니다.

```php
$repo->createBranch('feature/new-thing');
$repo->createBranch('hotfix', $repo->resolve('v1.0.0'));
```

#### `deleteBranch`

```php
public function deleteBranch(string $name): void
```

이름으로 브랜치를 삭제합니다. 브랜치가 체크아웃되어 있거나 존재하지 않으면 `RuntimeException`을 발생시킵니다. `git branch -d`와 달리 브랜치가 병합되었는지는 확인하지 않습니다.

```php
$repo->deleteBranch('feature/old');
```

#### `createLightweightTag`

```php
public function createLightweightTag(string $name, ?ObjectId $target = null): void
```

경량 태그를 만듭니다. `$target`가 null이면 현재 HEAD에 직접 태그를 지정합니다.

```php
$repo->createLightweightTag('v2.0.0');
$repo->createLightweightTag('hotfix-start', $repo->resolve('main~2'));
```

#### `createTag`

```php
public function createTag(string $name, string $message, ?ObjectId $target = null, ?string $tagger = null): ObjectId
```

주석 태그를 만듭니다. `$target`가 null이면 현재 HEAD에 태그를 지정합니다. 태그 객체의 `ObjectId`을 반환합니다.

```php
$tagId = $repo->createTag('v2.0.0', 'Release version 2.0.0');
```

#### `deleteTag`

```php
public function deleteTag(string $name): void
```

이름으로 태그를 삭제합니다.

```php
$repo->deleteTag('v1.0.0');
```

#### `packRefs`

```php
public function packRefs(): void
```

현재 느슨한 ref 집합으로 공유 `packed-refs` 파일을 다시 쓰고 해당 공유 느슨한 ref를 정리합니다. 연결된 worktree에서는 공통 git 디렉터리에 씁니다.

```php
$repo->packRefs();
```

#### `defaultBranch`

```php
public function defaultBranch(): string
```

저장소의 기본 브랜치를 확인합니다. 먼저 원격 HEAD symref를 확인하고, 다음으로 로컬 HEAD를 확인한 뒤, `main` 또는 `master`로 대체합니다.

#### `isBranchMerged`

```php
public function isBranchMerged(string $branch, ?string $target = null): bool
```

브랜치가 다른 브랜치(기본값은 기본 브랜치)에 완전히 병합되었는지 확인합니다.

```php
if ($repo->isBranchMerged('feature/done')) {
    $repo->deleteBranch('feature/done');
}
```

#### `checkout`

```php
public function checkout(string $target): void
```

브랜치를 전환하거나 HEAD를 분리합니다. HEAD, 인덱스, 작업 트리를 업데이트합니다. `$target`는 브랜치 이름, 태그, 전체 해시 또는 리비전 표현식일 수 있습니다. 브랜치가 아니면 HEAD가 분리됩니다.

현재 트리와 대상 트리 사이에서 경로가 달라지는 위치에 로컬 변경 사항(스테이징 여부 무관)이나 추적되지 않는 파일이 있으면 `RuntimeException`을 발생시킵니다.

다른 추적 파일의 커밋되지 않은 변경 사항은 유지되지 않습니다. 인덱스와 작업 트리는 대상 트리에서 다시 만들어지므로 양쪽에서 동일한 파일에 대한 수정도 사라집니다. git에서는 이런 수정이 유지됩니다([#2](https://github.com/inline0/pitmaster/issues/2)). 먼저 커밋하거나 stash에 저장하세요. `merge()`, `cherryPick()`, `revert()`, `rebase()` 및 bisect 메서드도 같은 방식으로 작업 트리를 다시 만듭니다.

```php
$repo->checkout('feature/login');
$repo->checkout($repo->resolve('v1.0.0')->hex);  // Detached HEAD
```

---

### 인덱스

#### `index`

```php
public function index(): Index
```

`.git/index`에서 현재 인덱스(스테이징 영역)를 읽어 반환합니다.

#### `add`

```php
public function add(string ...$paths): void
```

파일을 스테이징합니다. 각 파일을 작업 트리에서 읽어 blob 객체를 만들고 인덱스를 업데이트합니다. 각 인수는 작업 트리 루트 기준 상대 경로인 파일(또는 심볼릭 링크)이어야 합니다. 디렉터리와 glob은 확장되지 않으며, 파일이 없으면 `RuntimeException`을 발생시킵니다.

```php
$repo->add('src/Feature.php', 'tests/FeatureTest.php');
```

#### `remove`

```php
public function remove(string ...$paths): void
public function removeCached(string ...$paths): void
```

추적 중인 경로를 인덱스와 작업 트리에서 모두 제거합니다. Git 형태의 동작을 위해 인수 목록에서 `--cached` 및 `-r` / `--recursive`를 지원합니다.

```php
$repo->remove('old-file.txt');
$repo->remove('-r', 'src');
$repo->remove('--cached', 'old-file.txt');
$repo->removeCached('old-file.txt');
```

#### `mv`

```php
public function mv(string $source, string $destination): void
```

작업 트리와 인덱스에서 파일을 이동하거나 이름을 바꿉니다.

```php
$repo->mv('old-name.php', 'new-name.php');
```

---

### 커밋

#### `commit`

```php
public function commit(?string $message = null, ?string $author = null): ObjectId
```

현재 인덱스에서 커밋을 만듭니다. 인덱스 항목으로 트리 계층을 만들고, HEAD를 부모로 하는 커밋 객체를 생성한 다음 HEAD를 업데이트합니다. 새 커밋의 `ObjectId`을 반환합니다.

병합, cherry-pick, revert 또는 rebase 중 충돌로 작업이 중단된 경우에만 `$message`은 `null`일 수 있으며, 이때 준비된 메시지가 사용됩니다. 그 외에 `null` 메시지가 있으면 `RuntimeException('Commit message required')`을 발생시킵니다. 인덱스에 병합되지 않은 경로가 있거나, 커밋이 없는 저장소에서 인덱스가 비어 있거나, 트리가 HEAD와 변경되지 않은 경우에도 커밋은 예외를 발생시킵니다.

`$author` 매개변수는 전체 git 작성자 문자열(`Name <email> timestamp timezone`)입니다. null이면 환경 변수, 상수, `.git/config`에서 값을 확인하며 순서는 [시작하기](https://pitmaster.inline0.dev/ko/docs/getting-started)에 설명되어 있습니다.

```php
$id = $repo->commit('Fix null pointer bug');
```

#### `show`

```php
public function show(string $revision): array
```

커밋과 첫 번째 부모의 차이를 표시합니다. 리비전이 주석 태그로 확인되면 반환값에 `['tag' => Tag]`도 포함되며, `commit`은 태그를 해제한 대상 커밋입니다.

```php
$result = $repo->show('HEAD');
echo $result['commit']->message;

foreach ($result['diff'] as $diff) {
    echo $diff->format();
}
```

#### `reset`

```php
public function reset(string $revision, string $mode = 'mixed'): void
```

HEAD를 커밋으로 재설정합니다. 모드:
- `'soft'`: HEAD만 이동
- `'mixed'`: HEAD 이동 + 인덱스 재설정(기본값)
- `'hard'`: HEAD 이동 + 인덱스 재설정 + 작업 트리 재설정

```php
$repo->reset('HEAD~1', 'soft');
$repo->reset('main', 'hard');
```

#### `restore`

```php
public function restore(string $path, ?string $source = null, bool $staged = false, bool $worktree = false): void
```

인덱스나 지정한 소스 리비전에서 경로를 복원합니다. 플래그를 하나도 지정하지 않으면 Pitmaster은 인덱스(지정한 경우 `$source`)에서 작업 트리를 복원합니다. `staged: true`는 HEAD(또는 `$source`)에서 인덱스를 복원하고 작업 트리는 그대로 둡니다. 둘 다 복원하려면 `staged: true, worktree: true`를 전달하세요.

```php
$repo->restore('src/File.php');                                        // Worktree from index
$repo->restore('src/File.php', staged: true);                          // Index from HEAD
$repo->restore('src/File.php', 'HEAD~1');                              // Worktree from another commit
$repo->restore('src/File.php', 'HEAD~1', staged: true, worktree: true); // Index + worktree from another commit
```

#### `cherryPick`

```php
public function cherryPick(string $revision): ObjectId
public function cherryPickContinue(): ObjectId
public function cherryPickAbort(): void
```

원래 작성자를 유지하면서 현재 브랜치에 새 커밋으로 적용합니다. 충돌로 작업이 중단되면 충돌을 해결하고 경로를 스테이징한 다음 `cherryPickContinue()` 또는 `cherryPickAbort()`을 호출하세요.

```php
$newId = $repo->cherryPick('feature~2');

// After resolving conflicts:
$repo->add('src/File.php');
$repo->cherryPickContinue();
```

#### `revert`

```php
public function revert(string $revision): ObjectId
public function revertContinue(): ObjectId
public function revertAbort(): void
```

다른 커밋을 되돌리는 커밋을 만듭니다. revert 중 충돌로 작업이 중단되면 충돌을 해결하고 경로를 스테이징한 다음 `revertContinue()` 또는 `revertAbort()`을 호출하세요.

```php
$revertId = $repo->revert('HEAD~1');

// After resolving conflicts:
$repo->add('src/File.php');
$repo->revertContinue();
```

#### `rebase`

```php
public function rebase(string $onto): array
public function rebaseContinue(): array
public function rebaseAbort(): void
public function rebaseSkip(): array
```

현재 브랜치를 다른 리비전 위로 rebase합니다. Pitmaster는 현재 Git 형태로 충돌 중단 상태를 처리하는 선형 rebase와 계속하기, 중단하기, 건너뛰기를 지원합니다. HEAD는 브랜치를 가리켜야 합니다.

`rebase()`, `rebaseContinue()`, `rebaseSkip()`는 `['success' => bool, 'commits' => int, 'conflicts' => list<string>]`을 반환합니다. 여기에는 rebase 완료 여부, 적용한 커밋 수, 중단 시 충돌한 경로가 포함됩니다.

```php
$result = $repo->rebase('main');

if (!$result['success']) {
    // Resolve conflicts, then continue or skip/abort.
    $repo->add('src/File.php');
    $repo->rebaseContinue();
}
```

---

### 상태

#### `status`

```php
public function status(): array
```

작업 트리 상태를 계산합니다. 각각 다음을 포함하는 `StatusEntry` 객체 배열을 반환합니다:
- `path`: 파일 경로
- `index`: HEAD에서 인덱스까지의 변경을 나타내는 `FileStatus` 열거형
- `worktree`: 인덱스에서 작업 트리까지의 변경을 나타내는 `FileStatus` 열거형
- `origPath`, `renameScore`: 이름이 바뀐 항목에 설정되며, 그 외에는 `null`

추적되지 않는 파일은 두 열 모두에 `FileStatus::Untracked`이 표시됩니다. 무시된 파일은 보고되지 않습니다.

```php
foreach ($repo->status() as $entry) {
    echo $entry->shortFormat() . "\n"; // "M  src/File.php", "?? notes.txt"
}
```

#### `statusPorcelainV2`

```php
public function statusPorcelainV2(): string
```

git의 porcelain v2 형식으로 읽을 수 있는 상태 정보를 출력합니다.

```php
echo $repo->statusPorcelainV2();
// 1 M. N... 000000 000000 000000 0000...0000 0000...0000 modified-file.txt
// ? untracked-file.txt
```

---

### 변경 사항

#### `diff`

```php
public function diff(?string $pathspec = null, string $algorithm = 'myers'): array
```

작업 트리와 인덱스(스테이징되지 않은 변경 사항)를 비교합니다. `DiffResult` 객체 배열을 반환합니다. `$pathspec`은 정확히 일치하는 파일 경로 하나로 필터링합니다. `$algorithm`는 `'myers'`(기본값), `'patience'`, `'histogram'` 또는 `'minimal'`입니다. 다른 값을 지정하면 `InvalidArgumentException`을 발생시킵니다.

```php
$diffs = $repo->diff();

foreach ($diffs as $diff) {
    echo $diff->format();  // Unified diff output
}
```

#### `diffStaged`

```php
public function diffStaged(?string $pathspec = null, string $algorithm = 'myers'): array
```

인덱스와 HEAD(스테이징된 변경 사항)를 비교합니다. 이름 변경 감지를 적용해 삭제된 파일과 추가된 파일을 대조한 `DiffResult[]`을 반환합니다.

현재 `$pathspec` 인수는 무시되므로 스테이징된 파일이 모두 반환됩니다. 첫 커밋 전에는 메서드가 빈 배열을 반환합니다([#5](https://github.com/inline0/pitmaster/issues/5)).

```php
$staged = $repo->diffStaged();
```

#### `diffTree`

```php
public function diffTree(ObjectId $a, ObjectId $b, string $algorithm = 'myers'): array
```

두 트리를 `ObjectId`로 비교합니다. `DiffResult[]`을 반환합니다. 커밋 ID가 아닌 트리 ID를 전달하세요. 커밋 ID는 트리로 읽히지 않아 빈 결과를 냅니다.

```php
$old = $repo->readObject($repo->resolve('HEAD~1')->hex);
$new = $repo->head();
$diffs = $repo->diffTree($old->tree, $new->tree);
```

---

### 로그

#### `log`

```php
public function log(int $limit = 50, ?ObjectId $from = null): array
```

커미터 타임스탬프를 기준으로 최신 항목부터 커밋 기록을 순회합니다. `Commit[]`을 반환합니다. `$from`이 null이면 HEAD에서 시작합니다. HEAD에 아직 커밋이 없으면 빈 배열을 반환합니다.

```php
$commits = $repo->log(10);
```

#### `logAll`

```php
public function logAll(int $limit = 50): array
```

모든 ref와 HEAD에서 도달할 수 있는 커밋을 최신순으로 순회합니다. `Commit[]`을 반환합니다.

#### `logOneline`

```php
public function logOneline(int $limit = 50, bool $all = false, ?string $path = null): array
```

HEAD에서, 모든 ref에서(`$all`) 또는 경로로 필터링하여 `git log --oneline` 형식(`abc1234 Subject`)의 로그 줄을 반환합니다.

#### `logPath`

```php
public function logPath(string $path, int $limit = 50): array
```

파일 경로로 로그를 필터링합니다. 첫 번째 부모와의 diff에서 해당 파일을 추가, 수정, 삭제 또는 이름 변경한 커밋만 반환합니다. 디렉터리 경로는 일치하지 않습니다.

가장 최근 `5 * $limit`개 커밋만 검사하므로, 파일을 변경한 더 오래된 커밋은 결과에서 누락될 수 있습니다([#7](https://github.com/inline0/pitmaster/issues/7)).

```php
$commits = $repo->logPath('src/Repository.php', 20);
```

---

### 병합

#### `merge`

```php
public function merge(string $branch, string $strategy = 'recursive'): MergeResult
public function mergeOctopus(array $branches): MergeResult
public function mergeContinue(): ObjectId
public function mergeAbort(): void
```

브랜치(또는 임의의 리비전)를 HEAD에 병합합니다. fast-forward 병합을 자동으로 감지합니다. fast-forward가 아닌 병합은 트리 수준에서 충돌을 감지하고 충돌이 없으면 병합 커밋을 만듭니다. 충돌로 중단되면 Git 형태의 병합 상태를 인덱스와 작업 트리에 기록합니다. 경로를 해결해 스테이징한 다음 `mergeContinue()` 또는 `mergeAbort()`를 호출하세요.

`$strategy` `'ours'`은 HEAD의 트리를 유지하는 병합 커밋을 기록합니다. 그 외 값은 기본 재귀 병합을 사용합니다. `mergeOctopus()`는 둘 이상의 브랜치를 하나의 커밋으로 병합합니다. 브랜치 중 하나라도 충돌하면 병합 상태를 기록하거나 커밋을 만들지 않고, 병합되지 않은 결과를 반환합니다.

HEAD에 이미 포함된 브랜치를 병합하면 "Already up to date"라고 표시하는 대신 병합 커밋이 하나 더 생성됩니다([#6](https://github.com/inline0/pitmaster/issues/6)). 추적 중인 파일의 커밋되지 않은 변경 사항은 `checkout()`에 설명된 대로 덮어씁니다.

다음 항목이 포함된 `MergeResult`을 반환합니다.
- `clean`: 충돌 없이 병합에 성공했는지 여부
- `commitId`: 병합 커밋 ID(충돌이 없을 때)
- `conflictPaths`: 충돌하는 파일 경로 배열(충돌이 있을 때)
- `mergedContents`: 충돌 표시가 포함된 파일 콘텐츠(충돌이 있을 때)

```php
$result = $repo->merge('feature/login');

if ($result->clean) {
    echo "Merged: {$result->commitId->hex}\n";
} else {
    echo "Conflicts in: " . implode(', ', $result->conflictPaths) . "\n";
    $repo->add('src/File.php');
    $repo->mergeContinue();
}
```

#### `mergeBase`

```php
public function mergeBase(ObjectId $a, ObjectId $b): ?ObjectId
```

두 커밋의 병합 기준(최저 공통 조상)을 찾습니다. 공통 조상이 없으면 null을 반환합니다.

```php
$base = $repo->mergeBase(
    $repo->resolve('main'),
    $repo->resolve('feature/login'),
);
```

### 이분 탐색

```php
public function bisectStart(string $bad, string $good): ObjectId
public function bisectGood(?string $revision = null): ?ObjectId
public function bisectBad(?string $revision = null): ?ObjectId
public function bisectReset(): void
```

변경을 도입한 커밋을 이분 탐색합니다. `bisectStart()`은 문제가 있는 리비전과 정상 리비전을 기록하고, 첫 번째 후보를 체크아웃한 뒤 반환합니다. `bisectGood()`과 `bisectBad()`는 현재 HEAD(또는 `$revision`)를 표시하고 다음 후보를 체크아웃하여 반환합니다. 의심되는 커밋이 하나만 남으면 해당 커밋을 반환하고 후보로 유지합니다. 이 커밋이 최초의 문제가 있는 커밋입니다. `bisectReset()`는 세션을 종료하고 시작했던 브랜치나 커밋으로 돌아갑니다.

```php
$candidate = $repo->bisectStart('HEAD', 'v1.0.0');
// test $candidate ...
$candidate = $repo->bisectBad();
$candidate = $repo->bisectGood();
$repo->bisectReset();
```

---

### 네트워크

#### `fetch`

```php
public function fetch(string $remote = 'origin', ?int $depth = null): void
```

원격 저장소에서 가져옵니다. 새 객체를 다운로드하고 원격 추적 참조를 갱신합니다. 얕은 이력을 더 깊게 가져오거나 제한하려면 `$depth`을 전달하세요.

`fetch()` 메서드와 `push*()` 메서드는 `['processes' => false]`로 열린 핸들에서 `RuntimeException`를 발생시킵니다.

```php
$repo->fetch();
$repo->fetch('upstream');
```

#### `push`

```php
public function push(string $remote = 'origin', ?string $branch = null): void
public function pushForceWithLease(string $remote = 'origin', ?string $branch = null, ?ObjectId $expected = null): void
public function pushAtomic(string $remote = 'origin', array $branches = []): void
public function pushMirror(string $remote = 'origin'): void
```

현재 브랜치(또는 지정한 브랜치)를 원격 저장소에 푸시합니다. 더 엄격하거나 폭넓은 참조 업데이트를 위해 force-with-lease, atomic 및 mirror 변형을 사용할 수 있습니다.

```php
$repo->push();
$repo->push('origin', 'feature/login');
$repo->pushForceWithLease('origin', 'main', $repo->resolve('refs/remotes/origin/main'));
$repo->pushAtomic('origin', ['main', 'release']);
$repo->pushMirror();
```

---

### 워크트리

#### `addWorktree`

```php
public function addWorktree(string $path, string $branch, ?ObjectId $from = null, ?string $name = null): Worktree
```

연결된 워크트리를 추가합니다. 브랜치가 없으면 생성하고, 워크트리 메타데이터를 설정한 뒤 작업 트리 파일을 생성합니다. 체크아웃 디렉터리의 기본 이름과 다른 결정적 메타데이터 slug가 필요하면 `$name`을 전달하세요.

```php
$wt = $repo->addWorktree('/tmp/review', 'feature/review');
$wt = $repo->addWorktree('/tmp/checkouts/review', 'feature/review', name: 'review-wt');
```

#### `removeWorktree`

```php
public function removeWorktree(string $pathOrName, bool $force = false): void
```

연결된 워크트리를 제거합니다. 잠겨 있어도 제거하려면 `$force = true`을 사용하세요.

#### `worktrees`

```php
public function worktrees(): array
```

모든 워크트리(기본 및 연결된 워크트리)를 나열합니다. `Worktree[]`을 반환합니다.

```php
foreach ($repo->worktrees() as $wt) {
    echo "{$wt->path} [{$wt->branch}]\n";
}
```

---

### 구성

#### `config`

```php
public function config(): GitConfig
```

저장소의 git 구성에 접근합니다. 구성 값을 읽고 씁니다.

```php
$config = $repo->config();
$name = $config->get('user.name');
$config->set('user.email', 'new@example.com');
```

---

### 내부 접근

이 메서드는 고급 사용 사례를 위해 내부 구성 요소를 노출합니다.

#### `objectDatabase`

```php
public function objectDatabase(): ObjectDatabase
```

원시 객체 데이터베이스(느슨한 객체 및 팩 저장소)에 접근합니다.

#### `refDatabase`

```php
public function refDatabase(): RefDatabase
```

원시 참조 데이터베이스(느슨한 참조 및 패킹된 참조)에 접근합니다.

---

## 하위 수준 클래스

이 클래스는 `Repository`로 래핑되지 않으며 내부 구성 요소로부터 생성됩니다.

### `Pitmaster\Graph\Blame`

```php
public function blame(ObjectId $headId, string $path): array
```

`$headId`의 `$path` 각 줄에 대해 항목 하나를 반환합니다: `['line' => int, 'hash' => string, 'author' => string, 'content' => string]`. `author`은 전체 식별 문자열입니다(`Name <email> timestamp timezone`).

```php
use Pitmaster\Graph\Blame;

$blame = new Blame($repo->objectDatabase());

foreach ($blame->blame($repo->resolve('HEAD'), 'src/File.php') as $line) {
    echo substr($line['hash'], 0, 8) . " {$line['content']}\n";
}
```

### `Pitmaster\Graph\Grep`

```php
public function grep(ObjectId $treeId, string $pattern, string $prefix = '', array $options = []): array
```

트리의 blob을 검색합니다. 일치하는 줄마다 `['path' => string, 'line' => int, 'content' => string]`을 반환합니다. 옵션: `regex`(패턴을 PCRE 패턴으로 처리) 및 `ignore_case`. 정규식이 잘못되면 `InvalidArgumentException`을 발생시킵니다.

```php
use Pitmaster\Graph\Grep;

$grep = new Grep($repo->objectDatabase());
$matches = $grep->grep($repo->head()->tree, 'TODO', '', ['ignore_case' => true]);
```

### `Pitmaster\Ref\Notes`

```php
public function get(ObjectId $commitId, string $notesRef = 'refs/notes/commits'): ?string
public function set(ObjectId $commitId, string $noteContent, string $notesRef = 'refs/notes/commits'): void
public function remove(ObjectId $commitId, string $notesRef = 'refs/notes/commits'): void
public function listAll(string $notesRef = 'refs/notes/commits'): array
public function merge(string $sourceRef, string $targetRef = 'refs/notes/commits'): void
```

notes ref 아래에 저장된 git 노트를 읽고 씁니다. `listAll()`은 커밋 해시를 노트 콘텐츠에 매핑해 반환합니다.

```php
use Pitmaster\Ref\Notes;

$notes = new Notes($repo->objectDatabase(), $repo->refDatabase());
$notes->set($repo->resolve('HEAD'), "Reviewed\n");
echo $notes->get($repo->resolve('HEAD'));
```
