◉CLAUDE LABEN
●2.1.301 — vim の % モーション、VS Code パネルでの LaTeX 数式表示、使用量上限待ちのリトライ監視の上限が入りました(10/15)●2.1.299 — 200K トークン超で約54分放置した会話はアイドル圧縮。MCP の許可ルールは名指ししたサーバーだけに一致します●SONNET 4.5 — Claude API での提供終了は 11/30、残り45日。移行先は Sonnet 5.5●WINDOWS — 日本語の環境でだけフックが黙って壊れる報告があり、文字コードの確認が呼びかけられています●FAST MODE — Opus 5.5 の Fast mode がリサーチプレビューを終え、ベータヘッダーが不要になりました(10/15)●NEW — Cowork に渡したフォルダの先頭に、十行の約束ファイルを置いてみました●2.1.301 — vim の % モーション、VS Code パネルでの LaTeX 数式表示、使用量上限待ちのリトライ監視の上限が入りました(10/15)●2.1.299 — 200K トークン超で約54分放置した会話はアイドル圧縮。MCP の許可ルールは名指ししたサーバーだけに一致します●SONNET 4.5 — Claude API での提供終了は 11/30、残り45日。移行先は Sonnet 5.5●WINDOWS — 日本語の環境でだけフックが黙って壊れる報告があり、文字コードの確認が呼びかけられています●FAST MODE — Opus 5.5 の Fast mode がリサーチプレビューを終え、ベータヘッダーが不要になりました(10/15)●NEW — Cowork に渡したフォルダの先頭に、十行の約束ファイルを置いてみました
記事一覧/Claude Code
⟐ Claude Code/2026-06-10中級

git add -A が .bak バックアップまで巻き込む — in-place 修正スクリプトと自動コミットの落とし穴

sed -i や自作の --fix スクリプトが残す .bak バックアップを git add -A が無言で取り込み、本番リポジトリに混入させてしまう問題。なぜ気づきにくいのか、scoped add と .gitignore でどう塞ぐかを実例で整理します。

troubleshooting61error15fix11git17claude-code132automation55

ある朝、3つのリポジトリの差分を見返していて、身に覚えのないファイルが並んでいることに気づきました。next.config.ts.bak。コミット履歴をたどると、前夜のリダイレクト整合性チェックを一括で走らせた処理が、.ts 本体と一緒にそのバックアップまで commit して push していたのです。

ビルドは通っていました。サイトも動いていました。だからこそ、誰も止めてくれませんでした。

私は2014年から個人開発でアプリとブログを並行運用していて、Dolice Labs では4つの技術ブログを Claude Code 経由のスクリプトで毎日更新しています。整形やリンク修正のような「機械的だけど壊れると怖い」処理を自動化していくと、この種の静かな混入が一番厄介になります。手を動かして直すミスより、自動化が淡々と運んでしまうゴミの方が見つけにくいのです。

何が混入したのか — --fix が残すバックアップ

きっかけは、自作の整合性チェッカーでした。削除済み記事へのリダイレクトと、復活した記事の MDX が衝突していないかを検査するスクリプトに、--fix オプションを付けて自動修正させていました。

このスクリプトは、書き換える前の next.config.ts を next.config.ts.bak という名前でリポジトリ直下に退避します。安全のためのバックアップです。問題は、その直後のコミット処理がこうなっていたことでした。

# 自動化パイプラインの末尾でやっていたこと
python3 redirect_integrity.py "$REPO" --fix
git add -A          # ← ここで .bak まで巻き込む
git commit -m "Fix redirect integrity"
git push origin main

git add -A は「作業ツリーの変更すべて」をステージします。新規作成された next.config.ts.bak も当然「新しいファイル」なので、何の警告もなく追加されます。.bak は Next.js のビルドに無害なので、CI も Cloudflare も赤いランプを出しません。結果として、gemilab・antigravitylab・rorklab の3リポジトリにバックアップファイルが居座ることになりました。

なぜ気づきにくいのか

このバグの本質は、「壊れないこと」にあります。

.bak ファイルが1つ増えても、サイトは普通に表示されます。テストも通ります。エラーログにも何も出ません。リポジトリがほんの少し汚れるだけで、その汚れは次に同じ --fix を走らせたとき上書きされるだけで、自然には消えません。静かに溜まっていきます。

しかも .bak は本体とほぼ同じ内容なので、後から差分を眺めても「これは何だろう」と引っかかりにくいのです。私が気づけたのは、たまたま3リポを横断して差分を見比べていて、同じ名前のファイルが3つ並んでいたからでした。

自動化において、終了コードが 0 でログが緑であることは「正しさ」を意味しません。git push がエラーを出さないことと、リポジトリが意図通りであることは別の問題です。同じ「無音の失敗」という観点では、git push が成功表示なのに反映されない罠 も根が同じで、自動化では「緑のログ」こそ疑う対象になります。

再現してみる

手元で簡単に再現できます。

mkdir -p /tmp/bak-demo && cd /tmp/bak-demo
git init -q
echo "export const config = {}" > next.config.ts
git add next.config.ts && git commit -qm "init"
 
# in-place 修正ツールが .bak を残す典型例
sed -i.bak 's/{}/{ trailingSlash: true }/' next.config.ts
ls
# → next.config.ts  next.config.ts.bak
 
git add -A
git status --short
# A  next.config.ts.bak   ← 意図せずステージされている
# M  next.config.ts

