Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】git stash Is Shared Across Worktrees. Two Parallel Agents Popped Each Other's Work

2026年11月05日に「Dev.to」が公開したITニュース「git stash Is Shared Across Worktrees. Two Parallel Agents Popped Each Other's Work」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

Gitの`stash`は`worktree`間で共有される。複数の`worktree`で同時に使うと、意図せず他者の変更が適用されたり、自分のスタッシュが消えたりする危険がある。作業の一時保存には、`stash`に頼らずWIPコミットなど、より安全な方法を用いるのが推奨される。

ITニュース解説

Gitのgit worktreeとgit stashは、開発作業を効率化するための非常に便利な機能だ。しかし、これらを組み合わせて利用する際には、注意が必要な点があることが今回のニュース記事で報告された問題から明らかになった。システムエンジニアを目指す上では、このようなGitの深い挙動を理解することが重要だ。

まず、それぞれの機能について簡単に説明する。git worktreeは、一つのGitリポジトリに対して複数の作業ディレクトリ(worktree)を紐づけることができる機能だ。これにより、例えば、あるブランチで作業を進めながら、別のブランチのバグ修正を一時的に確認したり、別の新機能開発を並行して行ったりすることが可能になる。それぞれのworktreeは、自身の作業ファイル、現在見ているコミットを示すHEAD、そしてステージングエリアであるindexを独立して持つ。

一方、git stashは、現在行っている作業内容を一時的に保存しておきたい場合に使う機能だ。例えば、まだコミットする準備ができていない変更があるが、急いで別のブランチに切り替えて別の作業をする必要がある、といった状況で活用される。git stash pushで変更を退避させ、git stash popやgit stash applyで退避した変更を元に戻すことができる。

今回のニュース記事で報告された問題は、これら二つの機能が組み合わさった時に発生した。あるプロジェクトで、二つの自動化されたエージェントが、それぞれ異なるgit worktreeを使って同じリポジトリ上で並行して作業を行っていた。両エージェントは、それぞれの課題に対する修正を加え、その修正が失敗することを検証するために「修正をstashで退避させ、テストを実行し、その後stashから修正を元に戻す」という同じ手順を踏んだ。

ここで予期せぬ事態が発生した。エージェントAがgit stash popを実行すると、驚くべきことにエージェントBがstashしたはずの変更が戻ってきたのだ。同様に、エージェントBのpopではエージェントAがstashした変更が戻ってきた。結果として、それぞれのworktreeには他方のエージェントの作業ファイルが持ち込まれ、stashリストは空になってしまった。Gitはエラーや警告を一切出力せず、'Dropped refs/stash@{0}'という通常のメッセージを表示したため、この異常はすぐに気づかれなかったという。

なぜこのような問題が発生したのだろうか。その理由は、Gitの内部構造、特に「参照(refs)」の扱いにあった。Gitのマニュアルには、git worktreeが分離するものと共有するものが明確に記されている。「HEADのような擬似参照はworktreeごとに独立しているが、refs/で始まる参照は共有される」というルールがある。git stashが保存される場所はrefs/stashであり、このルールに照らし合わせると、refs/stashはリポジトリ内のすべてのworktree間で共有されることがわかるのだ。

これは、各worktreeが自身の作業ファイルやHEAD、indexを「個別の机」のように独立して持っているのに対し、stashは「みんなで使う一つの引き出し」のようなものだと考えるとわかりやすい。git stash popコマンドは、引数を指定しない場合、常にstash@{0}、つまりstashリストの最も新しいエントリーを取り出す。この際、どのworktreeがそのstashを作成したかを確認する機能は持っていない。そのため、エージェントAがstashした直後に、エージェントBが自分の作業とは関係なくgit stash popを実行すると、Aがstashした内容がBのworktreeに適用され、リストから消えてしまうという状況が発生したのだ。

実際に同じコミットから二つのworktreeを作成し、一方のworktreeでファイルを編集してgit stash pushした後に、もう一方のworktreeに移動してgit stash listを実行すると、先ほどstashしたエントリーがリストに表示されることを確認できる。さらに、そのworktreeでgit stash popを実行すると、最初のworktreeでstashした変更が、全く関係ない現在のworktreeに適用されてしまう。元のworktreeに戻ってgit stash listを確認すると、リストは空になっているはずだ。

