0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

git worktree を削除したら gitignore 済みの夜間バッチデータが消えた——削除前チェックとリポジトリ外保存の設計

0
Posted at

環境情報

  • Git: 2.43.0
  • OS: Ubuntu 22.04 LTS / macOS 14.5
  • GUI: Fork 2.x, SourceTree 4.x, VS Code 1.90
  • バッチ: Python 3.11 + cron / systemd timer
  • 保存形式: Parquet / CSV / SQLite
  • データ量: 1晩あたり 200MB、数晩で 1GB 超
  • 元の保存先: <worktree>/data/(.gitignore 済み)
  • 移行後: /var/lib/myapp/data または $MYAPP_DATA_DIR

結論

  • .gitignore は「Git 管理から外す」だけで「削除から守る」ではない。
  • worktree 削除はディレクトリを丸ごと消す。ignored ファイルは警告に出ないことがある。
  • 削除前に git status --ignored と git log origin/main..HEAD を見る。
  • データはリポジトリ外の固定パスに置き、環境変数で上書き可能にする。
  • リポジトリ内に置きたいなら symlink を使い、実体を外に出す。

何が起きたか

夜間バッチで集めたデータを、git worktree 内の data/ に置いていました。data/ は .gitignore 済みです。GUI で作業済みワークツリーを削除した瞬間、データごと消えました。

盲点は「Git の管理外=削除対象」です。削除確認には「未コミットの変更なし」としか出ず、ignored ファイルが消えるとは示されません。数晩分の収集が一度で飛びました。

再現手順

  1. worktree を作成する。
git worktree add ../worktrees/collector -b feature/collector
cd ../worktrees/collector
mkdir data
echo '/data/' >> .gitignore
  1. 夜間バッチが data/ に出力する。
python collect.py --out data/$(date +%F).parquet
  1. 状態を確認する。git status ではクリーンに見える。
git status
# On branch feature/collector
# nothing to commit, working tree clean

git status --ignored --short
# !! data/
  1. GUI で worktree を削除する。data/ も消える。ダイアログは「未コミットの変更なし」だけ。

なぜ見落とすのか

git status は ignored を表示しません。git clean -ndx も ignored を削除候補に出します。worktree は別ディレクトリなので、削除はそのディレクトリの rm -rf に近い動きになります。

削除前に見るべきコマンド

# ignored を含む全ファイル
git status --ignored --short

# ignored ファイルだけを一覧
git ls-files --others --ignored --exclude-standard

# サイズも確認
git ls-files --others --ignored --exclude-standard -z \
  | xargs -0 -r du -sh

# main に未マージのコミット数
git rev-list --count origin/main..HEAD

# 未マージコミット一覧
git log --oneline --decorate origin/main..HEAD

特に git ls-files --others --ignored --exclude-standard は、.gitignore で隠れたデータを明示できます。data/ が出たら削除前に退避します。

安全な削除ラッパー

#!/usr/bin/env bash
set -euo pipefail

MAIN_REPO="/path/to/main-repo"
WT="$1"

if [ ! -d "$WT" ]; then
  echo "worktree not found: $WT" >&2
  exit 1
fi

ignored="$(git -C "$WT" ls-files --others --ignored --exclude-standard || true)"
if [ -n "$ignored" ]; then
  echo "WARNING: ignored files exist in $WT" >&2
  echo "$ignored" | sed 's/^/  /' >&2
  if [ "${ALLOW_IGNORED_DELETE:-0}" != "1" ]; then
    echo "Set ALLOW_IGNORED_DELETE=1 to delete anyway." >&2
    exit 2
  fi
fi

unmerged="$(git -C "$WT" rev-list --count origin/main..HEAD || echo 0)"
if [ "$unmerged" -gt 0 ]; then
  echo "WARNING: $unmerged commits are not merged into origin/main" >&2
  git -C "$WT" log --oneline origin/main..HEAD >&2
  if [ "${ALLOW_UNMERGED_DELETE:-0}" != "1" ]; then
    exit 3
  fi
fi

git -C "$MAIN_REPO" worktree remove --force "$WT"

データをリポジトリ外に移す

export MYAPP_DATA_DIR="${MYAPP_DATA_DIR:-/var/lib/myapp/data}"
mkdir -p "$MYAPP_DATA_DIR/collector"
python collect.py --out "$MYAPP_DATA_DIR/collector/$(date +%F).parquet"

cron なら次のようにします。

MYAPP_DATA_DIR=/var/lib/myapp/data
0 2 * * * MYAPP_DATA_DIR=$MYAPP_DATA_DIR /opt/myapp/collect.sh

systemd なら次のようにします。

[Service]
Environment=MYAPP_DATA_DIR=/var/lib/myapp/data
ExecStart=/opt/myapp/collect.sh

この設計なら worktree を消してもデータは残ります。

リポジトリ内のパスを維持したい場合

実体を外に置き、worktree 内は symlink にします。

mkdir -p /var/lib/myapp/data
ln -s /var/lib/myapp/data ./data
echo '/data' >> .gitignore

worktree 削除で消えるのは symlink だけです。ただし git clean -fdx やバックアップツールが symlink 先を追う場合があるので注意します。

削除方法ごとの危険度

方法 tracked 変更 untracked ignored 危険度
git status だけ見て GUI 削除 警告 警告あり 警告なしの可能性 高
git worktree remove 拒否 拒否 バージョン依存 中
git worktree remove --force 無視 無視 削除 高
rm -rf <worktree> 無視 無視 削除 最高
リポジトリ外保存 + 削除前チェック 影響なし 影響なし 影響なし 低

ハマりポイント

  • .gitignore に入れたから安全、は誤り。管理外は削除対象。
  • git status がクリーンでも ignored が存在する。
  • GUI の確認ダイアログは ignored を表示しない製品がある。
  • git clean -fdx は ignored も削除する。-n で必ず予行演習する。
  • worktree の削除は main repo 側から実行する。worktree 内から git worktree remove . はできない。
  • origin/main..HEAD は main 未マージ確認。リモート名が upstream なら読み替える。

FAQ

Q. .gitignore に入れれば削除されない?
A. されないとは言えない。Git 管理外なので、worktree 削除や rm -rf では普通に消える。

Q. git status でクリーンなのに?
A. ignored はデフォルト非表示。git status --ignored を使う。

Q. 消したデータは Git から復元できる?
A. できない。Git 管理外だから。OS のゴミ箱、Time Machine、クラウドバックアップ、ディスク復元ツールを確認する。ただし確実性は低い。

Q. データをリポジトリ内に置きたい。
A. symlink か bind mount で実体を外に出す。CI では artifact や object storage に逃がす。

Q. 削除前チェックを自動化するには?
A. 上記ラッパーを git wt-safe-remove として alias 化し、GUI 削除の代わりにそれを使う。CI では git status --ignored --porcelain を検査する。

まとめ

作業ディレクトリを消す前に、Git が無視しているファイルの中身を確認する。削除前チェックでは、まず git status --ignored、次に git ls-files --others --ignored --exclude-standard、最後に origin/main..HEAD を見る。データはリポジトリ外の固定パスに置き、環境変数で上書き可能にする。これで数晩分の取得データが一瞬で飛ぶ事故を防げる。


この記事を書いた人

BENTEN Web Works — 業務自動化・システム開発のフリーランスエンジニアです。

GAS / Python / RPA を使った業務自動化や、Web制作・システム開発のご相談を承っています。
「こんなこと自動化できる?」というご質問だけでもお気軽にどうぞ。

👉 業務自動化サービス — 詳細・お問い合わせはこちら
🐦 X(旧Twitter) — 日々の知見を発信中

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?