
# PHP API

## `Pitmaster\Pitmaster`

Klasa `Pitmaster\Pitmaster` jest statycznym punktem wejścia. Udostępnia metody fabrykujące do otwierania, tworzenia i klonowania repozytoriów oraz funkcje pomocnicze do ich wykrywania.

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

### `open`

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

Otwórz istniejące repozytorium w `$path`. Ścieżka powinna wskazywać katalog główny drzewa roboczego (zawierający `.git/`). Obsługiwane są także powiązane worktree (w których `.git` jest plikiem zawierającym `gitdir: <path>`) oraz repozytoria bare (w których `$path` jest samym katalogiem `.git`).

`$options` przyjmuje dwie wartości logiczne, obie domyślnie `true`:

- `hooks`: uruchamianie skryptów `.git/hooks`. Przekaż `false`, aby je pominąć dla tego uchwytu.
- `processes`: zezwolenie na wszystko, co uruchamia proces lub otwiera transport sieciowy. Przekaż `false`, aby wyłączyć hooki, hook fsmonitor, klonowanie, pobieranie, wysyłanie oraz transport procesowy SSH. Odczyt i zapis lokalny nadal działają.

Zgłasza `InvalidArgumentException`, jeśli ścieżka nie wskazuje repozytorium git.

```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
```

Zainicjuj nowe repozytorium w `$path`. Tworzy strukturę katalogów `.git` z `objects/`, `refs/heads/`, `refs/tags/`, `hooks/`, `info/`, `HEAD` (wskazującym na `refs/heads/main`) oraz domyślnym `config`.

`$objectFormat` ma wartość `'sha1'` lub `'sha256'`. Repozytorium SHA-256 otrzymuje w konfiguracji `repositoryformatversion = 1` i `extensions.objectformat = sha256`. Każda inna wartość zgłasza `InvalidArgumentException`. `$options` przyjmuje te same klucze co `open()`.

Zgłasza `RuntimeException`, jeśli repozytorium już istnieje w tej ścieżce.

```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
```

Sklonuj zdalne repozytorium. Inicjuje nowe repo, wykrywa zdalne referencje, pobiera plik pack, konfiguruje zdalne gałęzie śledzące i tworzy drzewo robocze. Adresy URL SSH (`ssh://...` lub `user@host:path`) używają transportu SSH; pozostałe adresy URL używają inteligentnego HTTP, a gdy wymiana w tym trybie się nie powiedzie, przechodzą na prosty HTTP. Przekaż `$depth`, aby wykonać płytkie klonowanie.

Zgłasza `RuntimeException`, gdy `$options['processes']` ma wartość `false`. Jeśli klonowanie się nie powiedzie, a `$path` wcześniej nie istniało, częściowo utworzony katalog zostanie usunięty.

```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
```

Zwraca `['processes' => false, 'hooks' => false]` dla środowisk uruchomieniowych, które nie mogą uruchamiać procesów na hoście. Przekaż tę wartość do `open()` lub `init()`.

### `isRepository`

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

Sprawdź, czy ścieżka wskazuje repozytorium git. Zwraca `true` dla zwykłych repozytoriów (katalog `.git/`), powiązanych worktree (plik `.git`) i repozytoriów bare (gdy istnieje plik `HEAD`).

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

### `isWorktree`

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

Sprawdź, czy ścieżka wskazuje powiązane worktree (a nie główne repozytorium). Zwraca `true` tylko wtedy, gdy `.git` jest plikiem zawierającym odwołanie `gitdir:`.

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

### `commonGitDir`

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

Rozwiąż ścieżkę do wspólnego katalogu git na podstawie dowolnej ścieżki kopii roboczej. W zwykłych repozytoriach jest to katalog `.git`. W powiązanych worktree jest to współdzielony katalog git zawierający obiekty, konfigurację i packed-refs. Zwraca `null`, jeśli ścieżka nie wskazuje repozytorium.

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

