
# Εγγραφή σε αποθετήρια

Το Pitmaster γράφει σε αποθετήρια git χρησιμοποιώντας τις ίδιες δυαδικές μορφές με το κανονικό δυαδικό αρχείο `git`. Τα αντικείμενα γράφονται στο χώρο αποθήκευσης loose αντικειμένων (συμπιεσμένα με zlib), το index γράφεται σε μορφή v2 (ή v3 όταν οι εγγραφές χρειάζονται εκτεταμένες σημαίες· ένα υπάρχον index διατηρεί την έκδοσή του) και τα refs γράφονται ως loose αρχεία. Τα δεδομένα που γράφονται μπορούν να επαληθευτούν με το `git fsck --strict`.

## Αρχικοποίηση αποθετηρίου

```php
use Pitmaster\Pitmaster;

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

Αυτό δημιουργεί:
- `.git/objects/` (κενό χώρο αποθήκευσης αντικειμένων)
- `.git/refs/heads/` και `.git/refs/tags/` (κενούς καταλόγους ref)
- `.git/HEAD` που περιέχει `ref: refs/heads/main`
- `.git/hooks/`, `.git/info/exclude` και `.git/description`
- `.git/config` με προεπιλεγμένες ρυθμίσεις

Περάστε το `'sha256'` ως δεύτερο όρισμα για να δημιουργήσετε αποθετήριο SHA-256: `Pitmaster::init($path, 'sha256')`.

Η προεπιλεγμένη διαμόρφωση δεν ορίζει το `core.logallrefupdates`, επομένως το Pitmaster δεν καταγράφει εγγραφές reflog σε αποθετήριο που δημιούργησε, μέχρι να ορίσετε αυτό το κλειδί σε `true` ([#8](https://github.com/inline0/pitmaster/issues/8)). Τα αποθετήρια που δημιουργούνται από το `git init` το έχουν ήδη ορισμένο.

## Σταδιοποίηση αρχείων

### προσθήκη

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

Κάθε όρισμα είναι μια διαδρομή αρχείου σχετική με τη ρίζα του working tree. Οι κατάλογοι και τα glob δεν επεκτείνονται, ενώ αν λείπει κάποιο αρχείο προκαλείται `RuntimeException`. Για κάθε αρχείο, το Pitmaster:
1. Διαβάζει το περιεχόμενο του αρχείου από το working tree
2. Δημιουργεί ένα αντικείμενο `Blob` και υπολογίζει το hash του
3. Γράφει το blob στο `.git/objects/` (συμπιεσμένο με zlib)
4. Ενημερώνει την εγγραφή του index για αυτή τη διαδρομή με το νέο hash και τα μεταδεδομένα αρχείου

### αφαίρεση

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

Αφαιρεί παρακολουθούμενες διαδρομές από το index και το working tree. Χρησιμοποιήστε το `--cached` για αφαίρεση μόνο από το index ή τα `-r` / `--recursive` για παρακολουθούμενους καταλόγους.

### μετακίνηση

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

Μετονομάζει το αρχείο στο working tree, αφαιρεί την παλιά εγγραφή από το index και προσθέτει τη νέα.

## Δημιουργία commits

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

Η διαδικασία commit:

1. **Δημιουργία δέντρου από το index**: η επίπεδη λίστα εγγραφών του index οργανώνεται σε ιεραρχία δέντρου (οι κατάλογοι γίνονται αντικείμενα tree, τα αρχεία γίνονται αναφορές blob). Κάθε tree γράφεται στον χώρο αποθήκευσης αντικειμένων.
2. **Καθορισμός γονέα**: το τρέχον commit HEAD γίνεται ο γονέας. Τα commit ρίζας (το πρώτο commit σε ένα repo) δεν έχουν γονείς.
3. **Καθορισμός συντάκτη**: λαμβάνεται από τις μεταβλητές περιβάλλοντος `PITMASTER_AUTHOR_*` και `GIT_AUTHOR_*`, έπειτα από τις σταθερές `PITMASTER_AUTHOR_NAME`/`PITMASTER_AUTHOR_EMAIL`, στη συνέχεια από το `.git/config` (`user.name`/`user.email`) και τέλος από το `Pitmaster <pitmaster@localhost>`. Ο committer επιλύεται με τον ίδιο τρόπο.
4. **Εγγραφή αντικειμένου commit**: το περιεχόμενο του commit (`tree`, `parent`, `author`, `committer`, μήνυμα) σειριοποιείται, κατακερματίζεται και γράφεται.
5. **Ενημέρωση HEAD**: το ref του branch στο οποίο δείχνει το HEAD ενημερώνεται με το νέο hash commit.

Τα hooks εκτελούνται γύρω από αυτά τα βήματα (`pre-commit`, `prepare-commit-msg`, `commit-msg`, `post-commit`), εκτός αν το handle ανοίχτηκε με `['hooks' => false]`. Το `commit()` προκαλεί σφάλμα όταν λείπει το μήνυμα, όταν το δέντρο δεν έχει αλλάξει σε σχέση με το HEAD ή όταν το index περιέχει μη συγχωνευμένες διαδρομές.

### Προσαρμοσμένος συντάκτης

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

## Επαλήθευση με git fsck

Η κάλυψη ενσωμάτωσης του Pitmaster ελέγχει τα αντικείμενα που γράφτηκαν έναντι του `git fsck --strict`, ενώ μπορείτε να κάνετε την ίδια επαλήθευση χειροκίνητα:

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

Αν το Pitmaster γράψει μη έγκυρο αντικείμενο, το `git fsck` θα αναφέρει αμέσως το σφάλμα.

## Διαχείριση branches

### Δημιουργία branch

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

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

Δημιουργεί ένα αρχείο ref στο `.git/refs/heads/<name>` που περιέχει το hash του commit-στόχου.

### Διαγραφή branch

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

### Εναλλαγή branches

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

Ενημερώνει το HEAD ώστε να είναι συμβολικό ref που δείχνει στο branch, επαναφέρει το index ώστε να ταιριάζει με το δέντρο της κορυφής του branch και ενημερώνει το working tree.

Οι τοπικές αλλαγές σε διαδρομές που διαφέρουν ανάμεσα στα δύο δέντρα σταματούν το checkout με ένα `RuntimeException`. Οι μη δεσμευμένες αλλαγές σε όλα τα άλλα παρακολουθούμενα αρχεία αντικαθίστανται από το περιεχόμενο του branch, όπως θα τις διατηρούσε το git ([#2](https://github.com/inline0/pitmaster/issues/2)). Κάντε commit ή stash τις αλλαγές πριν από την εναλλαγή.

### Έλεγχος αν έχει γίνει merge

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

## Διαχείριση tags

### Ελαφρύ tag

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

Ένα ελαφρύ tag είναι απλώς ένα ref που δείχνει απευθείας σε ένα commit.

### Σχολιασμένο tag

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

Ένα σχολιασμένο tag δημιουργεί ένα αντικείμενο tag (με συντάκτη tag, μήνυμα και στόχο) και ένα ref που δείχνει σε αυτό. Ο συντάκτης tag είναι η ταυτότητα του τρέχοντος committer, η οποία επιλύεται όπως και ο συντάκτης του commit.

### Προσαρμοσμένος συντάκτης tag

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

## Άμεση ενημέρωση refs

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

## Άμεση εγγραφή αντικειμένων

Για προχωρημένες περιπτώσεις χρήσης, μπορείτε να γράψετε αντικείμενα χωρίς να χρησιμοποιήσετε το υψηλού επιπέδου API.

### Εγγραφή blob

```php
use Pitmaster\Object\Blob;

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

Σε αποθετήριο SHA-256, περάστε τον αλγόριθμο: `Blob::fromContent('Hello, world!', 'sha256')`.

### Εγγραφή tree

```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()` γράφει τις εγγραφές με τη σειρά που δίνονται και τις συμβολοσειρές mode όπως δίνονται. Περάστε τις με τη σειρά tree του git (κατά όνομα, με τους καταλόγους να συγκρίνονται σαν το όνομά τους να τελείωνε σε `/`) και γράψτε τα mode καταλόγων ως `40000`: το git αποθηκεύει τα mode tree χωρίς το αρχικό μηδενικό, ενώ το `git fsck --strict` απορρίπτει το `040000`.

### Εγγραφή commit

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

## Επαναφορά

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

## Αποκατάσταση

```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 και revert

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

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

Και τα δύο δέχονται οποιοδήποτε revision αποδέχεται το `resolve()`. Τα πλήρη hash λειτουργούν· τα συντομευμένα όχι.