このstashが共有されるという設計は、もともとGit開発チームによる意図的な選択だった。一人の開発者が複数のブランチを行き来しながら作業する場合、あるworktreeでstashした内容を、別のworktreeで取り出して適用することで、未コミットの変更を異なるブランチや作業フォルダに簡単に移動できるという利便性があったためだ。しかし、「stashは一人で使うもので、何が保存されているかをその所有者が把握している」という前提は、並行して作業する複数のエージェントや開発者がいる環境では成り立たなくなり、今回の問題のような衝突の原因となる。

このような問題を回避し、git worktree環境下で安全に作業を進めるためには、いくつかの「安全なパターン」が提案されている。

一つ目の方法は「WIP(Work In Progress)コミット」を使うことだ。作業を一時的に脇に置きたい場合、git stashの代わりに、現在の変更をgit add -Aでステージングし、git commit -m "wip(<タスクID>): <変更の目的>"といったメッセージで一時的なコミットを作成する。後でその作業に戻る際には、git reset --soft HEAD~1を実行すれば、コミットは取り消されるが、変更内容はステージングされた状態で保持される。この方法は、コミットがブランチごとに管理されるため、他のworktreeに影響を与える心配がない。ただし、Gitは通常、既にチェックアウトされているブランチで新たなworktreeを作成することを拒否するため、他のworktreeが同じブランチをチェックアウトしている状況は稀だ。

二つ目の方法は、「使い捨てのworktree」を利用することだ。今回の事例でエージェントが行っていた「修正なしでテストが失敗することを確認する」といった目的のために、一時的なworktreeを作成する方法だ。まず、テストコードのみをコミットし、そのコミットに対してgit worktree add --detach <パス> HEADを実行して、一時的なworktreeを分離モードで作成する。このworktreeは、現在の作業中の未コミットの変更を一切含まないため、他の作業に影響を与えることがない。テスト検証が終われば、git worktree remove <パス>で削除するだけだ。

三つ目の方法は、「refs/worktree/下のスクラッチ参照」を使うことだ。これは少し高度な方法だが、git stashの共有の性質を回避しつつ、同様の退避機能を実現できる。Gitのマニュアルで「共有されない」と明記されているrefs/worktreeネームスペースを利用する。具体的には、git stash createコマンドで、現在の作業内容をstashコミットとして作成し、そのコミットのハッシュをgit update-ref refs/worktree/wip <ハッシュ>のようにして、独自の参照としてrefs/worktree/下に保存する。これにより、このstashは他のworktreeから直接見られることなく、プライベートな退避場所として機能する。後で元に戻す際は、git stash apply refs/worktree/wipで適用し、git update-ref -d refs/worktree/wipで参照を削除する。この方法の注意点として、git stash createは追跡されていないファイルは保存しないこと、また何も保存するものがなければ何も出力しないことが挙げられる。

四つ目の方法は、「名前付きstashをハッシュで適用する」ことだ。git stash push -m "タスクID: リファクタリング前"のようにメッセージ付きでstashを作成し、後でgit stash list --format='%gd %H %gs'のようなコマンドでリストを表示し、目的のstashのハッシュ値を見つける。そして、git stash apply <ハッシュ>で直接指定して適用する。この方法では、popやdropの際にstash@{n}のような相対的な位置指定ではなく、明確なハッシュで操作できるため、他のworktreeがstashを追加しても影響を受けにくい。ただし、popやdropを実行する直前には、改めて位置を確認する必要がある。

今回の問題が報告されたプロジェクトでは、具体的な対策として二つのことが行われた。一つは、エージェント向けの指示書に警告を追加し、git stashの共有性質と安全な代替手段を明記すること。もう一つは、git stashの共有スタックを変更するコマンド(push, pop, drop, clearなど)をブロックするPreToolUseフックを導入することだ。このフックは、明示的にハッシュを指定するapplyやstore、情報を表示するlistやshow、そしてcreateのみを許可する。これにより、エージェントが誤って共有stashを変更するのを防ぐことができる。

git stashは非常に便利な機能だが、git worktreeと組み合わせて複数の作業環境を使う場合、その共有される性質を理解しておくことが非常に重要だ。特に、複数の人間や自動化されたエージェントが並行して同じリポジトリで作業する際には、「自分の作業は自分だけのもの」という前提がstashには当てはまらないことを意識し、上記のような安全なパターンを採用することで、意図しない衝突やデータの消失を防ぐことができる。これは、Gitの奥深い機能を使う上で、システムエンジニアを目指す初心者にとっても重要な学びとなるだろう。

関連コンテンツ

関連IT用語