---

## `Pitmaster\Repository`

Klasa `Pitmaster\Repository` jest głównym uchwytem do wszystkich operacji git. Można go uzyskać za pomocą `Pitmaster::open()`, `Pitmaster::init()` lub `Pitmaster::clone()`.

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

### Akcesory ścieżek

#### `gitDir`

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

Zwraca katalog git danego worktree. W zwykłych repozytoriach jest to `.git/`. W powiązanych worktree jest to `.git/worktrees/<name>/`.

#### `commonGitDir`

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

Zwraca współdzielony katalog git. W zwykłych repozytoriach jest taki sam jak `gitDir()`. W powiązanych worktree jest to katalog `.git/` głównego repozytorium (zawierający obiekty, konfigurację i packed-refs).

#### `workDir`

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

Zwraca ścieżkę katalogu głównego drzewa roboczego.

#### `isLinkedWorktree`

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

Zwraca `true`, jeśli ten uchwyt repozytorium wskazuje powiązane worktree.

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

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

Informuje, czy repozytorium nie ma drzewa roboczego oraz które opcje `hooks` / `processes` są aktywne dla tego uchwytu. `hooksEnabled()` ma wartość `false` zawsze wtedy, gdy procesy są wyłączone.

---

### Obiekty

#### `readObject`

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

Odczytaj dowolny obiekt, podając jego pełny skrót szesnastkowy (40 znaków lub 64 w repozytorium SHA-256). Skrócone skróty nie są akceptowane. Zwraca instancję `Blob`, `Tree`, `Commit` lub `Tag`. Najpierw przeszukuje luźne obiekty, a następnie pliki pack.

Zgłasza `ObjectNotFoundException`, jeśli nie znaleziono skrótu.

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

#### `writeObject`

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

Zapisz obiekt w magazynie luźnych obiektów. Zwraca obliczony `ObjectId`.

```php
use Pitmaster\Object\Blob;

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

#### `catFile`

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

Zwraca surową zawartość obiektu; odpowiednikiem jest `git cat-file -p`.

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

#### `objectExists`

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

Sprawdź, czy obiekt istnieje w repozytorium (wśród luźnych lub spakowanych obiektów).

#### `listObjects`

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

Wyświetl wszystkie skróty obiektów w repozytorium. Zwraca tablicę ciągów szesnastkowych.

---

### Referencje

#### `head`

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

Zwraca bieżący commit HEAD. Podąża za referencjami symbolicznymi (HEAD -> refs/heads/main -> skrót commita).

Zgłasza `RuntimeException`, jeśli HEAD nie wskazuje prawidłowego commita (e.g, puste repozytorium).

#### `branch`

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

Bez argumentów zwraca nazwę bieżącej gałęzi (e.g, `'main'`) lub `null`, jeśli HEAD jest odłączony.

Po podaniu nazwy gałęzi rozwiązuje ją do skrótu commita (ciągu szesnastkowego) lub zwraca `null`, jeśli gałąź nie istnieje.

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

#### `branches`

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

Zwraca posortowaną tablicę wszystkich nazw gałęzi (bez prefiksu `refs/heads/`).

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

#### `tags`

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

Zwraca posortowaną tablicę wszystkich nazw tagów (bez prefiksu `refs/tags/`).

#### `resolve`

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

Rozwiąż wyrażenie rewizji do obiektu `ObjectId`. Obsługiwane są:
- Pełne skróty szesnastkowe (40 znaków lub 64 dla SHA-256). Skrócone skróty, takie jak `abc1234`, nie są rozwiązywane.
- `HEAD`
- Nazwy gałęzi (`main`, `feature/login`)
- Nazwy tagów (`v1.0.0`)
- Pełne ścieżki referencji (`refs/heads/main`)
- Wyrażenia rewizji (`HEAD~3`, `main^2`)

Zgłasza `RuntimeException`, jeśli nie można rozwiązać rewizji.

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

#### `allRefs`

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

Zwraca wszystkie referencje w postaci tablicy asocjacyjnej mapującej nazwy referencji na skróty szesnastkowe.

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

#### `updateRef`

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

Zaktualizuj referencję, aby wskazywała nowy cel.

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

#### `createBranch`

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

Utwórz nową gałąź. Jeśli `$from` ma wartość null, utwórz gałąź na podstawie bieżącego HEAD.

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

#### `deleteBranch`

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

Usuń gałąź według nazwy. Zgłasza `RuntimeException`, jeśli gałąź jest wypisana lub nie istnieje. W przeciwieństwie do `git branch -d` nie sprawdza, czy gałąź została scalona.

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

#### `createLightweightTag`

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

Utwórz lekki tag. Jeśli `$target` ma wartość null, przypisz tag bezpośrednio do bieżącego 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
```

