
# Pisanje v repozitorije

Pitmaster zapisuje repozitorije git z enakimi binarnimi formati kot kanarni binarni program `git`. Objekti se zapišejo v shrambo razvezanih objektov (stisnjeno z zlib), indeks se zapiše v formatu v2 (ali v3, kadar vnosi potrebujejo razširjene zastavice; obstoječi indeks ohrani svojo različico), sklici pa se zapišejo kot razvezane datoteke. Zapisane podatke je mogoče preveriti z `git fsck --strict`.

## Inicializacija repozitorija

```php
use Pitmaster\Pitmaster;

$repo = Pitmaster::init('/path/to/new-project');
```

To ustvari:
- `.git/objects/` (prazna shramba objektov)
- `.git/refs/heads/` in `.git/refs/tags/` (prazna imenika sklicev)
- `.git/HEAD`, ki vsebuje `ref: refs/heads/main`
- `.git/hooks/`, `.git/info/exclude` in `.git/description`
- `.git/config` s privzetimi nastavitvami

Za ustvarjanje repozitorija SHA-256 kot drugi argument podajte `'sha256'`: `Pitmaster::init($path, 'sha256')`.

Privzeta konfiguracija ne nastavi `core.logallrefupdates`, zato Pitmaster v ustvarjenem repozitoriju ne zabeleži nobenega vnosa refloga, dokler tega ključa ne nastavite na `true` ([#8](https://github.com/inline0/pitmaster/issues/8)). Repozitoriji, ki jih ustvari `git init`, ga že imajo nastavljenega.

## Priprava datotek

### add

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

Vsak argument je pot do datoteke glede na koren delovnega drevesa. Imeniki in globi se ne razširijo, manjkajoča datoteka pa sproži `RuntimeException`. Za vsako datoteko Pitmaster:
1. Prebere vsebino datoteke iz delovnega drevesa
2. Ustvari objekt `Blob` in izračuna njegovo zgoščeno vrednost
3. Zapiše blob v `.git/objects/` (stisnjen z zlib)
4. Posodobi vnos v indeksu za to pot z novo zgoščeno vrednostjo in metapodatki datoteke

### remove

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

Odstrani sledene poti iz indeksa in delovnega drevesa. Za odstranitev samo iz indeksa uporabite `--cached`, za sledene imenike pa `-r` / `--recursive`.

### mv

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

Preimenuje datoteko v delovnem drevesu, odstrani stari vnos iz indeksa in doda novega.

## Ustvarjanje potrditev

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

Postopek potrditve:

1. **Izdelava drevesa iz indeksa**: ploščat seznam vnosov indeksa se uredi v hierarhijo dreves (imeniki postanejo objekti dreves, datoteke pa reference na blobe). Vsako drevo se zapiše v shrambo objektov.
2. **Določitev nadrejenega elementa**: trenutna potrditev HEAD postane nadrejeni element. Korenske potrditve (prva potrditev v repozitoriju) nimajo nadrejenih elementov.
3. **Določitev avtorja**: vzame se iz spremenljivk okolja `PITMASTER_AUTHOR_*` in `GIT_AUTHOR_*`, nato iz konstant `PITMASTER_AUTHOR_NAME`/`PITMASTER_AUTHOR_EMAIL`, nato iz `.git/config` (`user.name`/`user.email`), nazadnje pa iz `Pitmaster <pitmaster@localhost>`. Potrditelj se določi na enak način.
4. **Zapis objekta potrditve**: vsebina potrditve (`tree`, `parent`, `author`, `committer`, sporočilo) se serializira, zgosti in zapiše.
5. **Posodobitev HEAD**: sklic veje, na katero kaže HEAD, se posodobi na novo zgoščeno vrednost potrditve.

Okoli teh korakov se izvedejo kavlji (`pre-commit`, `prepare-commit-msg`, `commit-msg`, `post-commit`), razen če je bil ročaj odprt z `['hooks' => false]`. `commit()` sproži napako, če manjka sporočilo, če se drevo ni spremenilo glede na HEAD ali če so v indeksu nerazrešene poti.

### Avtor po meri

```php
$author = 'Jane Smith <jane@example.com> 1700000000 +0000';
$commitId = $repo->commit('Add feature', $author);
```

## Preverjanje z fsck ukaza git

Integracijski testi za Pitmaster preverjajo zapisane objekte glede na `git fsck --strict`, enako pa lahko preverite tudi ročno:

```bash
cd /path/to/repo
git fsck --strict
```

Če Pitmaster zapiše neveljaven objekt, `git fsck` napako takoj prikaže.

## Upravljanje vej

### Ustvarjanje veje

```php
// Branch from HEAD
$repo->createBranch('feature/new-thing');

// Branch from a specific commit
$repo->createBranch('hotfix', $repo->resolve('v1.0.0'));
```

Ustvari datoteko sklica na `.git/refs/heads/<name>`, ki vsebuje zgoščeno vrednost ciljne potrditve.

### Brisanje veje

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

### Preklapljanje med vejami

```php
$repo->checkout('feature/login');
```

Posodobi HEAD v simbolični sklic na vejo, ponastavi indeks, da se ujema z drevesom na vrhu veje, in posodobi delovno drevo.

Lokalne spremembe na poteh, ki se med drevesoma razlikujejo, ustavijo prevzem z `RuntimeException`. Nepotrjene spremembe v vseh drugih sledenih datotekah se nadomestijo z vsebino veje; ukaz git bi jih ohranil ([#2](https://github.com/inline0/pitmaster/issues/2)). Pred preklopom jih potrdite ali shranite v zalogo.

### Preverjanje združitve

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

## Upravljanje oznak

### Lahka oznaka

```php
$repo->createLightweightTag('v1.0.0');                          // At HEAD
$repo->createLightweightTag('v0.9.0', $repo->resolve('HEAD~5'));
```

Lahka oznaka je zgolj sklic, ki kaže neposredno na potrditev.

### Označena oznaka

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

Označena oznaka ustvari objekt oznake (z avtorjem oznake, sporočilom in ciljem) ter sklic, ki kaže nanj. Avtor oznake je trenutna identiteta potrditelja, določena enako kot avtor potrditve.

### Avtor oznake po meri

```php
$tagId = $repo->createTag(
    'v2.0.0',
    'Release version 2.0.0',
    $repo->resolve('HEAD'),
    'Jane Smith <jane@example.com> 1700000000 +0000',
);
```

## Neposredno posodabljanje sklicev

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

## Neposredno zapisovanje objektov

Za napredne primere uporabe lahko objekte zapišete brez uporabe visokoravenskega API.

### Zapis bloba

```php
use Pitmaster\Object\Blob;

$blob = Blob::fromContent('Hello, world!');
$id = $repo->writeObject($blob);
echo $id->hex;  // SHA-1 of "blob 13\0Hello, world!"
```

V repozitoriju SHA-256 podajte algoritem: `Blob::fromContent('Hello, world!', 'sha256')`.

### Zapis drevesa

```php
use Pitmaster\Object\Tree;
use Pitmaster\Object\TreeEntry;
use Pitmaster\Object\ObjectId;

$entries = [
    new TreeEntry('100644', 'README.md', $blobId),
    new TreeEntry('40000', 'src', $subtreeId),
];

$tree = Tree::fromEntries($entries);
$id = $repo->writeObject($tree);
```

`Tree::fromEntries()` zapiše vnose v navedenem vrstnem redu in nize načinov takšne, kot so podani. Podajte jih v drevesnem vrstnem redu git (po imenu, pri čemer se imeniki primerjajo, kot da bi se njihovo ime končalo z `/`), načine imenikov pa zapišite kot `40000`: git shrani načine dreves brez začetne ničle, `git fsck --strict` pa zavrne `040000`.

### Zapis potrditve

```php
use Pitmaster\Object\Commit;
use Pitmaster\Object\ObjectId;
use Pitmaster\Object\ObjectType;

$content = Commit::buildContent(
    tree: $treeId,
    parents: [$parentId],
    author: 'Name <email> 1700000000 +0000',
    committer: 'Name <email> 1700000000 +0000',
    message: "Commit message\n",
);

$commitId = ObjectId::compute(ObjectType::Commit, $content);
$commit = Commit::parse($content, $commitId);
$repo->writeObject($commit);
```

## Ponastavitev

```php
// Soft: move HEAD only
$repo->reset('HEAD~1', 'soft');

// Mixed: move HEAD + reset index (default)
$repo->reset('HEAD~1');

// Hard: move HEAD + reset index + reset working tree
$repo->reset('HEAD~3', 'hard');
```

## Obnovitev

```php
// Restore a file from the index into the worktree
$repo->restore('src/File.php');

// Restore the index version from HEAD (the worktree is left alone)
$repo->restore('src/File.php', staged: true);

// Restore from a specific commit
$repo->restore('src/File.php', 'HEAD~1');

// Restore both index and worktree from a specific commit
$repo->restore('src/File.php', 'HEAD~1', staged: true, worktree: true);
```

## Cherry-pick in razveljavitev

```php
// Apply a commit on the current branch
$newId = $repo->cherryPick('feature~1');

// Undo a commit
$revertId = $repo->revert('HEAD~2');
```

Oba sprejmeta katero koli revizijo, ki jo sprejema `resolve()`. Polne zgoščene vrednosti delujejo; skrajšane ne.
