Zapisywanie w repozytoriach
Pitmaster zapisuje repozytoria git, używając tych samych formatów binarnych co kanoniczny plik binarny git. Obiekty są zapisywane w magazynie luźnych obiektów (kompresowane zlib), indeks jest zapisywany w formacie v2 (lub v3, gdy wpisy wymagają rozszerzonych flag; istniejący indeks zachowuje swoją wersję), a referencje są zapisywane jako luźne pliki. Zapisane dane można zweryfikować za pomocą git fsck --strict.
Inicjalizowanie repozytorium
use Pitmaster\Pitmaster;
$repo = Pitmaster::init('/path/to/new-project');
To tworzy:
.git/objects/(pusty magazyn obiektów).git/refs/heads/i.git/refs/tags/(puste katalogi referencji).git/HEADzawierającyref: refs/heads/main.git/hooks/,.git/info/excludei.git/description.git/configz ustawieniami domyślnymi
Przekaż 'sha256' jako drugi argument, aby utworzyć repozytorium SHA-256: Pitmaster::init($path, 'sha256').
Domyślna konfiguracja nie ustawia core.logallrefupdates, więc Pitmaster nie zapisuje żadnych wpisów refloga w utworzonym repozytorium, dopóki ten klucz nie zostanie ustawiony na true (#8). Repozytoria utworzone przez git init mają już tę wartość ustawioną.
Dodawanie plików do obszaru stagingu
add
$repo->add('src/Feature.php');
$repo->add('src/Feature.php', 'tests/FeatureTest.php', 'README.md');
Każdy argument to ścieżka pliku względem katalogu głównego drzewa roboczego. Katalogi i wzorce glob nie są rozwijane, a brakujący plik powoduje zgłoszenie RuntimeException. Dla każdego pliku Pitmaster:
- Odczytuje zawartość pliku z drzewa roboczego
- Tworzy obiekt
Blobi oblicza jego hash - Zapisuje blob w
.git/objects/(kompresowany zlib) - Aktualizuje wpis indeksu dla tej ścieżki, zapisując nowy hash i metadane pliku
remove
$repo->remove('old-file.txt');
$repo->remove('-r', 'src');
$repo->remove('--cached', 'old-file.txt');
Usuwa śledzone ścieżki z indeksu i drzewa roboczego. Użyj --cached, aby usunąć tylko z indeksu, lub -r / --recursive dla śledzonych katalogów.
mv
$repo->mv('old-name.php', 'new-name.php');
Zmienia nazwę pliku w drzewie roboczym, usuwa stary wpis indeksu i dodaje nowy.
Tworzenie commitów
$commitId = $repo->commit('Fix null pointer bug');
echo $commitId->hex;
Proces tworzenia commita:
- Tworzenie drzewa z indeksu: płaska lista wpisów indeksu jest organizowana w hierarchię drzewa (katalogi stają się obiektami drzewa, a pliki referencjami do blobów). Każde drzewo jest zapisywane w magazynie obiektów.
- Ustalanie rodzica: bieżący commit HEAD staje się rodzicem. Commity główne (pierwszy commit w repozytorium) nie mają rodziców.
- Ustalanie autora: autor jest pobierany ze zmiennych środowiskowych
PITMASTER_AUTHOR_*iGIT_AUTHOR_*, następnie ze stałychPITMASTER_AUTHOR_NAME/PITMASTER_AUTHOR_EMAIL, potem z.git/config(user.name/user.email), a na końcu zPitmaster <pitmaster@localhost>. Tożsamość commitera jest ustalana w ten sam sposób. - Zapisywanie obiektu commit: zawartość commita (
tree,parent,author,committer, komunikat) jest serializowana, hashowana i zapisywana. - Aktualizacja HEAD: referencja gałęzi wskazywana przez HEAD jest aktualizowana do nowego hasha commita.
Hooki uruchamiają się przed tymi krokami i po nich (pre-commit, prepare-commit-msg, commit-msg, post-commit), chyba że uchwyt otwarto z ['hooks' => false]. commit() zgłasza błąd, gdy brakuje komunikatu, drzewo nie zmieniło się względem HEAD lub indeks zawiera ścieżki z nierozwiązanymi scaleniami.
Niestandardowy autor
$author = 'Jane Smith <jane@example.com> 1700000000 +0000';
$commitId = $repo->commit('Add feature', $author);
Weryfikacja za pomocą git fsck
Testy integracyjne Pitmaster sprawdzają zapisane obiekty za pomocą git fsck --strict; możesz też zweryfikować je ręcznie:
cd /path/to/repo
git fsck --strict
Jeśli Pitmaster zapisze nieprawidłowy obiekt, git fsck natychmiast zgłosi błąd.
Zarządzanie gałęziami
Tworzenie gałęzi
// Branch from HEAD
$repo->createBranch('feature/new-thing');
// Branch from a specific commit
$repo->createBranch('hotfix', $repo->resolve('v1.0.0'));
Tworzy plik referencji w .git/refs/heads/<name> zawierający hash docelowego commita.
Usuwanie gałęzi
$repo->deleteBranch('feature/old');
Przełączanie gałęzi
$repo->checkout('feature/login');
Aktualizuje HEAD do symbolicznej referencji wskazującej gałąź, resetuje indeks tak, aby odpowiadał drzewu na końcu gałęzi, i aktualizuje drzewo robocze.
Lokalne zmiany w ścieżkach różniących się między tymi drzewami przerywają checkout z RuntimeException. Niezatwierdzone zmiany we wszystkich pozostałych śledzonych plikach są zastępowane zawartością gałęzi, zgodnie z zachowaniem git (#2). Zatwierdź je lub odłóż przed przełączeniem.
Sprawdzanie, czy gałąź została scalona
if ($repo->isBranchMerged('feature/done')) {
$repo->deleteBranch('feature/done');
}
Zarządzanie tagami
Tag lekki
$repo->createLightweightTag('v1.0.0'); // At HEAD
$repo->createLightweightTag('v0.9.0', $repo->resolve('HEAD~5'));
Tag lekki to po prostu referencja wskazująca bezpośrednio na commit.
Tag z adnotacją
$tagId = $repo->createTag('v2.0.0', 'Release version 2.0.0');
Tag z adnotacją tworzy obiekt tagu (z autorem tagu, komunikatem i celem) oraz referencję, która na niego wskazuje. Autor tagu to bieżąca tożsamość commitera, ustalana tak samo jak autor commita.
Niestandardowy autor tagu
$tagId = $repo->createTag(
'v2.0.0',
'Release version 2.0.0',
$repo->resolve('HEAD'),
'Jane Smith <jane@example.com> 1700000000 +0000',
);
Bezpośrednia aktualizacja referencji
$repo->updateRef('refs/heads/main', $commitId);
$repo->updateRef('refs/tags/latest', $commitId);
Bezpośrednie zapisywanie obiektów
W zaawansowanych zastosowaniach można zapisywać obiekty bez użycia interfejsu wysokiego poziomu API.
Zapis blobu
use Pitmaster\Object\Blob;
$blob = Blob::fromContent('Hello, world!');
$id = $repo->writeObject($blob);
echo $id->hex; // SHA-1 of "blob 13\0Hello, world!"
W repozytorium SHA-256 przekaż algorytm: Blob::fromContent('Hello, world!', 'sha256').
Zapis drzewa
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() zapisuje wpisy w podanej kolejności i używa podanych ciągów trybu. Przekaż je w kolejności drzewa git (według nazwy, przy czym katalogi są porównywane tak, jakby ich nazwa kończyła się na /) i zapisuj tryby katalogów jako 40000: git przechowuje tryby drzew bez początkowego zera, a git fsck --strict odrzuca 040000.
Zapis commita
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);
Reset
// 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');
Przywracanie
// 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 i cofanie
// Apply a commit on the current branch
$newId = $repo->cherryPick('feature~1');
// Undo a commit
$revertId = $repo->revert('HEAD~2');
Obie operacje przyjmują dowolną rewizję akceptowaną przez resolve(). Pełne hashe działają, skrócone nie.