Utwórz tag z adnotacją. Jeśli `$target` ma wartość null, przypisz tag do bieżącego HEAD. Zwraca `ObjectId` obiektu tagu.

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

#### `deleteTag`

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

Usuń tag według nazwy.

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

#### `packRefs`

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

Zapisz ponownie współdzielony plik `packed-refs` na podstawie bieżącego zestawu luźnych referencji i usuń odpowiadające im współdzielone luźne referencje. W przypadku powiązanych worktree zapis następuje we wspólnym katalogu git.

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

#### `defaultBranch`

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

Rozwiąż domyślną gałąź repozytorium. Najpierw sprawdza symboliczną referencję HEAD zdalnego repozytorium, potem lokalny HEAD, a na końcu przechodzi do `main` lub `master`.

#### `isBranchMerged`

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

Sprawdź, czy gałąź została w pełni scalona z inną gałęzią (domyślnie z gałęzią domyślną).

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

#### `checkout`

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

Przełącz gałęzie lub odłącz HEAD. Aktualizuje HEAD, indeks i drzewo robocze. `$target` może być nazwą gałęzi, tagiem, pełnym skrótem lub wyrażeniem rewizji; jeśli nie wskazuje gałęzi, HEAD zostaje odłączony.

Zgłasza `RuntimeException`, gdy zmiana lokalna (przygotowana lub nie) albo nieśledzony plik znajduje się w ścieżce, która różni się między bieżącym a docelowym drzewem.

