🐳

Dev Container でも Git Worktree で快適に開発したい

に公開

はじめに

Claude Code の公式ドキュメントで紹介されたことで再び注目された Git Worktree ですが、普段 Dev Container を利用して開発を行なっているとあまりうまく使えないなと感じていました。

今回は Git Worktree が使いやすい Dev Container について考えてみました。

抱えていた問題

これまで Dev Container の環境を用意する場合、プロダクトのソースと同じリポジトリで管理してきました。

例えば以下のような形です。

.
├── .devcontainer
├── .git
├── frontend
└── backend

この状態で git worktree add を行うと、当然 .devcontainer を含めた一式がクローンされることになります。
(main/.git はフォルダですが追加されたワークツリーの feature-1/.git はファイルになっています)

.
├── main
│   ├── .devcontainer
│   ├── .git
│   ├── frontend
│   └── backend
└── feature-1
    ├── .devcontainer
    ├── .git
    ├── frontend
    └── backend

この場合、 feature-1 をどの Dev Container で開発するのかに悩みました。

  1. feature-1 内の .devcontainer で新たにコンテナを立ち上げる
  2. main でコンテナを起動してアタッチして feature-1 を開く

実行環境とプロダクトのソースは一体だと考えるなら 1. で行うのが筋ですが、Git Worktree を用いたワークフローだと頻繁にワークツリーを作成・削除するかと思います(使い回しても良いのでしょうが)。新たなワークツリーを追加するたびに環境構築をやり直して新しいコンテナが立ち上がるのを待つのはちょっと面倒だなと感じます。

2.の場合は例えば feature-1 のために必要なツールをインストールするような時に、 main の環境を汚しているような気分になります。

解決策

しばらく 2. の方法で運用していたのですが、共有するくらいだったら Dev Container 専用のリポジトリを作って別で管理してもいいのでは?と考えました。

開発環境専用のリポジトリを作成する

サンプルがこちら
https://github.com/KOHSUK/container-env

フォルダ構成

.
├── .devcontainer
├── .gitignore
├── README.md
└── <ここに好きなリポジトリをクローン>

ホワイトリスト形式の .gitignore

このリポジトリのミソは .gitignore をホワイトリスト形式で記述していることにあります。
要するに、一旦全て ignore して、Git 管理したいものだけ ! をつけて記載するようにしています。
内容は以下のとおりです。

*
!/*

!/.gitignore
!/README.md

!/.devcontainer/**

これで、この環境のルートに置いたフォルダの中身は全て無視されます。

Dev Container CLI を利用する

私は普段の開発で Cursor を利用しているのですが、最近 Cursor の Extension から Microsoft の Dev Container をインストールすることができなくなりました。

https://zenn.dev/varubogu/articles/cursor-extension-devcontainers

一時期は代替手段として Cursor の開発元の Anysphere 製の Dev Container を利用していたのですが、今まで利用できていたコンテナが起動できなくなるなどトラブルがありました。

そこで、いっそのこと Dev Container CLI を使うのはどうだろうということで利用し始めました。

https://github.com/devcontainers/cli

コマンドインストール後、 .devcontainer フォルダのある階層で以下のコマンドでコンテナが起動します。

devcontainer up --workspace-folder .

IDE でコンテナにアタッチする

VSCode など Dev Container 対応の IDE で先ほどのコンテナにアタッチします。
結局 Dev Container の Extension をインストールしておく必要はあります(Cursor の場合は Anysphere 製で OK のはず)。

コマンドパレットから Attach to Running Container... を選択。

上記のサンプルのリポジトリだと3つのコンテナが起動するので app が名前につくコンテナを選択します。

これでコンテナにアタッチした状態で VSCode が起動します。

任意のリポジトリをクローンする

次に開発を行うリポジトリをクローンします。サンプルのコンテナの設定ではリポジトリのルートのフォルダをコンテナ内の /workspaces にマウントしているのでそこにクローンします。

cd /workspaces
git clone <リポジトリ>

例えば以下のようにとあるリポジトリを main フォルダにクローンします。

.
├── .devcontainer
├── .gitignore
├── README.md
└── main <----開発用の別リポジトリ

開発用のフォルダを IDE で開く

最後に先ほどコンテナにアタッチした IDE で、今クローンしたフォルダを開けば開発を開始できます。
コマンドパレットの File: Open Folder... から /workspaces/main 選択します。

File: Open Workspace from File... でワークスペースを開くのも良いかもしれません。

Git Worktree を活用する

この環境で Git Worktree を活用していきます。

例えば新しく機能開発をするとして、先ほどクローンした main のフォルダで

git worktree add ../feature-1 -b feature/#1

workspaces/feature-1 フォルダに feature/#1 がチェックアウトされた状態になります。

当然この workspace/feature-1 も大元の Dev Container のリポジトリに差分表示されることはありません。

feature-1 に移動すれば、feature/#1 がチェックアウトされた状態になっていることがわかります。

VSCode 側でも main を開いたのと同じ手順で feature-1 を開けばすぐに開発に着手できます。

注意点

コンテナ内で作成した Git Worktree はローカルでは認識しません。
厳密には git worktree list で表示はされますが、以下のように prunable になっています。

コンテナ内

$ git worktree list
/workspaces/main       a8d57e7 [main]
/workspaces/feature-1  a8d57e7 [feature/#1]

ローカル

git worktree list
/Users/username/container-env/main  a8d57e7 [main]
/workspaces/feature-1                                a8d57e7 [feature/#1] prunable

feature-1/.git の中身が以下のようにコンテナ内の絶対パスになっていることからも、併用は避けた方が良いと思われます。

gitdir: /workspaces/main/.git/worktrees/feature-1

この環境で開発を行なっての所感

上記サンプルの Dev Container の環境には Postgres も仕込んでいます。git worktree で新しい環境を作った際にもデータベースは同じものに簡単に接続できるので重宝しています(DB に変更が加わる機能開発は別ですが)。

これまで多くのプロジェクトで、プロダクトのソースと Dev Container のソースを同じリポジトリで管理できていましたが、今回のやり方では良くも悪くも別管理になりました。
「今回のバージョンからこのツールが必要です。」みたいな時に古い Dev Container 環境で立ち上げてトラブルになる、などは考えられますが、プルリク内で言及するなどで今のところは問題なく運用できています。

一方で今回の工夫で、Git Worktree での開発フローがとても安定しました。
Claude Code と Git Worktree で並列で開発するやり方も定着しそうです。

Discussion