
# 워크트리

Git 워크트리를 사용하면 여러 작업 디렉터리가 하나의 저장소를 공유할 수 있습니다. 각 워크트리에는 자체 체크아웃(HEAD, 인덱스, 작업 파일)이 있지만 객체 저장소, 설정 및 packed-refs는 기본 저장소와 공유합니다. Pitmaster는 연결된 워크트리를 만들고, 나열하고, 잠그고, 제거할 수 있으며 이를 저장소로 열 수도 있습니다. 워크트리 정리, 이동 및 복구는 구현되지 않았습니다.

## 개념

일반 git 저장소에는 작업 트리가 하나 있습니다:

```
project/
  .git/          (directory)
  src/
  README.md
```

worktree를 사용하면 추가 체크아웃을 만들 수 있습니다:

```
project/                          (main worktree)
  .git/                           (the actual git directory)
    worktrees/
      review/                     (metadata for linked worktree)
        HEAD
        commondir
        gitdir

/tmp/review/                      (linked worktree)
  .git                            (file, not directory)
  src/
  README.md
```

연결된 worktree의 `.git`는 worktree 기준 상대 경로를 담은 파일입니다(디렉터리가 아님):
```
gitdir: ../../path/to/project/.git/worktrees/review
```

## worktree 만들기

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

// Create a linked worktree on a new branch
$wt = $repo->addWorktree('/tmp/review', 'feature/review');

echo $wt->path;        // '/tmp/review'
echo $wt->branch;      // 'feature/review'
echo $wt->gitDir;      // '/path/to/project/.git/worktrees/review'
var_dump($wt->isMain);      // bool(false)
var_dump($wt->isDetached);  // bool(false)
```

`addWorktree()`는 다음 작업을 수행합니다:
1. 브랜치가 없으면 HEAD 또는 지정된 `$from` 커밋에서 브랜치를 만듭니다
2. `.git/worktrees/<name>/`에 메타데이터 디렉터리를 만듭니다
3. worktree 디렉터리에 `.git` 파일을 씁니다
4. `HEAD`, `commondir`, `gitdir` 메타데이터 파일을 씁니다
5. 브랜치의 트리를 worktree 디렉터리에 체크아웃합니다
6. worktree의 인덱스를 씁니다

브랜치가 이미 있으면 `$from`는 무시됩니다. git과 달리 `addWorktree()`는 다른 worktree에서 이미 체크아웃된 브랜치를 거부하지 않으며 `post-checkout` 후크도 실행하지 않습니다.

### 명시적 메타데이터 이름 사용

연결된 worktree 여러 개가 같은 체크아웃 기본 이름을 공유할 수 있는 경우 선택적 `name` 인수를 사용하세요.

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

해당 메타데이터 이름은 `basename($path)` 대신 `.git/worktrees/<name>/` 아래에서 사용됩니다.

### 특정 커밋에서 만들기

```php
$wt = $repo->addWorktree('/tmp/hotfix', 'hotfix/urgent', $repo->resolve('v1.0.0'));
```

## worktree 목록 보기

```php
$worktrees = $repo->worktrees();

foreach ($worktrees as $wt) {
    $type = $wt->isMain ? 'main' : 'linked';
    $branch = $wt->branch ?? 'detached';
    $locked = $wt->isLocked ? ' [locked]' : '';

    echo "{$wt->path} ({$type}) [{$branch}]{$locked}\n";
}
```

목록에는 항상 주 worktree가 먼저 나오고, 그 뒤에 연결된 worktree가 나옵니다. 주 저장소의 핸들에서 `worktrees()`를 호출하세요. 연결된 worktree 핸들에서는 주 항목에 현재 연결된 worktree 자체의 경로가 표시됩니다([#14](https://github.com/inline0/pitmaster/issues/14)).

## worktree 제거

```php
$repo->removeWorktree('review');          // by metadata name
$repo->removeWorktree('/tmp/review');     // or by worktree path

// Force remove (even if locked)
$repo->removeWorktree('review', force: true);
```

worktree를 제거하면 다음 작업을 수행합니다:
1. worktree가 잠겨 있는지 확인합니다(`$force`가 true가 아니면 실패)
2. worktree 디렉터리에서 `.git` 파일을 제거합니다
3. `.git/worktrees/`에서 메타데이터 디렉터리를 제거합니다

작업 트리 파일은 자동으로 삭제되지 않습니다. 브랜치는 계속 존재합니다.

## 연결된 worktree 열기

연결된 worktree는 일반 저장소처럼 열 수 있습니다:

```php
$repo = Pitmaster::open('/tmp/review');

