gitkit について
gitkit は、他のスキルや直接の利用者向けに、Git worktree、ベースブランチ、同期の規則をまとめる基盤スキルです。中核の操作には Git を使い、worktree の準備にはリポジトリが宣言したコマンドを実行します。
主な機能
- 01worktree の作成、引き継ぎ、検索、削除
- 02ベースブランチの特定
- 03リベースまたはマージによる同期
- 04リポジトリ定義の worktree セットアップ
- 05ブランチの整理と失われた作業の復旧
- 06ブランチチェーンの作成と再構成
活用シーン
- 01Issue ブランチ用の worktree を作成
- 02フィーチャーブランチをベースに同期
- 03worktree の準備または削除
- 04リセットやリベース後の作業を復旧
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ mimukit/ skills --skill gitkitインストール後、Claude Code で/gitkitを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
clean.mdprepare.mdrescue.mdstacks.md詳細
翻訳後に原文が更新されています。最新の内容は原文をご確認ください。
gitkit
ツールチェーン内のほかのすべてのスキルが再確認している Git の情報を一元化します。worktree の保存場所と名前、作成・採用・削除の方法、ベースブランチ、rebase と merge の使い分けです。
ここでのコアは凝ったものではありません。すべてネイティブの git worktree を使い、ベンダーの CLI も GUI への依存もなく、記録された状態は Git 自体のもの以外にありません。それが狙いです。デスクトップ開発ツールを使うノートパソコンでも、ヘッドレスの Linux マシンでも、同じコマンドを実行するため、同期が必要な別のコードパスも、考慮すべき「機能制限モード」もありません。
このスキルのスタックブランチの機能はコアの範囲外にあり、そのことを明示しています。スタックブランチでは GitHub の gh stack 拡張機能を使うため、この機能だけは gh と GitHub のリモートが必要です。失敗する代わりに通常の Git にフォールバックし、このファイルのほかの機能には依存しないため、gh がないマシンでも上記の機能はそのまま使えます。
gitkit はプリミティブスキルです。ほとんどの場合、人が名前を指定して呼び出すのではなく、別のスキルから呼び出されます。
起動する場面
- worktree。 「このブランチ用の worktree を作って」「#42 用の worktree を作って」「
issue-42-…の worktree はどこ?」「この worktree を削除して」「自分の worktree を一覧表示して」。 - ベース参照。 「ここでのベースブランチは?」「ベースとの差分を見せて」。
- 同期。 「ここでは rebase と merge のどちらにすべき?」「このブランチを main と同期して」「このブランチは遅れているので最新にして」「この PR を最新のベースに対してレビュー可能な状態にして」。
- クリーンアップ。 「マージ済みの worktree を片付けて」「削除できるブランチは?」「このリポジトリには使われていないブランチがたくさんある」。
- 復旧。 「コミットを失くした」「rebase で作業内容が消えた」「間違ったブランチを reset した」「この stash の内容は?」。
- スタック。 「これを #43 の上に積んで」「#43 のブランチを土台にして #44 を始めて」「このチェーンを restack して」「スタックを見せて」。
- 別のスキルから呼び出される場合。 worktree、ベース参照、ブランチ名、同期の判断が必要なスキルは、ここに処理を委ね、返された情報を使います。
このスキルの対象外: コミット、PR の作成、差分のレビュー、コードの評価。gitkit が答えるのは「どこで、どうやって」だけで、「変更内容をどうすべきか」には答えません。
モード
| モード | 内容 | 詳細 |
|---|---|---|
worktree | worktree の作成、採用、検索、一覧表示、削除 | 以下 |
sync | フィーチャーブランチを対応するリモートの最新状態にし、最新のベースに合わせて、リース付きで強制プッシュする | 以下 |
clean | マージ済みの worktree とブランチを、確認を一つずつ行いながら一掃する | clean.md |
rescue | 失われたように見える作業を探し、新しいブランチに復元する | rescue.md |
stack | gh stack を使ってブランチのチェーンを作成し、restack する | stacks.md、およびスタックブランチ |
worktree と sync の説明がここに含まれているのは、ほとんどの実行や呼び出し元のスキルで必要になるためです。ほかの三つは、それぞれのモードが起動したときにだけ読み込みます。
依頼内容からモードが明確でない場合は、動詞からモードを選び、選んだモードを伝えてください。 マージ後の「これを片付けて」は clean、ベースより遅れているブランチでの「これを片付けて」は sync です。最初の行でモードを示すことで、処理の実行前に人が誤った選択を修正できます。
規約
パス
$WORKTREE_ROOT/<repo-basename>/<local-branch-name>
$WORKTREE_ROOT のデフォルトは ~/worktrees です。環境変数が設定されている場合は、その値を使用します。<repo-basename> はメインのチェックアウトのディレクトリ名です。
ブランチ名のスラッシュはハイフンに置き換えます。 ブランチ mimukit/6-marketing-site のディレクトリは、入れ子になった mimukit/ の親ディレクトリではなく、mimukit-6-marketing-site になります。これにより、リポジトリごとにフラットな階層が保たれるため、ls "$WORKTREE_ROOT/<repo>" で、ツリーをたどらずにすべてを一覧できます。その代わり、ディレクトリ名からブランチ名を元に戻せなくなりますが、パスを読むのではなく、ブランチのワークツリーは常に git で検索するため、問題にはなりません。まれに名前が衝突する場合(ブランチ a/b と a-b がどちらも a-b になる場合)、既存のパスがあるため git worktree add は失敗します。報告して、人が名前を選べるようにしてください。黙って接尾辞を追加してはいけません。
ワークツリーはリポジトリの外部に置き、内部の .worktrees/ ディレクトリには決して置きません。リポジトリ内のワークツリーは、Docker のビルドコンテキスト、バインドマウント、ファイルウォッチャー、テストの glob に取り込まれます。これらの問題はいずれも、原因から離れた場所で発生します。
不変条件:ブランチごとにワークツリーは1つ
ブランチに対応するワークツリーは最大1つで、ディレクトリ名はブランチ名と同じです。 ワークツリーはワークフローの段階ではなく、ブランチに紐付いています。そのため、機能を実装した後にそのプルリクエストをレビューする場合も、同じブランチなので同じワークツリーを使います。
これはスタイルの好みではありません。Git によって強制されます。
$ git worktree add ../wt-b feature-x
fatal: 'feature-x' is already used by worktree at '.../wt-a'
ワークツリーに段階ごとの名前(review-…、qa-…)を付ける方式は、2つの段階が同じブランチを扱った時点で必ず失敗します。これは例外ではなく、通常のケースです。
その結果:
- 検索は常にブランチ名で行います。 ディレクトリのパスを読んだり推測したりせず、必ず git を使います。これにより、前述のフラット化に支障がなくなり、以前のルートにあるワークツリーも変更せず使い続けられます。
- ワークツリーの「種類」は自動的に引き継がれます。 ブランチ名にすでに含まれているためです。
issue-42-auth-capability-moduleという名前なのは、作業開始時のブランチ名がその名前だったからです。
ブランチ名の付け方
- トラッカーの Issue から始める作業には
issue-<n>-<slug>を使います。Issue のタイトルからスラッグを作成します。type(scope): summaryという一般的な形式の場合は、接頭辞を取り除きます。残りをケバブケースにし、単語の途中で切らないようにおよそ50文字までにして、末尾のハイフンを削除します。スラッグが空の場合は、issue-<n>のみを使います。番号によって一意性が保証されるため、同名の場合の調整は不要です。 - ベースブランチに直接パッチを適用する緊急修正には
hotfix-<slug>を使います。症状をもとにスラッグを作成し、ケバブケースにして、およそ40文字までにします。Hotfix はトラッカーの Issue ではなく症状を起点にするため、番号は付けません。Issue に紐付く場合も、ブランチ名はこの形式のままにし、プルリクエストにCloses #<n>のリンクを記載します。既存のブランチと名前が衝突した場合は、カウンターではなく、区別できる短い単語を追加します。区切り文字にはハイフンを使います。hotfix-<slug>とし、hotfix/<slug>は使わないでください。 スラッシュを使うと、git は ref 名前空間でhotfixをディレクトリとして扱います。そのため、hotfix-restore-sessionとhotfix/restore-sessionは共存できません。また、ブランチ名から種類を読み取るすべてのスキル(commitkit、prkit)は、ハイフン形式のみを認識します。 pr-<n>-<slug>は、ローカルブランチがまだ存在せず、取得した head を保持するために新たに作成する必要があるフォークのプルリクエストにのみ使います。head ブランチ名からスラッグを作成し、ケバブケースにして、およそ40文字までにします。- それ以外の場合は、人またはリポジトリの規約が指定するブランチ名を使います。gitkit は名前を変更しません。
同じリポジトリ内のプルリクエストには、すでにローカルのブランチ名があります。それを使ってください。既存のブランチに pr-* という名前を新たに付けると、前述の「1つのブランチに2つのワークツリー」の問題が起きます。
worktree
4つとも git 標準の機能で、すべて冪等です。そのため、同じものを2回実行するのは通常のことであり、エラーになったり作業内容が失われたりしてはいけません。
ブランチのワークツリーを検索する
ほかのすべての基盤となる検索プリミティブです。
git -C "$REPO" worktree list --porcelain
各レコードには worktree <path> の行が含まれ、ブランチがチェックアウトされている場合は branch refs/heads/<name> の行も含まれます。ブランチを照合してパスを取得します。porcelain の形式が変わった場合は、推測せず git worktree list --help を確認してください。ただし、worktree と branch の組み合わせは安定しているものとして扱ってください。porcelain 出力はそのためのものです。
作成または引き継ぎ
必ず最初に検索します。そのブランチのワークツリーがすでに存在する場合は、それを引き継ぎます。パスを報告して終了してください。 二つ目を作成してはいけません。エラーにもしてはいけません。
存在しない場合:
WT="${WORKTREE_ROOT:-$HOME/worktrees}/$(basename "$REPO")/$(printf '%s' "$BRANCH" | tr / -)" # slashes flatten to dashes
git -C "$REPO" fetch origin --prune
git -C "$REPO" worktree add -b "$BRANCH" "$WT" "$BASE"
- 常に最初にフェッチします。古いベースからブランチを作成すると、気づかないまま進み、後になって競合として表面化します。
$BASEはベース参照から取得します。ハードコードされたorigin/mainや、兄弟のフィーチャーブランチは使いません。例外はスタックレイヤーだけです。そのベースは下位のブランチであり、呼び出し元によって明示的に指定されています。- ブランチがすでにローカルに存在する場合(フェッチ済みの同一リポジトリ内の PR や、以前に作成したブランチなど)は、
-bを省略します。git worktree add <path> <branch>がそのブランチをチェックアウトします。 - フォークからのプルリクエストでは、まず
git fetch origin "pull/<n>/head:pr-<n>-<slug>"でヘッドをローカルブランチにフェッチし、そのブランチに対して-bを付けずにワークツリーを追加します。
その後、gitkit が作成したものを記録します。後のセッションでは、自分のワークツリーと他の人のワークツリーを見分ける方法がほかにないためです。
touch "$(git -C "$WT" rev-parse --absolute-git-dir)/gitkit-created" # gitkit made this worktree
git -C "$REPO" config "branch.$BRANCH.gitkitCreated" true # only after -b: gitkit made the branch too
- 両方の記録は git 独自のストレージに保存されるため、状態ファイルは不要です。ワークツリーのマーカーは
.git/worktrees/の下にあるワークツリーの管理ディレクトリに置かれ、ワークツリーとともにgit worktree removeが削除します。ブランチキーはブランチの設定セクションに置かれ、ブランチとともにgit branch -dが削除します。 - この実行で
-bを使った場合にのみブランチキーを書き込みます。 すでに存在していたブランチは作成した人のものです。引き継いだワークツリーにはマーカーを付けません。 - 記録がなければ gitkit のものではありません。 このルールができる前に作成されたワークツリーやブランチ、または手作業で作成されたものは、引き継いだものとして扱われ、削除してもそのまま残ります。
パスとブランチを報告します。ワークツリーを作成しても、その中で何かを行うことにはなりません。$WT に $BRANCH のワークツリーが存在し、その記録が書き込まれた時点で終了します。
削除
test -e "$(git -C "$WT" rev-parse --absolute-git-dir)/gitkit-created" # gitkit made this worktree?
git -C "$REPO" config --get "branch.$BRANCH.gitkitCreated" # gitkit made this branch?
git -C "$REPO" worktree remove "$WT"
git -C "$REPO" branch -d "$BRANCH"
このコレクションで削除に関するルールはこれだけです。 ワークツリーを削除する、またはブランチを削除するすべてのスキルは、ここでの一括処理、ワークスペースツールのクリーンアップ、イシューのクローズ処理を問わず、このルールに従い、独自のルールとして書き直すのではなく、ここを参照します。実際に作業を失う可能性を防ぐ、4つのルールです。
- 変更のあるワークツリーでは削除処理を中止します。 失われる内容(未コミットの変更、未追跡ファイル、未プッシュのコミット)を正確に示し、人間に判断を委ねます。本人に代わって
--forceを使ってはいけません。 - マーカーに gitkit が作成したと記されているワークツリーだけを削除します。 マーカーのないワークツリーは引き継いだものであり、他者の作業コンテキストです。そこで何が開かれているかはわかりません。
gitkitCreatedキーが設定されているブランチだけを、-dで削除します。 これにより Git 自体がマージされていないブランチの削除を拒否するため、誤った判断は作業を削除するのではなく、明確なエラーになります。- 削除ごとに確認を1回行います。 ワークツリーもブランチもそれぞれ個別の判断が必要です。複数を一覧表示する場合は、まとめて1回ではなく、行ごとに1回ずつ回答を求めます。
squash マージの場合だけ、gitkit は -D を実行します。 squash マージされたブランチは、squash コミットからブランチへの祖先関係がないため、-d では削除できません。gh がそのブランチのマージ済みプルリクエストを報告し、そのプルリクエストの headRefOid がローカルブランチの先端と一致する場合に限り、-D を使います。
gh pr list --head "$BRANCH" --state merged --json number,headRefOid --jq '.[0]'
git -C "$REPO" rev-parse "$BRANCH" # must equal headRefOid, or -D is off the table
SHA が一致すれば、ブランチ上のすべてのコミットがマージ済みのプルリクエストに含まれていることが証明されます。マージ後に先端が移動していれば、マージ時にはなかったコミットが含まれているため、ブランチは残し、レポートに記載します。その行の確認では、プルリクエスト番号を明記してください。
すでに存在しない場合は、「すでに削除済み」と報告して成功とします。引き継ぎと同じく、削除処理も冪等です。確認済みの各ワークツリーとブランチが削除されるか、削除できない理由が報告された時点で終了します。
一覧表示
git -C "$REPO" worktree list
通常の一覧です。ユーザーが状況確認ではなく整理を求めている場合は、clean モードを使います。このモードでは、選択肢を提示する前に各行を分類します。
移動または復元後にパスがおかしくなった場合は、git worktree repair <path> で参照先を更新します。Git は .git/worktrees/<name>/gitdir と各ワークツリーの .git ファイルに絶対パスを保存するため、ワークツリーを手動で移動した場合は必ず修復が必要です。
ベース参照
main と決めつけてはいけません。実際のリポジトリではデフォルトブランチが develop、trunk、master の場合もあり、間違えると差分が空になったり、極端に大きくなったりしても気づけません。次の順序で確認します。
gh repo view --json defaultBranchRef --jq .defaultBranchRef.name。GitHub CLI が利用可能で認証済みの場合、これが正式な情報です。git symbolic-ref --short refs/remotes/origin/HEAD。設定されている場合は、ローカルに記録された情報を使います。- 設定されていない
origin/HEADを修復するためにgit remote set-head origin --autoを実行し、前の手順を再試行します。ほとんどの実装が省略しますが、失敗を回答に変える重要な手順です。 - 実際に存在する
origin/mainまたはorigin/masterのいずれかを使います。 - 人間に尋ねます。この段階から先は推測してはいけません。
以降の処理ではすべて、この回答を使います。git diff <base>...HEAD、先行・後続コミット数、ワークツリーの作成元となるブランチが対象です。
スタックのレイヤーだけは意図的な例外ですが、ここでも確認手順は実行します。 レイヤーのベースはリポジトリのデフォルトではなく、その下のブランチです。そのため呼び出し側が指定し、このレイヤーのブランチには確認手順を適用しません。ただし、スタック最下層の基盤となるトランクの解決には、引き続き確認手順を使います。兄弟ブランチをベースにすることを禁止するルールは、それ以外のすべての場面で引き続き有効です。このルールが防ぐのは、たまたまチェックアウトされていたブランチからワークツリーを作るという意図しないケースだからです。レイヤーのベースが認められるのは、誰かがブランチとその理由を指定した場合です。スタックブランチを参照してください。
リベースまたはマージ
フィーチャーブランチをベースブランチと同期する際のデフォルトはここでは一つであり、プルリクエストが開かれているかどうかには左右されません。
フィーチャーブランチを同期するには、公開済みかどうかにかかわらず、ベースブランチにリベースします。ベースブランチをマージするのは、明確な理由とユーザーの同意が必要な例外です。
デフォルトがリベースなのは、それが望ましい履歴だからです。フィーチャーブランチは、ベースブランチの作業を下地にして、そのブランチ独自のコミットが直線的に並ぶ形で読めます。同期のたびにベースブランチをマージするリポジトリでは、同期ごとにマージコミットが一つずつ増え、ブランチが取り込まれるずっと前から、そのブランチ独自の履歴が読みにくくなります。
リベースには実際のコストがあります。それは見た目の問題ではなく、コミット SHA が書き換えられることです。プルリクエストが開かれると、レビューのスレッドはそのコミットに紐づくため、強制プッシュをすると、人間とボットの両方による指摘がすべて古いものとして扱われます。これは開示して検討すべきコストであり、リベースを妨げる理由ではありません。また、マージを例外として提案するための、あらかじめ想定された唯一の理由でもあります。
実際には、次のようにします。
git fetch origin
git rev-list --left-right --count "origin/$BASE"...HEAD # "<behind>\t<ahead>"; left > 0 means behind
git rebase "origin/$BASE"
- このブランチについて別の指示がない限り、答えはリベースです。 プルリクエストがない場合、開かれている場合、共有ブランチの場合のいずれも、推奨する方法は同じです。
- リモートに対応するブランチがない場合は、そのままリベースします。 その SHA を参照するものがないため、壊れるものもありません。これは低コストで、元に戻せるケースなので、確認は不要です。
- 公開済みのブランチでは、事前に一度だけ内容を提示して待ちます。 リベースと、その後の
--force-with-leaseによるプッシュは一つの判断なので、両方に対して一度の確認で済みます。同じ質問を二度する必要はありません。コストを明示してください。同期するベースブランチ、書き換えられるコミット、古いものとして扱われる未解決のレビュースレッドの数です。 - 確認を求める前に、スレッド数を数えます。 オープン PR のあるブランチでは、GraphQL の
reviewThreads接続を照会して未解決のスレッドを数え、その数を事前の提示に含めます。「3 件のレビュースレッドが古いものとして扱われます」は、ユーザーが判断できる事実です。「レビューコメントが古くなる可能性があります」では判断できません。 - 未解決のスレッドは、マージの例外を選ぶ理由ではなく、例外を提示する理由です。 それでもリベースを推奨し、数とともに、選択可能な代替案としてマージを示します。それに反対するケースを取り込んでしまうような選好は、そもそも選好とは言えません。
--force-with-leaseを使い、単独の--forceは決して使いません。 リベース中に他の人がプッシュしたコミットを上書きしないようにするのが、リースの役割です。- コンフリクトが発生したら、作業を止めて内容を提示します(
git diff --name-only --diff-filter=U)。ファイルごとに解決案を提案し、書き込む前に確認します。コンフリクトの解決はコード変更なので、その後、リポジトリのテストゲートを実行します。
sync
上記のルールを実行可能な形にしたものです。同期はこの順番で行う2つの段階から成ります。まず、ローカルブランチを origin 上の対応するブランチに追いつかせ、次にベースの最新状態を取り込みます。最初の段階を省くと、後で --force-with-lease が拒否されたり、別のマシンからプッシュされたコミットが気付かないうちに失われたりします。ブランチのワークツリーで実行してください。各手順には、その手順を終える条件が記されています。
- 基盤を整えます。 ベース参照の優先順位に従ってベースを解決します。
git -C "$WT" fetch origin --pruneを実行します。ワークツリーがフィーチャーブランチ上にあり、git status --porcelainの結果が空であることを確認します。作業ツリーに変更がある場合は同期を中止し、ファイルを報告して、人間に stash またはコミットを依頼します。ベース名、ブランチ名、クリーンなツリーがすべて確認できた時点で終了します。 origin/$BRANCHと同期します。 リモートブランチが存在する場合はgit rev-list --left-right --count "origin/$BRANCH"...HEADを実行します。Behind の数が0なら、取り込むものはありません。それ以外の場合はgit pull --rebase origin "$BRANCH"を実行し、ローカルのコミットをリモートのコミットの上にリプレイします。競合が発生した場合は、以下のすべての競合を解決するのルールに従って、すべて解決します。リモートに対応するブランチがない場合は、この手順を省略します。ローカルブランチにorigin/$BRANCH上のすべてのコミットが含まれた時点で終了します。- ベースとの差分を測定します。
git rev-list --left-right --count "origin/$BASE"...HEADを実行します。Behind の数が0なら、ブランチはすでに最新のベースに追いついています。前の手順で取り込んだものがあればプッシュして報告し、終了します。Behind と Ahead の数が確定した時点で終了します。 - プレビューして、一度だけ確認を求めます。 プッシュされていないブランチではこの手順を省略し、そのまま次へ進みます。公開済みのブランチでは、リベースと強制プッシュの両方を対象にしたプレビューを1回行います。内容は、ベース、Behind と Ahead の数、およびプルリクエストが開いている場合は GraphQL の
reviewThreads接続から取得した未解決のレビュー スレッド数です。マージを代替案として提示し、リベースを引き続き推奨します。ユーザーの回答を得た時点で終了します。 - ベースの上にリベースします。
git rebase "origin/$BASE"を実行します。リベースが成功したと報告されるか、競合で停止した時点で終了します。 - すべての競合を解決します。 停止するたびに
git diff --name-only --diff-filter=Uでファイルを一覧表示します。競合している各ファイルを読み、ブランチの意図とベースの新しいコードの両方を保つ解決案を提示して、書き込み前に確認を得ます。ファイルをステージし、git rebase --continueを実行します。残りの停止についても繰り返します。git rebase --abortを実行するとリベース前の状態に戻ります。競合を自分で解決できない場合は、この方法を選びます。競合マーカーが残っておらず、リベースが完了した時点で終了します。 - ブランチが引き続き動作することを確認します。 リポジトリ独自のテストおよびビルドのゲートを実行します。失敗した場合は出力とともに報告し、プッシュする前に停止します。成功するか、停止した時点で終了します。
- リース付きでプッシュします。
git push --force-with-lease origin "$BRANCH"を実行します。リースが拒否された場合、リベース中に誰かがプッシュしたことを意味します。フェッチして新しいコミットを表示し、再試行する前に確認を求めます。リモートブランチがローカルブランチと一致した時点で終了します。
引き継ぎ
origin/$BRANCH から取り込んだコミット、ベース、Behind と Ahead の数、競合を解決したファイル、ゲートの結果、プッシュしたブランチを報告します。ブランチがすでに最新で、変更がなかった場合はその旨も伝えます。次に、新しいベース上でプルリクエストの差分を確認し、スレッドが古くなっていた場合はレビューを再依頼します。
マージ時の例外
ユーザーがマージを選ぶ場合、または明示的にマージを求める場合も、これは同期であり、そのことが分かるようにします。
git merge "origin/$BASE" -m "chore(repo): sync with origin $BASE"
- git のデフォルトの件名は使いません。
Merge branch 'main' into issue-42-tailwind-4-foundationは、誰もメッセージを選ばなかったときに git が書き込むもので、git logにいつまでもそのまま表示されます。固定の件名をメッセージにします。 mainをハードコードせず、ベースを補間します。 ベース参照の優先順位があるのは、developやtrunkを使うリポジトリも実在するからです。そのようなリポジトリでmainと書かれた件名は、単純に事実と異なります。--no-ffは使いません。 同期でファストフォワードできるなら、そのブランチに保持すべき独自の作業はなく、git はコミットをまったく書き込みません。そのため修正すべき件名もなく、強制すれば中身のないマージコミットを作ることになります。- 依頼と同時に理由を述べます。 「この PR には未解決のレビューコメントが 3 件あるため、リベースではなくマージします」は理由になります。「安全のためにマージします」は理由になりません。リベースで何が壊れるのかを明示できないなら、リベースしてください。
git pull を単独で実行しない
リモートより遅れているブランチで git pull を実行すると、デフォルトではマージが行われ、誰にも確認せずに Merge branch 'main' of github.com:owner/repo と書き込まれます。git pull --rebase を使うか、同期を自動解決せずにエラーで明示させたい場合は git pull --ff-only を使ってください。ベースブランチ自体を更新するために git pull を実行する場合も同様です。設定するかフラグを渡し、デフォルトのまま実行しないでください。
clean
作業が取り込まれたワークツリーとブランチを、自分のマシンと origin から一掃します。すべてのワークツリー、ローカルブランチ、リモートブランチを、active、adopted、dirty、reapable、orphan のいずれかに分類し、reapable のものだけを一件ずつ確認して削除します。リモートの削除には個別の確認と、作業が取り込まれたことを示す個別の証拠が必要です。
git branch --merged を繰り返すだけの処理と異なる点は 2 つあり、どちらも clean.md に記載されています。スカッシュマージされたブランチは系譜からは分からないため、「マージ済み」の判定には 1 つではなく 3 つの検出方法が必要です。また、項目ごとに確認するのは意図的です。掃除対象の行がどれも同じように安全に削除できるわけではないからです。実際に一掃する実行時には、そのファイルを読んでください。
削除のルールは、一掃処理によるすべての削除に適用されます。各項目を個別に確認し、スカッシュマージの例外に限って -D を使い、gitkit のマーカーがないワークツリーには触れず、dirty なものがあれば停止します。origin ではベースブランチを削除せず、オープンなプルリクエストの head も削除しません。
rescue
失われたように見える作業を見つけて、元に戻します。失敗したリベース、ハードリセット、削除されたブランチ、見つからないスタッシュなどの場合も、コミットは通常まだ残っており、git のログにはどこへ行ったのかが記録されています。
手順は rescue.md にあります。2 つのルールが手順を定めます。復元するまでは読み取り専用であり、リセットこそが作業を失わせた操作であるため、復元ではブランチを移動するのではなくブランチを追加します。また、安全性をほのめかさず、到達不能なコミットは git によってプルーニングされるまで残るという限界も明記しています。
救出中は、どのモードでも git gc、git prune、git reflog expire を実行しないでください。
スタックブランチ
スタックとは、同じリポジトリ内で、各ブランチがその下のブランチから分岐し、最下層が trunk をベースとするブランチの連なりです。まだマージされていない作業を前提とする作業に取りかかるときに使います。前提となるプルリクエストが取り込まれるのを待つ代わりに、そこからブランチを作成して作業を続けられます。
GitHub はこの機能をネイティブにサポートしており、gh stack 拡張機能で操作できます。gitkit の中で gh と GitHub リモートが必要なのは、この部分だけです。 コマンドの全容、事前チェック、機能が使えない場合の代替手順は stacks.md にあります。実際にスタックを操作する実行時には、そのファイルを読んでください。以下では、すべての実行で必要となる内容を説明します。
レイヤーごとに1つの worktree
各レイヤーは、それより下のレイヤーから分岐した独自の worktree を持ちます。 ブランチごとに1つの worktree という不変条件は変わりません。レイヤーはブランチなので、その名前を付けたディレクトリが割り当てられ、ブランチから引き続き検索できます。
gh stack up と down を使い、1つの worktree 内でレイヤー間を移動する方法が、拡張機能自体のナビゲーションが想定しているものです。しかし、このツール群ではその方法を採用しません。ここで呼び出すものはすべて、作業対象の issue に worktree を対応付けている一方で、スタックは複数の issue から成るからです。そのため、gh stack のナビゲーションコマンドは対象外であり、up、down、switch、top、bottom を実行するのは gitkit の役目ではありません。これらのコマンドは、別の worktree がすでに保持しているチェックアウトを移動させてしまうためです。
gitkit がここで担うことと、担わないこと
gitkit が担当するのは配管処理だけです。スタックの作成、レイヤーの追加、並べ替え、同期、確認、既存のブランチのスタックへの取り込みを行います。ほかの場所でこのスキルが守る境界を越えるため、次の2つのコマンドは意図的に対象外としています。
gh stack submitはプルリクエストを作成します。 gitkit は何も作成しません。それは、プルリクエストを作成するスキルの役目です。gh stack mergeはプルリクエストをマージします。 gitkit はマージを行いません。スタックのマージは複数のプルリクエストを一度に取り込む 連鎖処理 であり、対象を1つずつ明記した人間の確認が必要です。それは、マージを担当するスキルの役目です。
コマンドではなく担当者を明確にすることが重要です。gitkit は下準備を行い、ブランチがスタックされているかどうかにかかわらず、公開は別の担当者が行います。
深さに伴うコスト
スタックにレイヤーを1つ追加するごとに、レビュー対象のプルリクエストが1つ増え、下のレイヤーが変更されるたびにリベースするブランチも1つ増えます。並べ替えは連鎖的に行われるため、最下層の変更は、その上にあるすべてのレイヤーを書き換えます。
意図的に、ここでは数値を示しません。 スタックの深さは、各レイヤーの規模とレビューにかかる時間によって異なり、人間が判断することだからです。尋ねられたら仕組みを説明し、適切な深さを自分で判断できるようにしてください。
コンテナーとリモート環境
Git worktree は絶対パスを保存します。リポジトリがホストとは異なるパスにバインドマウントされると、コンテナー内のすべての worktree が壊れます。
$WORKTREE_ROOTとメインのチェックアウトの両方をマウントしてください。worktree の実際の Git ディレクトリはメインリポジトリの.git/worktrees/内にあるため、worktree だけをマウントするとチェックアウトが壊れます。- コンテナーの内外で同じ絶対パスに両方をマウントしてください。これが最も堅牢な方法です。
- さらに備えるには、git 2.48 以降で
git config worktree.useRelativePaths trueとgit worktree repair --relative-pathsを実行します。相対位置は、リポジトリと worktree のルートが相対的な位置関係を保つ限り、パスの変更後も有効です。そのため、たとえば~/code/<repo>と~/worktrees/<repo>のように、両者を並べて配置するのがおすすめです。
worktree を管理する GUI 開発ツールは、これらを監視して構いません。Orca とその類似ツールは git worktree list を読み取り、通常は外部で作成された worktree を表示する設定を備えています。GUI を実行するマシンで一度設定すればよく、これがマシン間にある唯一の非対称性です。
いかなるスキルも、ベンダーの CLI を通じて worktree を作成または削除してはいけません。そうした時点で、2台のマシンの状態に差が生じます。カードが関連付けられている issue、ステータス、コメントなど、ベンダー独自の worktree メタデータは別の層なので、利用して構いません。それらはそのツール内にしか存在せず、差異が生じる Git 上の同等物がないためです。コンパニオンスキルがその層を調整することもできます(Orca の場合は orcakit が行います)。ただし、worktree 自体の作成と削除は引き続きここで行ってください。gitkit はその逆方向の呼び出しを一切行いません。そのようなツールがインストールされていないマシンでも、動作し続ける必要があるためです。
注意事項
- gitkit は、自身が所有するものに関する唯一の信頼できる情報源です。 呼び出し元のスキルは、PR を取得する価値がある、ブランチを公開する準備ができているなど、いつ確認を求めるかに関するポリシーを持つことはできますが、git 操作の方法に関するポリシーを持つことはできません。
- 結論を言い直すことは許されます。導出過程を言い直すことが問題です。 すべての公開スキルは単独でインストールされ、gitkit が存在しないリポジトリでも使われるため、git に関する事実を必要とする呼び出し元は、動作を継続できるだけの情報を保持する必要があり、この要件は簡潔さより優先されます。境界は次のとおりです。呼び出し元は、gitkit の回答と、その機能が使えない場合の代替策を併記できます(「同期ルールは gitkit が管理し、結果は rebase です。gitkit がない場合は、ベースブランチに対して rebase します」)。一方、回答を導き出す推論、つまりベース参照の完全な優先順位、worktree のパス計算式、rebase と merge の比較論を再現してはいけません。回答は、名前が変わっても気付かないまま無効にならない一行です。導出過程こそが、ずれを生むものです。参考になる準拠例として、prkit と mergekit は rebase という結論と、確認を一度行うルールを、それ以上導出せずに示しています。また、wikikit と designkit は、5 段階の優先順位を持ち込まずに、ベース参照の代替策として
gh repo view --json defaultBranchRefを挙げています。 - gitkit は何も実装せず、コミットせず、公開もしません。 gitkit が行うのは、準備と後片付けです。worktree でのコード作成、コミット、公開は、ほかのスキルが担当します。
- 破壊的な手順は、事前に内容を示して確認を取ります。 worktree の削除、ブランチの削除、公開済みブランチの rebase では、実行内容を示し、承認を待ちます。worktree の作成、既存 worktree の採用、未 push のブランチの rebase は、低コストで元に戻せるため、そのまま実行します。判断基準は、入力したコマンドの種類ではなく、書き換えようとしているものをこのマシンの外部から参照しているものがあるかどうかです。
- シェルを利用できない場合(例:ブラウザベースのエージェント)は、git を実行できません。その場合は、ユーザーが提供した情報に基づいて判断し、ユーザーが実行するための正確なコマンドをコードブロックで出力してください。また、自分では実行できなかった worktree の作成や削除を、実行したと報告してはいけません。