sed -i.bak は macOS でよく使う書き方で、これも .bak を必ず残します。GNU sed の sed -i(拡張子なし)はバックアップを作りませんが、BSD/macOS の sed -i '' を書き間違えて sed -i.bak になっているケースは現場で本当によく見かけます。--fix 系の自作スクリプトも、安全のためにバックアップを書くものが多く、同じ罠を踏みます。

対処 — ステージ対象を「足し算」で決める

一番確実なのは、git add -A をやめて、コミットしたいものだけを明示的に列挙することです。

# Before: 全部入り(.bak も巻き込む)
git add -A
 
# After: 触ってよいパスだけを足し算で指定する
git add next.config.ts content/
git status --short   # 余計なものが入っていないか目視
git commit -m "Fix redirect integrity"

ブログ更新のパイプラインなら git add content/ のように、生成・編集する対象ディレクトリだけを指定します。設定ファイルを触る処理なら、そのファイル名を直接書きます。「全部追加してから余計なものを外す」より、「必要なものだけ足す」方が事故が起きません。

それでも git add -A を使いたい事情があるなら、修正ツールの直後にバックアップを掃除してからステージします。

python3 redirect_integrity.py "$REPO" --fix
rm -f "$REPO"/next.config.ts.bak   # 退避ファイルを先に消す
git add -A

再発防止 — .gitignore で構造的に締める

掃除や限定 add は「忘れたら破れる」運用ルールです。人間が手で回すなら覚えていられますが、毎日走る自動化では一度抜けると静かに復活します。構造で止めるのが本筋です。

リポジトリの .gitignore にバックアップ系の拡張子をまとめて入れておけば、git add -A を使っても二度とステージされません。

# in-place 修正ツールが残すバックアップ
*.bak
*.orig
*.tmp
*~

これを4リポジトリすべてに同じ内容で入れておくと、どのスクリプトがどんなバックアップを残しても、コミット対象から自動的に除外されます。.gitignore の挙動でつまずいた経験がある方は、Claude Code で .gitignore のファイルが見つからないと言われる問題 も合わせて読むと、無視ルールと検索対象の関係が整理できます。

もう一段の保険として、ステージ後・コミット前に「想定外のファイルが混ざっていないか」を機械的に弾くガードを入れておくと安心です。自動化パイプラインの末尾にこの数行を置くだけで、.bak のような退避ファイルが紛れ込んだ瞬間に処理を止められます。

# コミット直前: バックアップ系がステージされていたら中断する
if git diff --cached --name-only | grep -qE '\.(bak|orig|tmp)$|~$'; then
  echo "🛑 退避ファイルがステージされています。コミットを中止します。"
  git diff --cached --name-only | grep -E '\.(bak|orig|tmp)$|~$'
  exit 1
fi
git commit -m "Fix redirect integrity"

人の目が入らない処理ほど、こうした「最後の関所」が効きます。私はこの数行を入れてから、同じ混入を一度も繰り返していません。

すでに混入してしまった .bak は、.gitignore を足しただけでは消えません。追跡対象から外す操作が別途必要です。

git rm --cached next.config.ts.bak   # 履歴は残しつつ追跡解除
git commit -m "Remove stray backup file from tracking"

自動化では「足し算 add」を既定にする

今回の混入は、たった1つのコミットコマンドの書き方から生まれました。git add -A は対話的に使うぶんには便利ですが、人の目が入らない自動パイプラインでは、対象を明示する git add <path> を既定にすべきだと考えています。コミットメッセージを Claude Code に生成させるような仕組みを組むときも、何をステージするかは自分で握っておくのが安全です(その設計はコミットメッセージ自動生成の運用で触れています)。

自動化は、正しい手順を黙々と繰り返してくれます。裏を返せば、間違った手順も黙々と繰り返します。何をリポジトリに入れるかという判断だけは、機械任せにせず手元に残しておきたいところです。

同じ課題に向き合っている方の参考になれば幸いです。

シェア

お読みいただきありがとうございます

Claude Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • ✦コピー&ペーストで使える実装コード付き
  • ✦毎日新しい上級ガイドを追加
  • ✦¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

もしこの記事がお役に立ちましたら、チップ(¥150)で応援いただけると大変励みになります。広告なしでの運営を続けるため、皆さまのご支援が大きな力になっています。

関連記事

⟐ Claude Code2026-05-02
Claude Code の Edit ツールで「old_string is not unique」エラーを止める実践的な書き換え戦略
Claude Code の Edit ツールで「old_string is not unique」が頻発するのは、編集ターゲットの一意性をプロンプト側で保証していないことが根本原因です。再発を防ぐ書き換え戦略を解説します。
⟐ Claude Code2026-05-24
Claude Code で連続する Edit が連鎖的に失敗するときの直し方 — old_string が消える・重複する問題
Claude Code が同じファイルへ Edit を連続で投げると、2件目以降が突然『not found』や『not unique』で落ちることがあります。原因は順序依存とコンテキスト二重化で、Read 再実行と分割パッチで救えます。
⟐ Claude Code2026-05-10
Claude Code の Glob と Grep が0件を返すときの原因切り分け
Claude Code の Glob/Grep ツールがファイルを見つけられないとき、.gitignore除外・隠しファイル・パターン記法の落とし穴を切り分け、適切な代替手段で確実に発見する手順をまとめます。