
# 저장소 읽기

Pitmaster은 디스크 형식을 직접 파싱해 git 저장소를 읽습니다. 압축된 loose 객체(`.git/objects/`의 zlib 압축 파일), pack 파일(`.git/objects/pack/`), ref(`.git/refs/` 및 `packed-refs`의 파일), 인덱스(`.git/index`)를 처리합니다.

## 저장소 열기

```php
use Pitmaster\Pitmaster;

$repo = Pitmaster::open('/path/to/project');
```

Pitmaster은 세 가지 저장소 레이아웃을 지원합니다:

1. **일반 저장소**: `.git`은 프로젝트 루트의 디렉터리입니다.
2. **연결된 작업 트리**: `.git`은 `gitdir: <path>`가 포함되어 `.git/worktrees/<name>/`를 가리키는 파일입니다.
3. **Bare 저장소**: 경로 자체에 `HEAD`, `objects/` 등이 포함됩니다.

```php
// Detection helpers
Pitmaster::isRepository('/path');  // true for any of the above
Pitmaster::isWorktree('/path');    // true only for linked worktrees
```

## 객체 읽기

git 저장소의 모든 데이터는 해시로 식별되는 객체로 저장됩니다. 기본적으로 SHA-1(16진수 40자)을 사용하며, `Pitmaster::init($path, 'sha256')`로 만든 저장소에서는 SHA-256(16진수 64자)을 사용합니다. 객체 유형은 blob, tree, commit, tag 네 가지입니다.

아래 해시 기반 메서드에는 전체 16진수 해시를 전달해야 합니다. 축약 해시는 허용되지 않습니다.

### readObject

```php
use Pitmaster\Object\Blob;
use Pitmaster\Object\Tree;
use Pitmaster\Object\Commit;
use Pitmaster\Object\Tag;

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

match (true) {
    $object instanceof Blob   => handleBlob($object),
    $object instanceof Tree   => handleTree($object),
    $object instanceof Commit => handleCommit($object),
    $object instanceof Tag    => handleTag($object),
};
```

객체는 먼저 loose 저장소에서 검색하고, 그다음 pack 파일에서 검색합니다. pack 파일 조회에는 O(log n) 바이너리 검색을 위해 `.idx` 팬아웃 테이블을 사용합니다.

### catFile

유형 확인 없이 간단히 콘텐츠를 가져오려면 다음을 사용하세요:

```php
$content = $repo->catFile('a1b2c3d4e5f6...');
echo $content;
```

### objectExists

```php
if ($repo->objectExists('a1b2c3d4e5f6...')) {
    // Object is in the repository
}
```

### listObjects

```php
$hashes = $repo->listObjects();
// ['a1b2c3d4...', 'f6e5d4c3...', ...]
```

모든 객체(loose 및 packed)의 16진수 해시를 반환합니다.

## blob 사용

blob은 원시 파일 콘텐츠를 담습니다.

```php
$blob = $repo->readObject($hash);
echo $blob->content;     // Raw file bytes
echo $blob->size();      // Content length
echo $blob->id->hex;     // Object hash
```

## tree 사용

트리는 디렉터리 목록입니다. 각 항목에는 모드, 이름, 해시가 있습니다.

```php
$tree = $repo->readObject($hash);

foreach ($tree->entries as $entry) {
    echo "{$entry->mode} {$entry->name} {$entry->hash->hex}\n";
    // 100644 README.md abc123...
    // 40000 src def456...
    // 100755 deploy.sh 789012...

    if ($entry->isTree()) {
        // Recurse into subdirectory (mode 40000)
    } elseif ($entry->isExecutable()) {
        // Executable file (mode 100755)
    } elseif ($entry->isBlob()) {
        // Regular file (any 10xxxx mode, so executables match too)
    } elseif ($entry->isSymlink()) {
        // Symbolic link (mode 120000)
    } elseif ($entry->isGitlink()) {
        // Submodule (mode 160000)
    }
}
```