Nieutrwalone zmiany w innych śledzonych plikach nie zostaną przeniesione: indeks i drzewo robocze są odtwarzane na podstawie docelowego drzewa, więc zmiany w plikach identycznych po obu stronach zostaną utracone, podczas gdy git by je zachował ([#2](https://github.com/inline0/pitmaster/issues/2)). Najpierw je zatwierdź lub odłóż do schowka. `merge()`, `cherryPick()`, `revert()`, `rebase()` i metody bisect odtwarzają drzewo robocze w ten sam sposób.

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

---

### Indeks

#### `index`

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

Odczytaj i zwróć bieżący indeks (obszar przygotowania) z `.git/index`.

#### `add`

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

Przygotuj pliki. Odczytuje każdy plik z drzewa roboczego, tworzy obiekt blob i aktualizuje indeks. Każdy argument musi być ścieżką pliku (lub dowiązania symbolicznego) względem katalogu głównego drzewa roboczego; katalogi i wzorce glob nie są rozwijane, a brakujący plik powoduje zgłoszenie `RuntimeException`.

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

#### `remove`

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

Usuń śledzone ścieżki zarówno z indeksu, jak i drzewa roboczego. Obsługuje `--cached` oraz `-r` / `--recursive` na liście argumentów, zapewniając działanie zgodne z Git.

```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
```

Przenieś lub zmień nazwę pliku zarówno w drzewie roboczym, jak i w indeksie.

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

---

### Commity

#### `commit`

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

Utwórz commit na podstawie bieżącego indeksu. Buduje hierarchię drzewa z wpisów indeksu, tworzy obiekt commita z HEAD jako rodzicem (jeśli istnieje) i aktualizuje HEAD. Zwraca `ObjectId` nowego commita.

`$message` może mieć wartość `null` tylko wtedy, gdy scalanie, cherry-pick, cofanie lub rebase zostały wstrzymane z powodu konfliktu; przygotowana wiadomość zostanie wtedy użyta. W pozostałych przypadkach wiadomość `null` powoduje zgłoszenie `RuntimeException('Commit message required')`. Commit jest również zgłaszany, gdy indeks zawiera nierozstrzygnięte ścieżki, indeks jest pusty w repozytorium bez commitów lub drzewo jest niezmienione względem HEAD.

Parametr `$author` to pełny ciąg autora git (`Name <email> timestamp timezone`). Jeśli ma wartość null, jest ustalany na podstawie zmiennych środowiskowych, stałych i `.git/config`, w kolejności opisanej w [Pierwsze kroki](https://pitmaster.inline0.dev/pl/docs/getting-started).

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

#### `show`

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

Pokaż commit lub podobną do niego rewizję oraz różnice względem pierwszego rodzica. Gdy rewizja wskazuje na tag z adnotacją, zwracana wartość zawiera również `['tag' => Tag]`, a `commit` wskazuje na docelowy commit po rozwinięciu tagu.

```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
```

Zresetuj HEAD do commita. Tryby:
- `'soft'`: przesuń tylko HEAD
- `'mixed'`: przesuń HEAD i zresetuj indeks (domyślnie)
- `'hard'`: przesuń HEAD, zresetuj indeks i zresetuj drzewo robocze

```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
```

Przywróć ścieżkę z indeksu lub wskazanej rewizji źródłowej. Jeśli nie ustawiono żadnej z flag, Pitmaster przywraca drzewo robocze z indeksu (lub z `$source`, jeśli podano). `staged: true` przywraca indeks z HEAD (lub `$source`), pozostawiając drzewo robocze bez zmian; przekaż `staged: true, worktree: true`, aby przywrócić oba.

```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
```

Zastosuj commit jako nowy commit w bieżącej gałęzi, zachowując pierwotnego autora. Jeśli konflikty wstrzymają operację, rozwiąż je, przygotuj ścieżki, a następnie wywołaj `cherryPickContinue()` lub `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
```

Utwórz commit cofający inny commit. Jeśli konflikt wstrzyma cofanie, rozwiąż go, przygotuj ścieżki, a następnie wywołaj `revertContinue()` lub `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
```

Wykonaj rebase bieżącej gałęzi na podstawie innej rewizji. Pitmaster obsługuje obecnie rebase liniowe ze stanem zatrzymania z powodu konfliktów zgodnym z Git oraz kontynuowanie, przerywanie i pomijanie operacji. HEAD musi wskazywać gałąź.

`rebase()`, `rebaseContinue()` i `rebaseSkip()` zwracają `['success' => bool, 'commits' => int, 'conflicts' => list<string>]`: informację, czy rebase się zakończył, liczbę zastosowanych commitów oraz ścieżki powodujące konflikt, jeśli operacja została wstrzymana.

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

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

---

### Stan

#### `status`

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

Oblicz stan drzewa roboczego. Zwraca tablicę obiektów `StatusEntry`, z których każdy zawiera:
- `path`: ścieżkę pliku
- `index`: wyliczenie `FileStatus` dla zmian między HEAD a indeksem
- `worktree`: wyliczenie `FileStatus` dla zmian między indeksem a drzewem roboczym
- `origPath`, `renameScore`: ustawione dla zmienionych nazw wpisów, w przeciwnym razie `null`

Nieśledzone pliki mają `FileStatus::Untracked` w obu kolumnach. Ignorowane pliki nie są zgłaszane.

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

#### `statusPorcelainV2`

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

Wyjście stanu czytelne dla maszyn, w formacie porcelain v2 systemu git.

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

---

### Różnice

#### `diff`

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

Porównaj drzewo robocze z indeksem (zmiany nieprzygotowane). Zwraca tablicę obiektów `DiffResult`. `$pathspec` filtruje wyniki według jednej dokładnej ścieżki pliku. `$algorithm` ma wartość `'myers'` (domyślnie), `'patience'`, `'histogram'` lub `'minimal'`; każda inna wartość powoduje zgłoszenie `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
```

Porównaj indeks z HEAD (zmiany przygotowane). Zwraca `DiffResult[]` z wykrywaniem zmian nazw między usuniętymi a dodanymi plikami.

Argument `$pathspec` jest obecnie ignorowany, więc zwracany jest każdy przygotowany plik. Przed pierwszym commitem metoda zwraca pustą tablicę ([#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
```

Porównaj dwa drzewa na podstawie ich `ObjectId`. Zwraca `DiffResult[]`. Przekaż identyfikatory drzew, a nie commitów: identyfikator commita nie jest odczytywany jako drzewo i daje pusty wynik.

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

---

### Historia

#### `log`

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

Przejdź historię commitów, zaczynając od najnowszych według znacznika czasu committera. Zwraca `Commit[]`. Jeśli `$from` ma wartość null, zaczyna od HEAD. Zwraca pustą tablicę, jeśli HEAD nie wskazuje jeszcze żadnego commita.

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

#### `logAll`

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

Przejdź commity osiągalne z każdej referencji i HEAD, zaczynając od najnowszych. Zwraca `Commit[]`.

#### `logOneline`

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

Zwróć wiersze dziennika w formacie `git log --oneline` (`abc1234 Subject`), z HEAD, ze wszystkich referencji (`$all`) lub przefiltrowane według ścieżki.

#### `logPath`

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

Dziennik przefiltrowany według ścieżki pliku. Zwraca tylko te commity, których różnice względem pierwszego rodzica dodają, modyfikują, usuwają lub zmieniają nazwę dokładnie tego pliku. Ścieżki katalogów nie są dopasowywane.

Sprawdzanych jest tylko `5 * $limit` najnowszych commitów, więc starsze commity dotyczące pliku mogą nie pojawić się w wynikach ([#7](https://github.com/inline0/pitmaster/issues/7)).

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

---

### Scalanie

#### `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
```

Scal gałąź (lub dowolną rewizję) z HEAD. Automatycznie wykrywa scalenia typu fast-forward. W przypadku scalenia bez fast-forward sprawdza konflikty na poziomie drzewa i tworzy commit scalający, jeśli nie ma konfliktów. Gdy wystąpi konflikt, zapisuje w indeksie i drzewie roboczym stan scalania zgodny z Git; rozwiąż konflikty, przygotuj ścieżki, a następnie wywołaj `mergeContinue()` lub `mergeAbort()`.

`$strategy` `'ours'` zapisuje commit scalający, który zachowuje drzewo HEAD; każda inna wartość powoduje użycie domyślnego scalania rekurencyjnego. `mergeOctopus()` scala co najmniej dwie gałęzie w jeden commit. Jeśli wystąpi konflikt w którejkolwiek gałęzi, zwraca wynik wskazujący na nieudane scalanie bez zapisywania stanu scalania ani tworzenia commita.

Scalenie gałęzi, która jest już zawarta w HEAD, tworzy kolejny commit scalający zamiast zgłosić „Already up to date” ([#6](https://github.com/inline0/pitmaster/issues/6)). Niezatwierdzone zmiany w śledzonych plikach są nadpisywane, jak opisano w sekcji `checkout()`.

Zwraca `MergeResult` z:
- `clean`: informacją, czy scalanie zakończyło się bez konfliktów
- `commitId`: identyfikatorem commita scalającego (jeśli scalanie przebiegło czysto)
- `conflictPaths`: tablicą ścieżek do plików powodujących konflikt (jeśli wystąpił konflikt)
- `mergedContents`: ścieżką do pliku z zawartością i znacznikami konfliktu (jeśli wystąpił konflikt)

```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
```

Znajduje bazę scalania (najniższego wspólnego przodka) dwóch commitów. Zwraca null, jeśli nie istnieje wspólny przodek.

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

### Bisekcja

```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
```

Wyszukiwanie binarne commita, który wprowadził zmianę. `bisectStart()` zapisuje błędne i poprawne rewizje, przełącza się na pierwszego kandydata i go zwraca. `bisectGood()` i `bisectBad()` oznaczają bieżący HEAD (lub `$revision`), a następnie przełączają się na kolejnego kandydata i go zwracają. Gdy pozostaje jeden podejrzany commit, zostaje on zwrócony i nadal jest kandydatem: to pierwszy błędny commit. `bisectReset()` kończy sesję i wraca do gałęzi lub commita, od którego się rozpoczęła.

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

---

### Sieć

#### `fetch`

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

Pobiera dane ze zdalnego repozytorium. Pobiera nowe obiekty i aktualizuje zdalne referencje śledzące. Przekaż `$depth`, aby pogłębić płytką historię lub ograniczyć jej zakres.

`fetch()` i metody `push*()` zgłaszają `RuntimeException` dla uchwytu otwartego z `['processes' => false]`.

```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
```

Wysyła bieżącą gałąź (lub wskazaną gałąź) do zdalnego repozytorium. Dostępne są warianty force-with-lease, atomic i mirror, które zapewniają ściślejszą kontrolę lub szerszą aktualizację referencji.

```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();
```

---

### Drzewa robocze

#### `addWorktree`

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

Dodaje powiązane drzewo robocze. Tworzy gałąź, jeśli jeszcze nie istnieje, konfiguruje metadane drzewa roboczego i tworzy pliki w drzewie roboczym. Przekaż `$name`, gdy potrzebujesz deterministycznego sluga metadanych, który różni się od nazwy katalogu kopii roboczej.

```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
```

Usuwa powiązane drzewo robocze. Użyj `$force = true`, aby usunąć je nawet wtedy, gdy jest zablokowane.

#### `worktrees`

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

Wyświetla wszystkie drzewa robocze (główne i powiązane). Zwraca `Worktree[]`.

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

---

### Konfiguracja

#### `config`

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

Umożliwia dostęp do konfiguracji git repozytorium. Odczytuje i zapisuje wartości konfiguracji.

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

---

### Dostęp wewnętrzny

Te metody udostępniają komponenty wewnętrzne do zaawansowanych zastosowań.

#### `objectDatabase`

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

Umożliwia dostęp do surowej bazy obiektów (luźne obiekty i magazyny pakietów).

#### `refDatabase`

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

Umożliwia dostęp do surowej bazy referencji (luźne i spakowane referencje).

---

## Klasy niższego poziomu

Te klasy nie są opakowane przez `Repository` i są tworzone na podstawie jego komponentów wewnętrznych.

### `Pitmaster\Graph\Blame`

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

Zwraca po jednym wpisie na każdy wiersz `$path` w `$headId`: `['line' => int, 'hash' => string, 'author' => string, 'content' => string]`. `author` to pełny ciąg identyfikacyjny (`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
```

Przeszukuje bloby drzewa. Zwraca `['path' => string, 'line' => int, 'content' => string]` dla każdego pasującego wiersza. Opcje: `regex` (traktuje wzorzec jako wzorzec PCRE) oraz `ignore_case`. Nieprawidłowe wyrażenie regularne powoduje zgłoszenie `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
```

Odczytuje i zapisuje notatki git przechowywane pod referencją notes. `listAll()` zwraca mapowanie skrótów commitów na treść notatek.

```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'));
```
