Gravação em repositórios
Pitmaster grava repositórios git usando os mesmos formatos binários do binário canônico git. Os objetos são gravados no armazenamento de objetos soltos (compactados com zlib), o índice é gravado no formato v2 (ou v3 quando as entradas precisam de flags estendidas; um índice existente mantém sua versão), e as refs são gravadas como arquivos soltos. Os dados gravados podem ser verificados com git fsck --strict.
Inicializando um repositório
use Pitmaster\Pitmaster;
$repo = Pitmaster::init('/path/to/new-project');
Isso cria:
.git/objects/(armazenamento de objetos vazio).git/refs/heads/e.git/refs/tags/(diretórios de refs vazios).git/HEADcontendoref: refs/heads/main.git/hooks/,.git/info/excludee.git/description.git/configcom as configurações padrão
Passe 'sha256' como segundo argumento para criar um repositório SHA-256: Pitmaster::init($path, 'sha256').
A configuração padrão não define core.logallrefupdates, então Pitmaster não registra entradas no reflog de um repositório que criou até você definir essa chave como true (#8). Os repositórios criados por git init já a definem.
Preparando arquivos
add
$repo->add('src/Feature.php');
$repo->add('src/Feature.php', 'tests/FeatureTest.php', 'README.md');
Cada argumento é um caminho de arquivo relativo à raiz da árvore de trabalho. Diretórios e padrões glob não são expandidos, e um arquivo ausente gera RuntimeException. Para cada arquivo, Pitmaster:
- Lê o conteúdo do arquivo na árvore de trabalho
- Cria um objeto
Blobe calcula seu hash - Grava o blob em
.git/objects/(compactado com zlib) - Atualiza a entrada do índice para esse caminho com o novo hash e os metadados do arquivo
remove
$repo->remove('old-file.txt');
$repo->remove('-r', 'src');
$repo->remove('--cached', 'old-file.txt');
Remove os caminhos rastreados do índice e da árvore de trabalho. Use --cached para remover apenas do índice, ou -r / --recursive para diretórios rastreados.
mv
$repo->mv('old-name.php', 'new-name.php');
Renomeia o arquivo na árvore de trabalho, remove a entrada antiga do índice e adiciona a nova.
Criando commits
$commitId = $repo->commit('Fix null pointer bug');
echo $commitId->hex;
O processo de commit:
- Criar árvore a partir do índice: a lista simples de entradas do índice é organizada em uma hierarquia de árvores (diretórios se tornam objetos de árvore, arquivos são referências a blobs). Cada árvore é gravada no armazenamento de objetos.
- Determinar o pai: o commit HEAD atual se torna o pai. Commits raiz (o primeiro commit em um repositório) não têm pais.
- Determinar o autor: obtido das variáveis de ambiente
PITMASTER_AUTHOR_*eGIT_AUTHOR_*, depois das constantesPITMASTER_AUTHOR_NAME/PITMASTER_AUTHOR_EMAIL, depois de.git/config(user.name/user.email) e, por fim, dePitmaster <pitmaster@localhost>. O committer é determinado da mesma forma. - Gravar o objeto de commit: o conteúdo do commit (
tree,parent,author,committer, mensagem) é serializado, tem seu hash calculado e é gravado. - Atualizar o HEAD: a ref da branch para a qual o HEAD aponta é atualizada com o novo hash do commit.
Os hooks são executados antes e depois dessas etapas (pre-commit, prepare-commit-msg, commit-msg, post-commit), a menos que o handle tenha sido aberto com ['hooks' => false]. commit() gera um erro quando a mensagem está ausente, quando a árvore não mudou em relação ao HEAD ou quando o índice contém caminhos não mesclados.
Autor personalizado
$author = 'Jane Smith <jane@example.com> 1700000000 +0000';
$commitId = $repo->commit('Add feature', $author);
Verificação com fsck de git
Os testes de integração de Pitmaster verificam os objetos gravados em relação a git fsck --strict, e você pode fazer a mesma verificação manualmente:
cd /path/to/repo
git fsck --strict
Se Pitmaster gravar um objeto inválido, git fsck informará o erro imediatamente.
Gerenciando branches
Criar uma branch
// Branch from HEAD
$repo->createBranch('feature/new-thing');
// Branch from a specific commit
$repo->createBranch('hotfix', $repo->resolve('v1.0.0'));
Cria um arquivo ref em .git/refs/heads/<name> contendo o hash do commit de destino.
Excluir uma branch
$repo->deleteBranch('feature/old');
Alternar branches
$repo->checkout('feature/login');
Atualiza o HEAD para uma ref simbólica que aponta para a branch, redefine o índice para corresponder à árvore do ponto mais recente da branch e atualiza a árvore de trabalho.
Alterações locais nos caminhos que diferem entre as duas árvores interrompem o checkout com um RuntimeException. Alterações não commitadas em todos os outros arquivos rastreados são substituídas pelo conteúdo da branch, enquanto git as manteria (#2). Faça commit ou guarde-as antes de alternar.
Verificar se foi mesclada
if ($repo->isBranchMerged('feature/done')) {
$repo->deleteBranch('feature/done');
}
Gerenciando tags
Tag leve
$repo->createLightweightTag('v1.0.0'); // At HEAD
$repo->createLightweightTag('v0.9.0', $repo->resolve('HEAD~5'));
Uma tag leve é apenas uma ref que aponta diretamente para um commit.
Tag anotada
$tagId = $repo->createTag('v2.0.0', 'Release version 2.0.0');
Uma tag anotada cria um objeto de tag (com criador da tag, mensagem e destino) e uma ref que aponta para ele. O criador da tag é a identidade atual do committer, determinada da mesma forma que o autor do commit.
Criador de tag personalizado
$tagId = $repo->createTag(
'v2.0.0',
'Release version 2.0.0',
$repo->resolve('HEAD'),
'Jane Smith <jane@example.com> 1700000000 +0000',
);
Atualizando refs diretamente
$repo->updateRef('refs/heads/main', $commitId);
$repo->updateRef('refs/tags/latest', $commitId);
Gravando objetos diretamente
Para casos de uso avançados, você pode gravar objetos sem passar pela camada de alto nível API.
Gravar um blob
use Pitmaster\Object\Blob;
$blob = Blob::fromContent('Hello, world!');
$id = $repo->writeObject($blob);
echo $id->hex; // SHA-1 of "blob 13\0Hello, world!"
Em um repositório SHA-256, percorra o algoritmo: Blob::fromContent('Hello, world!', 'sha256').
Escrever uma árvore
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() grava as entradas na ordem fornecida e as strings de modo como fornecidas. Passe-as na ordem de árvore de git (por nome, comparando diretórios como se o nome terminasse em /) e grave os modos de diretório como 40000: git armazena os modos de árvore sem o zero à esquerda, e git fsck --strict rejeita 040000.
Escrever um commit
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);
Redefinir
// 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');
Restaurar
// 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);
Aplicar cherry-pick e reverter
// Apply a commit on the current branch
$newId = $repo->cherryPick('feature~1');
// Undo a commit
$revertId = $repo->revert('HEAD~2');
Ambos aceitam qualquer revisão que resolve() aceite. Hashes completos funcionam; hashes abreviados não.