Αντικείμενα Git
Όλα τα στοιχεία σε ένα αποθετήριο git αποθηκεύονται ως αντικείμενα. Υπάρχουν τέσσερις τύποι: blobs (περιεχόμενο αρχείων), trees (καταχωρίσεις καταλόγων), commits (στιγμιότυπα με μεταδεδομένα) και tags (σχολιασμένες αναφορές). Κάθε αντικείμενο ταυτοποιείται από το SHA-1 του περιεχομένου του.
Τύποι αντικειμένων
Blob
Ένα blob περιέχει ακατέργαστο περιεχόμενο αρχείου χωρίς μεταδεδομένα (όνομα αρχείου ή δικαιώματα).
use Pitmaster\Object\Blob;
// Create from content
$blob = Blob::fromContent('Hello, world!');
echo $blob->content; // 'Hello, world!'
echo $blob->size(); // 13
echo $blob->id->hex; // SHA-1 hash
echo $blob->type->value; // 'blob' (the property is ObjectType::Blob)
Δέντρο
Ένα tree είναι μια καταχώριση καταλόγου. Κάθε εγγραφή έχει μια λειτουργία, ένα όνομα και ένα hash που δείχνει σε blob ή υποδέντρο.
use Pitmaster\Object\Tree;
use Pitmaster\Object\TreeEntry;
// Read a tree
$tree = $repo->readObject($treeHash);
foreach ($tree->entries as $entry) {
echo "{$entry->mode} {$entry->name} {$entry->hash->hex}\n";
}
// Look up a specific entry
$entry = $tree->entry('README.md');
// Create a tree (entries already in git's sort order)
$entries = [
new TreeEntry('100644', 'README.md', $blobId),
new TreeEntry('100755', 'deploy.sh', $execBlobId),
new TreeEntry('40000', 'src', $subtreeId),
];
$tree = Tree::fromEntries($entries);
Tree::fromEntries() γράφει τις εγγραφές με τη σειρά που δίνονται και τις συμβολοσειρές λειτουργίας ακριβώς όπως δίνονται, επομένως ταξινομήστε τις πρώτα και γράψτε τις λειτουργίες καταλόγων ως 40000. Ένα tree που γράφεται με 040000 ή με άλλη σειρά έχει διαφορετικό hash από εκείνο του git, ενώ το git fsck --strict αναφέρει τη λειτουργία με μηδενικά στην αρχή.
TreeEntry
Το TreeEntry είναι ένα αντικείμενο τιμής μόνο για ανάγνωση:
use Pitmaster\Object\TreeEntry;
$entry->mode; // '100644', '100755', '40000', '120000', '160000'
$entry->name; // Filename
$entry->hash; // ObjectId
// Type checks
$entry->isBlob(); // regular or executable file (mode 10xxxx)
$entry->isExecutable(); // mode 100755
$entry->isTree(); // mode 40000 (also accepts 040000)
$entry->isSymlink(); // mode 120000
$entry->isGitlink(); // mode 160000 (submodule)
Τιμές λειτουργίας:
| Λειτουργία | Σημασία |
|---|---|
100644 |
Κανονικό αρχείο |
100755 |
Εκτελέσιμο αρχείο |
40000 |
Κατάλογος (tree), εμφανίζεται ως 040000 από το git ls-tree |
120000 |
Συμβολικός σύνδεσμος |
160000 |
Gitlink (υπομονάδα) |
Οι εγγραφές tree ταξινομούνται κατά όνομα, με τα ονόματα καταλόγων να θεωρείται ότι τελειώνουν σε / για τους σκοπούς της σύγκρισης.
Υποβολή
Ένα commit συνδέει ένα tree με το ιστορικό και τα μεταδεδομένα του.
use Pitmaster\Object\Commit;
$commit = $repo->head();
$commit->id; // ObjectId
$commit->tree; // ObjectId of the tree
$commit->parents; // array of ObjectId (zero for root, one for normal, two+ for merge)
$commit->author; // 'Name <email> timestamp timezone'
$commit->committer; // 'Name <email> timestamp timezone'
$commit->message; // Full commit message
$commit->isMerge(); // true if two or more parents
Δημιουργία περιεχομένου commit
$content = Commit::buildContent(
tree: $treeId,
parents: [$parentId],
author: 'Jane <jane@example.com> 1700000000 +0000',
committer: 'Jane <jane@example.com> 1700000000 +0000',
message: "Add feature\n",
);
Η ακατέργαστη μορφή commit:
tree <hex hash>\n
parent <hex hash>\n (zero or more)
author <name> <<email>> <timestamp> <tz>\n
committer <name> <<email>> <timestamp> <tz>\n
\n
<message>
Ετικέτα
Τα σχολιασμένα tags είναι αντικείμενα με μεταδεδομένα.
use Pitmaster\Object\Tag;
$tag = $repo->readObject($tagHash);
$tag->object; // ObjectId of the target
$tag->objectType; // ObjectType enum (ObjectType::Commit, ...)
$tag->name; // Tag name
$tag->tagger; // 'Name <email> timestamp timezone'
$tag->message; // Tag message
Ακατέργαστη μορφή tag:
object <hex hash>\n
type commit\n
tag <name>\n
tagger <name> <<email>> <timestamp> <tz>\n
\n
<message>
ObjectId
Το ObjectId είναι ένα αντικείμενο τιμής μόνο για ανάγνωση που αναπαριστά ένα hash SHA-1 (ή SHA-256).
use Pitmaster\Object\ObjectId;
use Pitmaster\Object\ObjectType;
// From hex string
$id = ObjectId::fromHex('a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2');
// From binary (20 bytes for SHA-1, 32 for SHA-256)
$id = ObjectId::fromBinary($rawBytes);
// Compute from content
$id = ObjectId::compute(ObjectType::Blob, 'Hello, world!');
$id = ObjectId::compute(ObjectType::Blob, 'Hello, world!', 'sha256');
// Zero hash
$id = ObjectId::zero();
$id = ObjectId::zero('sha256');
// Properties
$id->hex; // 40-character hex string (64 for SHA-256)
$id->binary; // 20-byte raw binary (32 for SHA-256)
$id->algo; // 'sha1' or 'sha256'
$id->isSha256();
$id->isZero();
// Comparison
$id->equals($otherId);
Το hash υπολογίζεται πάνω στα μη συμπιεσμένα δεδομένα: sha1("{type} {size} ή SHA-256 των ίδιων byte. Τα fromHex() και fromBinary() επιλέγουν τον αλγόριθμο με βάση το μήκος.
Απαρίθμηση ObjectType
use Pitmaster\Object\ObjectType;
ObjectType::Blob; // 'blob'
ObjectType::Tree; // 'tree'
ObjectType::Commit; // 'commit'
ObjectType::Tag; // 'tag'
Μορφή loose αντικειμένων
Τα loose αντικείμενα αποθηκεύονται ως μεμονωμένα αρχεία στο .git/objects/, οργανωμένα με βάση τους δύο πρώτους δεκαεξαδικούς χαρακτήρες του hash τους.
.git/objects/a1/b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2
Το περιεχόμενο του αρχείου είναι συμπιεσμένο με zlib. Το μη συμπιεσμένο περιεχόμενο είναι:
<type> <decimal size>\0<raw content>
Για παράδειγμα, ένα blob που περιέχει "Hello" αποθηκεύεται ως εξής:
zlib_encode("blob 5\0Hello")
Ανάγνωση loose αντικειμένων
use Pitmaster\Storage\LooseObjectStore;
$store = new LooseObjectStore('/path/to/.git/objects');
$object = $store->read($objectId);
Το LooseObjectStore:
- Υπολογίζει τη διαδρομή του αρχείου από το hash
- Διαβάζει και αποσυμπιέζει το αρχείο με zlib
- Αναλύει την κεφαλίδα για να εξαγάγει τον τύπο και το μέγεθος
- Αναθέτει την επεξεργασία στον κατάλληλο αναλυτή (
Blob,Tree,Commit,Tag)
Εγγραφή loose αντικειμένων
$store->write($object);
Η διαδικασία εγγραφής:
- Σειριοποίηση:
"{type} {size}\0{content}" - Χρήση του
idτου αντικειμένου, που υπολογίζεται κατά τη δημιουργία του (οι αναγνώσεις επαληθεύουν το hash) - Συμπίεση:
zlib_encode(serialized) - Εγγραφή στο
.git/objects/{hash[0..1]}/{rest of hash} - Ατομική εγγραφή (εγγραφή σε προσωρινό αρχείο και έπειτα μετονομασία)
ObjectSerializer
Το ObjectSerializer χειρίζεται την κωδικοποίηση και αποκωδικοποίηση της μορφής wire των αντικειμένων.
use Pitmaster\Storage\ObjectSerializer;
// Encode
$raw = ObjectSerializer::encodeRaw($object);
// "blob 5\0Hello"
$compressed = ObjectSerializer::encode($object);
// zlib-compressed form, as stored in a loose object file
// Decode
$object = ObjectSerializer::decodeRaw($raw);
$object = ObjectSerializer::decode($compressed, $expectedHex); // throws CorruptObjectException on a hash mismatch
ObjectDatabase
Το ObjectDatabase είναι ένας σύνθετος χώρος αποθήκευσης που αναζητά πρώτα loose αντικείμενα και έπειτα αρχεία pack.
use Pitmaster\Storage\ObjectDatabase;
$db = new ObjectDatabase('/path/to/.git/objects');
$object = $db->read($objectId); // Search loose, then packs
$db->write($object); // Always writes to loose
$exists = $db->exists($objectId); // Check loose, then packs
$all = $db->listAll(); // All hashes from loose + packs
Η εγγραφή απευθείας στον loose χώρο αποθήκευσης είναι σκόπιμη. Αφήστε το git gc να δημιουργήσει ξανά τα αρχεία pack.
Υποστήριξη SHA-256
Το Pitmaster αφαιρεί την εξάρτηση από τον αλγόριθμο hash μέσω του ObjectId. Το SHA-1 είναι η προεπιλογή. Το SHA-256 επιλέγεται για κάθε αποθετήριο κατά τη δημιουργία του:
$repo = Pitmaster::init('/path/to/project', 'sha256');
./vendor/bin/pitmaster init --object-format=sha256
Αυτό γράφει το extensions.objectformat = sha256 στη ρύθμιση. Τα αποθετήρια που ανοίγονται αργότερα διαβάζουν τη μορφή από αυτήν την εγγραφή και, στη συνέχεια, κάθε λειτουργία αντικειμένου, tree, ευρετηρίου και αναφοράς χρησιμοποιεί δεκαεξαδικές συμβολοσειρές 64 χαρακτήρων και δυαδικά hash 32 byte. Δεν υπάρχει καθολικός διακόπτης ή σταθερά για τον αλγόριθμο.
Οι κλάσεις αντικειμένων δέχονται τον αλγόριθμο ως όρισμα όταν δημιουργείτε αντικείμενα οι ίδιοι: Blob::fromContent($content, 'sha256'), Tree::fromEntries($entries, 'sha256'), ObjectId::compute($type, $content, 'sha256'). Το Pitmaster::clone() δημιουργεί αποθετήρια SHA-1.
Δυαδική μορφή εγγραφής tree
Μέσα στο περιεχόμενο ενός αντικειμένου tree, κάθε εγγραφή κωδικοποιείται ως εξής:
<octal mode as ASCII> <space> <filename> \0 <binary hash: 20 bytes SHA-1, 32 bytes SHA-256>
Σημείωση: το hash είναι ακατέργαστο δυαδικό, όχι δεκαεξαδικό, σε αντίθεση με τα δεκαεξαδικά hash στα αντικείμενα commit και tag.
Οι εγγραφές ταξινομούνται συγκρίνοντας τα ονόματα σαν οι κατάλογοι να έχουν στο τέλος /. Αυτή η σειρά ταξινόμησης πρέπει να είναι ακριβής, ώστε το hash του tree να ταιριάζει με τον υπολογισμό του git.