リポジトリへの書き込み
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'を2番目の引数として渡すと、SHA-256リポジトリを作成できます:Pitmaster::init($path, 'sha256')。
デフォルト設定ではcore.logallrefupdatesが設定されないため、Pitmasterが作成したリポジトリでは、そのキーをtrueに設定するまで reflog エントリが記録されません(#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;
コミット処理の流れ:
- インデックスからツリーを構築:インデックス項目のフラットな一覧をツリー階層にまとめます(ディレクトリはツリーオブジェクトになり、ファイルは 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 が指すブランチ参照を、新しいコミットハッシュに更新します。
ハンドルを['hooks' => false]で開いていない限り、これらの手順の前後でフックが実行されます(pre-commit、prepare-commit-msg、commit-msg、post-commit)。メッセージがない場合、HEAD からツリーが変更されていない場合、またはインデックスに未マージのパスがある場合、commit()はエラーをスローします。
カスタム作成者
$author = 'Jane Smith <jane@example.com> 1700000000 +0000';
$commitId = $repo->commit('Add feature', $author);
git fsck による検証
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 をブランチを指すシンボリック参照に更新し、インデックスをブランチ先端のツリーに合わせてリセットして、作業ツリーを更新します。
2つのツリー間で異なるパスにローカル変更があると、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);
Cherry-pick と revert
// Apply a commit on the current branch
$newId = $repo->cherryPick('feature~1');
// Undo a commit
$revertId = $repo->revert('HEAD~2');
どちらもresolve()が受け付ける任意のリビジョンを指定できます。完全なハッシュは使えますが、省略形のハッシュは使えません。