こんにちは、エンジニアのオオバです。
Mac mini で常駐させている自作のマスター管理画面には、git の状態を見る「gitバー」があります。
そこに Pull / Push ボタンを置いているんですが、これがたびたび詰んでいました。
Pull を押すと失敗する。
Push を押すと「origin より遅れているので先に Pull してください」と止められる。
つまり画面からは何もできなくなるということ。
Pullをstash経由の取り込みに変え、ぶつかり方が決まった競合だけ自動で解消し、残りは「AIで解決」ボタンでClaude Codeに直させた
先に結論をまとめます。
①Pullは未コミットをstashしてから、fast-forwardかrebaseで取り込み、stashを戻す流れに変える
②ぶつかったら、型が決まっているものだけ自動で解消する。解消できなければ元の状態まで完全に巻き戻して失敗にする
③それでもダメなときは「AIで解決」ボタンで claude -p に直させる。許可する道具を絞り、終わったら機械で検証する
ここから、原因と解決を順番に書いていきます。
→11万文字で徹底解説した「DOTweenの教科書」Unityアニメーションの超効率化ツールはこちら
起きたこと:PullもPushも通らない
詰んでいたクローンの状態は、develop が 6 コミット遅れていて、未コミットのファイルが 8 つという状況でした。
管理画面は、普段から人間(僕)が画面上で編集する場所です。
そのため、未コミットの編集がたまっている状態は日常です。
ここに、別の場所から develop へ入ってきた更新を取り込もうとすると、冒頭の詰みが起きます。
原因:Pullの作りとPushの拒否が噛み合っていた
原因は2つに分かれていました。
PullがFast-forward専用だった
もともとの Pull は、次のコマンドでした。
git merge --ff-only @{u}
取り込む更新と同じファイルを、手元で未コミットのまま編集していると、git は「Your local changes would be overwritten」と言って拒否します。
そりゃそうですよね。
未コミットの編集は git が守ってくれないので、上書きを避けるために止まるしかないんです。
Pushは遅れていると拒否される
一方の Push は、リモートより遅れていると拒否されます。
Pull が通らないので遅れは解消されず、Push も通らない。
この2つが噛み合って、画面からは打つ手がなくなっていました。
「たびたび」の正体は翻訳CSVのid衝突
今回ぶつかっていたのは、翻訳の CSV です。
新しいキーを足すとき、id を「いまの最大値 + 1」で振る仕組みになっています。
そのため、管理画面側と develop 側が同じ時期に新しいキーを足すと、どちらも同じ id 603 になります。
diff3 形式で競合を見ると、base(共通の祖先)が空で、両方が末尾に追記した形でした。
これが、たびたび詰んでいた正体です。
ちなみに、片方が足した行が消えるわけではないので、本来は「両方残す」が正解。
ただし id だけは重複してはいけないので、振り直しが要ります。
解決1:Pullをstash経由の取り込みに変える
Pull の流れを、次のように変えました。
- 未コミットの変更を stash する(あとで確実に戻せるよう、SHA で識別する)
- fast-forward か rebase で上流を取り込む
- stash を apply する
stash を名前でなく SHA で扱うのがポイントで、ほかの stash が積まれていても自分の分だけを確実に戻せます。
解決2:ぶつかったら決まった型だけ自動で解消する
stash を戻したときに競合が出たら、型が決まっているものだけ自動で解消します。
- CSV から作れる生成物の JSON は、上流側(stage 2)を採って作り直す
- CSV は、diff3 の base が空のブロックだけ両方残す
- 翻訳 CSV は、自分側の行の id を「上流の最大 + 1」から振り直す
- キャラ ID のように意味を持つ id は振り直さない
- CRLF を壊さないよう、読み書きは
newline=""で行う
最後の CRLF は地味ですが、効きます。
改行コードを意図せず変えてしまうと、全行が差分になってしまうからです。
型に当てはまらないときは、無理に解消しません。
HEAD と未コミットの編集を完全に巻き戻して、失敗として返します。
マーカー(<<<<<<< など)が入った中途半端な状態を、残さないためです。
解決3:Pushは遅れていたら取り込んでから送る
Push は、遅れていたら同じ取り込み処理を走らせてから push するようにしました。
Pull と Push で別々のロジックを持たないので、詰みの片方だけが残ることがありません。
それでもダメなときの「AIで解決」ボタン
型にはまる競合は自動で解消できるようになりました。
とはいえ、現実のgitの状態は型にはまらないことがあります。
そこで、最後の逃げ道として「AIで解決」ボタンを足しました。
gitバーのブランチ名の左にあるボタンです。
Pull / Push が失敗した直後は、紫に光ります。

