Jujutsu (jj) でgit submoduleを追加したらgitlinkがコミットされない問題
TL;DR(結論)
最終結論: jjをメインで使いたいなら、submoduleをやめてVSCodeマルチルートワークスペースに移行する方が楽でした(後日談参照)。
submoduleを使い続ける場合: gitlinkはjjではなくgitで直接コミットする必要があります。
# submoduleを追加
git submodule add https://github.com/user/repo.git projects/repo
# gitで直接コミット(git submodule addで自動的にstageされている)
git commit -m "chore: repoをsubmoduleとして追加"
# jjにインポートしてpush
jj git import
jj git push
jjはgitのstagingを見ないため、jj commitではgitlinkがコミットされません。また、jj statusでsubmoduleがD(Deleted)と誤表示される問題もあり、運用上の課題が多いです。
問題の発生状況
やったこと
モノレポ構成で新規プロジェクトをsubmoduleとして追加しようとしました。
# GitHubでリポジトリ作成済み
# submoduleとして追加
git submodule add https://github.com/user/new-project.git projects/new-project
# .gitmodulesが更新されたことを確認
cat .gitmodules
# [submodule "projects/new-project"]
# path = projects/new-project
# url = https://github.com/user/new-project.git
# jjでコミット
jj commit -m "chore: new-projectをsubmoduleとして追加"
jj bookmark set main -r @-
jj git push
一見うまくいったように見えました。
発覚した問題
後日、submodule内で更新し、親リポジトリで参照を更新しようとしたところ、問題が発覚しました。
git submodule status
# (空の出力)
git status
# Untracked files:
# projects/
projects/ディレクトリがuntrackedになっている!
原因の調査
コミット履歴を確認:
git show <commit-hash> --stat
# .gitmodules | 3 +
# steering/plans/roadmap.md | 179 ++++++++++
# 2 files changed, 182 insertions(+)
.gitmodulesは追加されているが、submodule自体(gitlink)が含まれていない。
gitlinkがtrackされているか確認:
git ls-files --stage projects/new-project
# (空の出力)
git ls-tree HEAD projects/
# (空の出力)
gitlinkがコミットされていなかった。
原因:jjはgitのstagingを見ない
gitのsubmodule追加の仕組み
git submodule addを実行すると、以下が行われます:
-
.gitmodulesファイルの更新(submodule設定の追加) - gitのindex(staging area)にgitlinkを追加
gitlinkとは、submoduleが参照するコミットハッシュを格納する特殊なエントリです。git ls-files --stageで確認すると、モード160000で表示されます:
git ls-files --stage projects/new-project
# 160000 abc123... 0 projects/new-project
jjの動作
jjはgitのstagingを使わず、working copy(作業ディレクトリ)の変更を追跡します。
- jjがstagingを見ない理由:jjは独自のコミットモデルを持ち、gitとの互換性のために
.gitを使用するが、stagingは使わない -
git submodule addはstagingにgitlinkを追加するが、jjはこれを認識しない - 結果、
jj commitではgitlinkがコミットされない
図解
┌──────────────────────────────────────────────────────┐
│ git submodule add │
│ ├── .gitmodules (working copy) ← jjが認識 │
│ └── gitlink (staging area) ← jjが認識しない │
└──────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────┐
│ jj commit │
│ └── .gitmodules のみコミット │
│ (gitlinkは含まれない) │
└──────────────────────────────────────────────────────┘
解決方法
正しい手順
submoduleを追加する際は、gitで直接コミットします:
# 1. submoduleを追加
git submodule add https://github.com/user/repo.git projects/repo
# 2. gitlinkがstageされたことを確認
git ls-files --stage projects/repo
# 出力例: 160000 abc123... 0 projects/repo
# 3. .gitmodulesもstageされていることを確認
git status
# Changes to be committed:
# new file: .gitmodules
# new file: projects/repo
# 4. gitで直接コミット
git commit -m "chore: repoをsubmoduleとして追加"
# 5. jjにインポート
jj git import
# 6. push
jj git push
既存の問題を修復する場合
すでに.gitmodulesだけコミットされてしまった場合:
# 1. submoduleディレクトリが存在することを確認
ls projects/repo
# 2. gitlinkを明示的にstage
git add projects/repo
# 警告が出るが無視してよい
# 3. stageされたことを確認
git ls-files --stage projects/repo
# 160000 <commit-hash> 0 projects/repo
# 4. gitで直接コミット
git commit -m "chore: submodule gitlinkを追加"
# 5. jjにインポートしてpush
jj git import
jj git push
参照の更新時も同様
submodule内で更新し、親リポジトリの参照を更新する場合も、gitで直接操作します。
# submodule内で更新をpush済みの状態で
# 1. gitlinkを更新
git add projects/repo
# 2. コミット
git commit -m "chore: repoのsubmodule参照を更新"
# 3. jjにインポートしてpush
jj git import
jj git push
検証方法
submoduleが正しく登録されているか確認
# 方法1: git submodule status
git submodule status
# abc123... projects/repo (heads/main)
# 方法2: git ls-tree
git ls-tree HEAD projects/
# 160000 commit abc123... projects/repo
# 方法3: git ls-files
git ls-files --stage projects/repo
# 160000 abc123... 0 projects/repo
出力があればOK。空の場合はgitlinkがコミットされていません。
追加の問題:jj statusでsubmoduleが「削除」と表示される
submoduleが正しく登録されていても、jj statusを実行すると予期せぬ表示が出ます:
jj st
# Working copy changes:
# D projects/project-a
# D projects/project-b
すべてのsubmoduleがD(Deleted)と表示される!
なぜこうなるか
jjの視点では:
- 親コミット(HEAD)にはgitlinkエントリがある
- しかしjjはgitlinkを認識できない(ファイルではないため)
- 「あったものが消えた」→
D(Deleted)と判定
試した回避策
| 方法 | 結果 |
|---|---|
jj restore |
✗ gitlinkを復元できない |
snapshot.auto-trackで除外 |
✗ 既にHEADにあるgitlinkの「削除」検出は止められない |
.gitignoreに追加 |
✗ gitlinkはファイルではないので除外不可 |
危険な状態
このDマークがついた状態でjj newすると、gitlinkの「削除」がコミットされてしまう可能性があります。つまり、submoduleの参照が消える。
根本的な問題
jjはgitlinkを扱えません。これはjjの設計上の制約で、issue #494で議論されています。現時点ではworkaroundで解決できる問題ではなく、jj本体でのsubmoduleサポートを待つ必要があります。
まとめ
| 操作 | jjで可能か | 備考 |
|---|---|---|
git submodule add |
✗ | gitで実行 |
| gitlinkのコミット | ✗ | gitで直接コミット |
| submodule参照の更新 | ✗ | gitで直接コミット |
| 通常のファイル編集 | ✓ | jjで問題なし |
jj statusの表示 |
✗ | submoduleがDと誤表示される |
jjとgitを併用する環境でsubmoduleを扱う場合、gitlinkに関する操作はgitで直接行う必要があります。
jjはgitのstagingを見ないため、staging経由で追加されるgitlinkはjjでは扱えません。これはjjの設計上の制約であり、バグではありません。
後日談:submoduleをやめてVSCodeワークスペースに移行した
submodule運用の課題
実際にsubmoduleでモノレポ管理を運用してみると、本記事で解説した問題以外にも課題が出てきました。
- 参照更新の手間: submodule内で変更するたびに、親リポジトリでgitlinkを更新してコミットする必要がある
- jjとの相性: 前述の通り、submodule操作はgitで直接行う必要があり、jjのシンプルなワークフローが崩れる
-
クローン時の複雑さ:
--recurse-submodulesオプションが必要で、忘れるとsubmoduleが空になる - IDE連携: 一部のIDEやツールがsubmoduleを正しく認識しないことがある
VSCodeマルチルートワークスペースという選択肢
検討した結果、VSCodeマルチルートワークスペースに移行しました。
# 従来(submodule構成)
parent-repo/
├── .gitmodules
└── projects/
├── project-a/ (submodule)
└── project-b/ (submodule)
# 移行後(ワークスペース構成)
workspace/
├── hub/ # ドキュメント・計画管理
├── project-a/ # 独立リポジトリ
└── project-b/ # 独立リポジトリ
hub/workspace.code-workspace # VSCodeワークスペース定義
ワークスペース形式のメリット
| 観点 | submodule | VSCodeワークスペース |
|---|---|---|
| リポジトリ独立性 | 親に依存 | 完全独立 |
| 参照更新 | 手動で必要 | 不要 |
| jjとの相性 | 一部git直接操作 | 問題なし |
| クローン | --recurse-submodules |
各リポジトリを個別clone |
| IDE統合 | △ | ◎(VSCode前提) |
移行手順
# 1. submoduleを削除
git submodule deinit -f projects/project-a
git rm -f projects/project-a
rm -rf .git/modules/projects/project-a
# 2. .gitmodulesを削除(最後のsubmoduleの場合)
git rm .gitmodules
# 3. ワークスペースファイルを作成
cat > workspace.code-workspace << 'EOF'
{
"folders": [
{ "path": ".", "name": "hub" },
{ "path": "../project-a", "name": "project-a" },
{ "path": "../project-b", "name": "project-b" }
]
}
EOF
# 4. コミット
git add -A
git commit -m "chore: submoduleからワークスペース構成に移行"
どちらを選ぶべきか
submoduleが適している場合:
- 特定のコミットに固定したい(ライブラリのバージョン管理など)
- CI/CDで再現性のあるビルドが必要
- VSCode以外のIDEをメインで使う
ワークスペースが適している場合:
- 各プロジェクトは常に最新を参照したい
- jjをメインで使いたい
- VSCodeで開発している
- プロジェクト間の依存関係が緩い
私の場合、個人プロジェクトの統合管理が目的で厳密なバージョン固定は不要だったため、ワークスペース形式に移行しました。
Discussion