Запись в репозитории
Pitmaster записывает данные в репозитории git, используя те же двоичные форматы, что и эталонный двоичный файл git. Объекты записываются в хранилище отдельных объектов (сжатых zlib), индекс записывается в формате v2 (или v3, если записям нужны расширенные флаги; существующий индекс сохраняет свою версию), а ссылки записываются как отдельные файлы. Записанные данные можно проверить с помощью git fsck --strict.
Инициализация репозитория
use Pitmaster\Pitmaster;
$repo = Pitmaster::init('/path/to/new-project');
Будут созданы:
.git/objects/(пустое хранилище объектов).git/refs/heads/и.git/refs/tags/(пустые каталоги ссылок).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). В репозиториях, созданных с помощью git init, этот параметр уже задан.
Добавление файлов в индекс
добавить
$repo->add('src/Feature.php');
$repo->add('src/Feature.php', 'tests/FeatureTest.php', 'README.md');
Каждый аргумент — это путь к файлу относительно корня рабочего дерева. Каталоги и шаблоны glob не раскрываются, а при отсутствии файла возникает исключение RuntimeException. Для каждого файла Pitmaster выполняет следующие действия:
- Читает содержимое файла из рабочего дерева
- Создает объект
Blobи вычисляет его хеш - Записывает blob-объект в
.git/objects/(сжатый zlib) - Обновляет запись этого пути в индексе новым хешем и метаданными файла
удалить
$repo->remove('old-file.txt');
$repo->remove('-r', 'src');
$repo->remove('--cached', 'old-file.txt');
Удаляет отслеживаемые пути из индекса и рабочего дерева. Используйте --cached, чтобы удалить только из индекса, или -r / --recursive для отслеживаемых каталогов.
переместить
$repo->mv('old-name.php', 'new-name.php');
Переименовывает файл в рабочем дереве, удаляет старую запись из индекса и добавляет новую.
Создание коммитов
$commitId = $repo->commit('Fix null pointer bug');
echo $commitId->hex;
Процесс создания коммита:
- Создание дерева по индексу: плоский список записей индекса преобразуется в иерархию деревьев (каталоги становятся объектами tree, файлы — ссылками на blob-объекты). Каждое дерево записывается в хранилище объектов.
- Определение родителя: текущий коммит HEAD становится родительским. У корневых коммитов (первых коммитов в репозитории) родителей нет.
- Определение автора: данные берутся из переменных окружения
PITMASTER_AUTHOR_*иGIT_AUTHOR_*, затем из константPITMASTER_AUTHOR_NAME/PITMASTER_AUTHOR_EMAIL, затем из.git/config(user.name/user.email), затем изPitmaster <pitmaster@localhost>. Данные коммитера определяются таким же образом. - Запись объекта коммита: содержимое коммита (
tree,parent,author,committer, сообщение) сериализуется, хешируется и записывается. - Обновление HEAD: ссылка ветви, на которую указывает HEAD, обновляется новым хешем коммита.
На этих этапах запускаются хуки (pre-commit, prepare-commit-msg, commit-msg, post-commit), кроме случаев, когда дескриптор открыт с ['hooks' => false]. commit() вызывает исключение, если сообщение отсутствует, дерево не изменилось относительно HEAD или в индексе есть неслитые пути.
Пользовательский автор
$author = 'Jane Smith <jane@example.com> 1700000000 +0000';
$commitId = $repo->commit('Add feature', $author);
Проверка с помощью fsck в git
Интеграционные проверки Pitmaster сверяют записанные объекты с git fsck --strict. Вы можете выполнить такую проверку вручную:
cd /path/to/repo
git fsck --strict
Если Pitmaster записывает некорректный объект, git fsck сразу сообщит об ошибке.
Управление ветвями
Создание ветви
// Branch from HEAD
$repo->createBranch('feature/new-thing');
// Branch from a specific commit
$repo->createBranch('hotfix', $repo->resolve('v1.0.0'));
Создает файл ссылки по пути .git/refs/heads/<name>, содержащий хеш целевого коммита.
Удаление ветви
$repo->deleteBranch('feature/old');
Переключение ветвей
$repo->checkout('feature/login');
Обновляет HEAD, чтобы он указывал на символическую ссылку ветви, сбрасывает индекс в соответствии с деревом вершины ветви и обновляет рабочее дерево.
Локальные изменения по путям, которые различаются в двух деревьях, останавливают извлечение с ошибкой RuntimeException. Незакоммиченные изменения в остальных отслеживаемых файлах заменяются содержимым ветви; git сохранил бы их (#2). Перед переключением создайте коммит или сохраните изменения в хранилище.
Проверка, выполнено ли слияние
if ($repo->isBranchMerged('feature/done')) {
$repo->deleteBranch('feature/done');
}
Управление метками
Легковесная метка
$repo->createLightweightTag('v1.0.0'); // At HEAD
$repo->createLightweightTag('v0.9.0', $repo->resolve('HEAD~5'));
Легковесная метка — это просто ссылка, напрямую указывающая на коммит.
Аннотированная метка
$tagId = $repo->createTag('v2.0.0', 'Release version 2.0.0');
Аннотированная метка создает объект метки (с автором метки, сообщением и целевым объектом) и ссылку на него. Автором метки становится текущий коммитер; его данные определяются так же, как для автора коммита.
Пользовательский автор метки
$tagId = $repo->createTag(
'v2.0.0',
'Release version 2.0.0',
$repo->resolve('HEAD'),
'Jane Smith <jane@example.com> 1700000000 +0000',
);
Непосредственное обновление ссылок
$repo->updateRef('refs/heads/main', $commitId);
$repo->updateRef('refs/tags/latest', $commitId);
Непосредственная запись объектов
В более сложных сценариях можно записывать объекты, не используя высокоуровневый API.
Запись blob-объекта
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').
Запись дерева
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() записывает записи в заданном порядке и строки режима как есть. Передавайте их в порядке дерева git (по имени; каталоги сравниваются так, как если бы их имя оканчивалось на /), а режимы каталогов записывайте как 40000: git хранит режимы дерева без ведущего нуля, а git fsck --strict отклоняет 040000.
Запись коммита
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);
Сброс
// 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');
Восстановление
// 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);
Выборочное применение коммита и отмена
// Apply a commit on the current branch
$newId = $repo->cherryPick('feature~1');
// Undo a commit
$revertId = $repo->revert('HEAD~2');
Оба метода принимают любую ревизию, которую принимает resolve(). Полные хеши подходят, сокращённые — нет.