押すと、Mac mini の Claude Code(claude -p、サブスクの枠)が git の状態を直します。
とはいえ、AI に git を直接触らせるのは怖いですよね。
「git push --force されたらどうしよう」と思うかも知れません。
なので、安全のための仕掛けを3つ入れました。
事前にバックアップを取る
AI を呼ぶ前に、未コミットの差分と未追跡ファイルをバックアップします。
最悪、何が起きても手元の編集は戻せます。
使える道具を絞る
--allowedTools で、使えるのは git / python3 / Edit だけにしました。
さらに --disallowedTools で、次の操作を道具ごと禁止しています。
- force push
git branch -Dgit cleangit stash drop
お願いで止めるのでなく、呼べない状態にしておくのがコツです。
終わったら機械で検証する
AI が「直りました」と言っても、そのまま信じません。
次の項目を、コードで機械的に確かめます。
- rebase の途中で止まっていない
index.lockが残っていない- 競合マーカーがファイルに残っていない
- リモートより遅れていない
- 未 push のコミットがない
試してダメだったこと
最初の試走では、2つ詰まりました。
index.lockを消す許可が効かなかった
Bash(rm -f .git/index.lock) を許可しておいたのに、Claude Code が .git の中の rm を拒否しました。
すると AI は「拒否されたものを別の方法で消すのは控えます」と言って止まったんです。
偉い。
正直、ここは感心しました。
ただ、これでは直らないので、ロックの削除は AI に頼まず、ボタンを押したときの呼び出し側で決定的に行う形に変えました。
条件は「60 秒以上古く、かつ git のプロセスが動いていないとき」だけです。
安全のためのstashが残った
AI が安全のために作った stash が、そのまま残ってしまいました。
git stash drop を禁止しているので、AI 自身は消せないからです。
そこで、指示を「git stash pop で戻すこと」に変えました。
結果:3秒で取り込み、AIは130秒で解決
実データで試した結果です。
- id 603 の衝突は、約3秒で取り込み完了。603 は上流側に残し、自分側は 604 / 605 に振り直した
- 同じ行の別の列を双方が書き換えていて、未 push のコミットと古い
index.lockもある、という複雑なケースは、AI が130秒で両方残して解決し、push まで終えた
自動マージで済む分は3秒、型に外れた分は AI に2分ちょっと。
この使い分けが、いまのところちょうどいい感触です。
学び:決まった型は機械に、残りはAIに
今回の整理を一言でいうと、型が決まっている競合はコードで、決まっていない競合は AI にということです。
しかも、AI には「何でもできる」権限を渡さず、道具を絞ったうえで、結果を機械で検証する。
これだけで、管理画面から git を触るのが怖くなくなりました。
まとめ
- Pull の
--ff-onlyは、未コミット編集と取り込みがぶつかると拒否される。Push は遅れていると拒否される。この2つで詰む - 翻訳 CSV の id を「最大 + 1」で振っていると、双方が同時に足したときに同じ id になる。これが「たびたび」の正体
- Pull は stash(SHA 識別)→ ff か rebase → stash apply にし、型が決まった競合だけ自動解消する。解消できなければ完全に巻き戻して失敗にする
- 最後の逃げ道に「AIで解決」ボタン。バックアップ、道具の許可と禁止、終了後の機械検証の3点セットで安全側に倒した
- ロックファイルの削除のような危ない操作は、AI に許可を出すより、呼び出し側で条件つきで決定的に行う
同じように「画面から git が詰む」ことがある人は、まず Pull を stash 経由にしてみてください。
一緒に、詰みのない管理画面を作っていきましょう。

この記事が気に入ったらフォローしよう
- macOS / git 2.50 / Python 3.14(Mac mini)/ Claude Code Opus 5.5