// Pitmaster detects the .git file and follows the gitdir reference
echo $repo->isLinkedWorktree();  // true
echo $repo->gitDir();            // '/path/to/project/.git/worktrees/review'
echo $repo->commonGitDir();      // '/path/to/project/.git'
echo $repo->workDir();           // '/tmp/review'
```

저장소 작업은 다음과 같이 경로를 확인합니다:
- **객체**: 공통 git 디렉터리(`project/.git/objects/`)에서 읽고 여기에 씁니다
- **설정**: 공통 git 디렉터리(`project/.git/config`)에서 읽습니다
- **Packed-refs**: 공통 git 디렉터리에서 읽습니다
- **HEAD**: worktree별로 관리됩니다(각 worktree가 자체 브랜치를 추적). `ORIG_HEAD`, `MERGE_HEAD` 및 기타 작업 ref도 함께 관리됩니다
- **인덱스**: worktree별로 관리됩니다(각 worktree에 자체 스테이징 영역이 있음)
- **Loose refs**: 브랜치와 태그는 공통 디렉터리에서 공유됩니다(`project/.git/refs/`)
- **후크**: worktree별 git 디렉터리에서 읽으므로 공유 후크는 실행되지 않습니다([#12](https://github.com/inline0/pitmaster/issues/12))

Stash는 아직 연결된 worktree를 처리하지 않습니다. worktree별 디렉터리에서 ID를 읽는데 그곳에는 설정이 없고, reflog도 그 디렉터리에 씁니다([#14](https://github.com/inline0/pitmaster/issues/14)).

## Worktree 값 객체

```php
use Pitmaster\Worktree\Worktree;

$wt->name;          // Metadata name (null for the main worktree)
$wt->path;          // Working directory path
$wt->gitDir;        // Per-worktree git dir
$wt->branch;        // Branch name (null if detached)
$wt->head;          // ObjectId of HEAD commit
$wt->isMain;        // true for the main worktree
$wt->isDetached;    // true if HEAD is detached
$wt->isLocked;      // true if worktree is locked
$wt->lockReason;    // Lock reason, or null when there is none (an empty reason is also null)
```

## WorktreeManager

저수준 worktree 관리용입니다. `WorktreeManager::add()`는 메타데이터와 `.git` 파일만 씁니다. 브랜치를 만들거나 파일을 체크아웃하거나 인덱스를 쓰지는 않습니다. 사용할 수 있는 체크아웃을 만들려면 `$repo->addWorktree()`를 사용하세요. 브랜치가 이미 존재해야 합니다(또는 HEAD를 분리하는 `refs/tags/v1.0.0` 같은 전체 ref 이름이어야 합니다).

```php
use Pitmaster\Worktree\WorktreeManager;

$manager = new WorktreeManager($repo->commonGitDir(), $mainWorkDir);

// List
$worktrees = $manager->list();

// Add
$wt = $manager->add('/tmp/review', 'feature/review');
$wt = $manager->add('/tmp/review', 'feature/review', 'review-app');

// Remove
$manager->remove('review');
$manager->remove('review', force: true);

// Lock/unlock (metadata name only)
$manager->lock('review', 'Do not delete, code review in progress');
$manager->unlock('review');
```

## worktree 잠그기

```php
// Lock (prevents removal)
$manager->lock('review', 'Code review in progress');

// Attempt to remove locked worktree
try {
    $manager->remove('review');
} catch (\RuntimeException $e) {
    // "Worktree is locked: review (Code review in progress)"
}

// Force remove ignores the lock
$manager->remove('review', force: true);

// Unlock
$manager->unlock('review');
```

잠금은 `.git/worktrees/<name>/locked`의 파일로 저장됩니다. 파일 내용은 잠금 사유이며 비어 있을 수도 있습니다.

## worktree 감지

```php
// Check if a path is a linked worktree
if (Pitmaster::isWorktree('/tmp/review')) {
    echo "This is a linked worktree\n";
}

// Resolve the common git dir from any path
$common = Pitmaster::commonGitDir('/tmp/review');
// '/path/to/project/.git'

// Resolve a .git file to the actual git directory
$gitDir = WorktreeManager::resolveGitFile('/tmp/review');
// '/path/to/project/.git/worktrees/review'
```

## 파일 배치

### 연결된 worktree의 주 worktree 메타데이터

```
.git/worktrees/<name>/
  HEAD         Per-worktree HEAD (symbolic ref or detached hash)
  commondir    Relative path to the shared git dir ("../..")
  gitdir       Path back to the worktree's .git file, as passed to addWorktree()
  locked       Present if locked (content = reason)
  index        Per-worktree index/staging area
```

### 연결된 worktree의 .git 파일

```
gitdir: <relative path to>/project/.git/worktrees/<name>
```

### 리소스 공유

| 리소스 | 위치 | 공유 여부 |
|----------|----------|---------|
| 객체 | `$commonDir/objects/` | 예 |
| 설정 | `$commonDir/config` | 예 |
| Packed-refs | `$commonDir/packed-refs` | 예 |
| 후크 | `$gitDir/hooks/` | 아니요: Pitmaster는 연결된 worktree에서 공유 후크를 실행하지 않습니다 |
| HEAD | `$gitDir/HEAD` | 아니요(worktree별) |
| 인덱스 | `$gitDir/index` | 아니요(worktree별) |
| Loose refs | `$commonDir/refs/` | 예(HEAD와 `MERGE_HEAD` 같은 작업 ref는 worktree별) |
