
# Kirjoittaminen repositorioihin

Pitmaster kirjoittaa git-repositorioita käyttäen samoja binaarimuotoja kuin gitin kanoninen `git`-binaari. Objektit kirjoitetaan irto-objektivarastoon (zlib-pakattuina), indeksi kirjoitetaan v2-muodossa (tai v3-muodossa, jos merkinnät tarvitsevat laajennettuja lippuja; olemassa oleva indeksi säilyttää versionsa), ja viitteet kirjoitetaan irtotiedostoina. Kirjoitetut tiedot voi varmentaa komennolla `git fsck --strict`.

## Repositorion alustaminen

```php
use Pitmaster\Pitmaster;

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

Tämä luo:
- `.git/objects/` (tyhjä objektivarasto)
- `.git/refs/heads/` ja `.git/refs/tags/` (tyhjät viitehakemistot)
- `.git/HEAD`, joka sisältää `ref: refs/heads/main`
- `.git/hooks/`, `.git/info/exclude` ja `.git/description`
- `.git/config` oletusasetuksilla

Välitä `'sha256'` toisena argumenttina luodaksesi SHA-256-repositorion: `Pitmaster::init($path, 'sha256')`.

Oletusasetuksissa ei määritetä avainta `core.logallrefupdates`, joten Pitmaster ei kirjaa luomansa repositorion reflog-merkintöjä ennen kuin asetat avaimeksi `true` ([#8](https://github.com/inline0/pitmaster/issues/8)). Komennolla `git init` luodut repositoriot määrittävät sen jo valmiiksi.

## Tiedostojen lisääminen indeksiin

### add

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

Kukin argumentti on tiedostopolku suhteessa työhakemiston juureen. Hakemistoja ja jokerimerkkejä ei laajenneta, ja puuttuva tiedosto aiheuttaa virheen `RuntimeException`. Kullekin tiedostolle Pitmaster:
1. Lukee tiedoston sisällön työhakemistosta
2. Luo `Blob`-objektin ja laskee sen tiivisteen
3. Kirjoittaa blobin sijaintiin `.git/objects/` (zlib-pakattuna)
4. Päivittää kyseisen polun indeksimerkinnän uudella tiivisteellä ja tiedoston metatiedoilla

### remove

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

Poistaa seuratut polut indeksistä ja työhakemistosta. Käytä asetusta `--cached` vain indeksistä poistamiseen tai asetusta `-r` / `--recursive` seurattujen hakemistojen poistamiseen.

### mv

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

Nimeää tiedoston uudelleen työhakemistossa, poistaa vanhan indeksimerkinnän ja lisää uuden.

## Commitien luominen

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

Commit-prosessi:

1. **Rakenna puu indeksistä**: indeksimerkintöjen litteä luettelo järjestetään puuhierarkiaksi (hakemistoista tulee puuobjekteja, tiedostot ovat blob-viittauksia). Kukin puu kirjoitetaan objektivarastoon.
2. **Määritä ylätaso**: nykyisestä HEAD-commitista tulee ylätaso. Juuricommitilla (repositorion ensimmäisellä commitilla) ei ole ylätasoja.
3. **Määritä tekijä**: haetaan ympäristömuuttujista `PITMASTER_AUTHOR_*` ja `GIT_AUTHOR_*`, sitten vakioista `PITMASTER_AUTHOR_NAME`/`PITMASTER_AUTHOR_EMAIL`, sitten `.git/config`-asetuksesta (`user.name`/`user.email`) ja lopuksi asetuksesta `Pitmaster <pitmaster@localhost>`. Commitin tekijä selvitetään samalla tavalla.
4. **Kirjoita commit-objekti**: commitin sisältö (`tree`, `parent`, `author`, `committer` ja viesti) sarjoitetaan, tiivistetään ja kirjoitetaan.
5. **Päivitä HEAD**: haara, johon HEAD viittaa, päivitetään uuteen commit-tiivisteeseen.

Koukkuja suoritetaan näiden vaiheiden yhteydessä (`pre-commit`, `prepare-commit-msg`, `commit-msg`, `post-commit`), paitsi jos kahva on avattu asetuksella `['hooks' => false]`. `commit()` aiheuttaa virheen, jos viesti puuttuu, puu ei ole muuttunut HEADiin verrattuna tai indeksissä on ratkaisemattomia polkuja.

### Mukautettu tekijä

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

## Varmennus komennolla git fsck

Pitmaster-integraatiotestit tarkistavat kirjoitetut objektit komentoa `git fsck --strict` vasten, ja voit tehdä saman tarkistuksen itse:

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

Jos Pitmaster kirjoittaa virheellisen objektin, `git fsck` ilmoittaa virheestä heti.

## Haarojen hallinta

### Haaran luominen

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

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

Luo viitetiedoston sijaintiin `.git/refs/heads/<name>`; tiedosto sisältää kohdecommitin tiivisteen.

### Haaran poistaminen

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

### Haaran vaihtaminen

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

Päivittää HEADin haaraan osoittavaksi symboliseksi viitteeksi, palauttaa indeksin vastaamaan haaran kärjen puuta ja päivittää työhakemiston.

Poluissa, joiden sisältö eroaa puiden välillä, olevat paikalliset muutokset keskeyttävät checkoutin virheellä `RuntimeException`. Kaikkien muiden seurattujen tiedostojen commitoimattomat muutokset korvataan haaran sisällöllä, jos git säilyttäisi ne ([#2](https://github.com/inline0/pitmaster/issues/2)). Commitoi tai kätke muutokset ennen haaran vaihtamista.

### Yhdistämisen tarkistaminen

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

## Tagien hallinta

### Kevyt tagi

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

Kevyt tagi on pelkkä viite, joka osoittaa suoraan committiin.

### Annotated-tagi

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

Annotated-tagi luo tagiobjektin (joka sisältää tagin luojan, viestin ja kohteen) sekä siihen osoittavan viitteen. Tagin luoja on nykyinen commitin tekijä, joka selvitetään samalla tavalla kuin commitin tekijä.

### Mukautettu tagin luoja

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

## Viitteiden suora päivittäminen

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

## Objektien suora kirjoittaminen

Edistyneissä käyttötapauksissa voit kirjoittaa objekteja käyttämättä korkean tason API-rajapintaa.

### Blobin kirjoittaminen

```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älitä `Blob::fromContent('Hello, world!', 'sha256')`-repositoriossa algoritmi: SHA-256.

### Puun kirjoittaminen

```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()` kirjoittaa merkinnät annetussa järjestyksessä ja tilamerkkijonot annettuina. Välitä ne gitin puujärjestyksessä (nimen mukaan; hakemistojen nimeen verrataan lisättynä loppuun `/`) ja anna hakemistojen tilaksi `40000`: git tallentaa puutilat ilman alkunollaa, ja `git fsck --strict` hylkää `040000`.

### Commitin kirjoittaminen

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

## Resetointi

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

## Palauttaminen

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

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

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

Molemmat hyväksyvät minkä tahansa revision, jonka `resolve()` hyväksyy. Täydet tiivisteet toimivat, lyhennetyt eivät.