## 커밋 작업

커밋은 트리 스냅샷을 부모 커밋 및 메타데이터와 연결합니다.

```php
$commit = $repo->head();

echo $commit->id->hex;      // Full object hash
echo $commit->tree->hex;    // Tree object hash
echo $commit->author;       // "Name <email> timestamp tz"
echo $commit->committer;    // "Name <email> timestamp tz"
echo $commit->message;      // Full commit message

// Parent commits
foreach ($commit->parents as $parentId) {
    $parent = $repo->readObject($parentId->hex);
}

// Merge detection
if ($commit->isMerge()) {
    // Two or more parents
}
```

## 태그 작업

주석 태그는 대상, 유형, 이름, 태거, 메시지가 포함된 객체입니다.

```php
$tag = $repo->readObject($hash);

echo $tag->object->hex;           // Target object hash
echo $tag->objectType->value;     // 'commit', 'tree', 'blob', or 'tag'
echo $tag->name;            // Tag name
echo $tag->tagger;          // "Name <email> timestamp tz"
echo $tag->message;         // Tag message
```

## 참조 읽기

### 현재 HEAD

```php
$commit = $repo->head();
```

HEAD(`HEAD -> refs/heads/main -> commit hash`와 같은 심볼릭 참조를 따라)를 해석하고 커밋 객체를 반환합니다.

### 현재 브랜치

```php
$branch = $repo->branch();  // 'main' or null if detached
```

### 브랜치 및 태그 목록

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

### 모든 참조

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

### 리비전 해석

```php
$id = $repo->resolve('HEAD');
$id = $repo->resolve('main');
$id = $repo->resolve('v1.0.0');
$id = $repo->resolve('HEAD~3');
$id = $repo->resolve('main^2');
$id = $repo->resolve('3f2c1e0b9d6a4f8e7c5b2a1d0e9f8c7b6a5d4e3f');
```

리비전 파서는 `~N`(조상) 및 `^N`(부모) 연산자를 임의로 연결해 사용할 수 있도록 지원합니다. 해시는 전체 값을 지정해야 합니다.

## 커밋 기록 탐색

### 로그

커밋은 커미터 타임스탬프를 기준으로 최신순으로 반환됩니다.

```php
// Last 20 commits from HEAD
$commits = $repo->log(20);

foreach ($commits as $commit) {
    $short = substr($commit->id->hex, 0, 7);
    echo "{$short} {$commit->message}\n";
}
```

### 특정 커밋부터 log 보기

```php
$from = $repo->resolve('feature/login');
$commits = $repo->log(10, $from);
```

### 파일 경로로 log 필터링

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

첫 번째 부모와 비교해 지정된 파일이 변경(추가, 수정, 삭제 또는 이름 변경)된 커밋만 반환합니다. 경로는 정확한 파일 경로여야 하며 디렉터리는 일치하지 않습니다. 최신 `5 * $limit`개 커밋만 살펴보므로 더 오래된 일치 항목은 누락될 수 있습니다([#7](https://github.com/inline0/pitmaster/issues/7)).

### 표시

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

echo "Author: {$commit->author}\n";
echo "Message: {$commit->message}\n\n";

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

## 기본 브랜치 감지

```php
$default = $repo->defaultBranch();
// Checks: remote HEAD symref -> local HEAD -> main/master fallback
```

`remote.origin.url`가 설정되어 있고 프로세스가 활성화된 경우, 첫 번째 확인에서 원격에 스마트 HTTP 요청을 보냅니다. 네트워크 오류는 무시하고 로컬 확인을 이어갑니다.

## 병합 조상 관계

```php
// Is feature fully merged into main?
$merged = $repo->isBranchMerged('feature/done', 'main');

// Find the merge base of two commits
$base = $repo->mergeBase(
    $repo->resolve('main'),
    $repo->resolve('feature/login'),
);
```
