git worktree で共有されるものと分かれるもの

結論 linked worktree は、object・ブランチ (refs/heads/) とその reflog・.git/config を本体と共有する。HEAD とその reflog・index・作業ツリーは worktree ごとに持つ 本体か worktree かは、--git-dir と --git-common-dir を --path-format=absolute で絶対パスに揃えて比べる。揃えずに比べると、本体のサブディレクトリで worktree と判定される 1 つのブランチは 1 つの worktree でしか checkout できず、git worktree list --porcelain の先頭は本体になる。ブランチ名から worktree を引くと、本体のパスが返ることがある 新しい worktree に入るのは追跡中のファイルだけ。ignored の .env や node_modules は無く、パスに紐づく仕組み (node のモジュール解決、direnv の許可) も本体とは別の場所として扱う worktree で作ったコミットは、worktree とブランチを消すと reflog からも消える。gc の前なら git fsck --lost-found で拾える 以下は git 2.56.0 / GNU bash 5.3.20 / Linux x86_64 で実測した。出力の <root> は再現に使った一時ディレクトリで、コマンドのパスは <root> からの相対に縮めて書いた。 ...

2026-11-07 ·  2026-11-07 · 12 分 · 2333 文字

Terraform が張る依存と張らない依存

結論 Terraform がリソース間の依存を張るのは、式が別のリソースや module の output を参照したときと、depends_on を書いたときだけ。同じ値を文字列や root の変数で渡しても依存はできず、親と子は並行に作られる 依存が無くても、親が既にある環境では通る。落ちるのはまっさらな環境への初回 apply だけで、Firestore の Field が 404 になったり、API の有効化より先にリソースの作成が走ったりする depends_on = [module.x] は x の全リソースを待つ。x の中に自分の出力を使うリソースがあると、依存が一周して cycle (循環参照) になり、Error: Cycle で落ちる。x の output を書けば、その output が参照するリソースだけを待つ 設定を持つ provider ブロックを中に書いた module には、depends_on (と count / for_each) を付けられない。provider は root へ上げる module の depends_on は、依存先に未適用の変更がある plan で、module 内の data source の読み込みを apply 時まで遅らせる。その値を ForceNew の属性に渡すと、何も変わっていなくても replace が出る 以下のコードと出力は Terraform v1.15.8 で確かめた。使ったのは terraform init -backend=false と terraform validate だけで、plan / apply は打っていない。 GCP の API を呼ぶ節と data source の節は、作業ログの記録をもとに書いた。 ...

2026-11-06 ·  2026-11-06 · 6 分 · 1254 文字

bash の trap で書く後始末と終了コード

結論 trap は渡した文字列を登録し、発火した時点でコードとして読み直す。単引用符なら変数は発火時の値になり、二重引用符なら張った時点の値が構文の一部として埋まる EXIT trap が発火するのはシェルの終了時で、張った関数の中ではない。その関数の local 変数はもう無いので、値は printf -v q '%q' で埋めるか、グローバル変数に置く EXIT trap は SIGINT・SIGTERM・SIGHUP で止められても走り、SIGKILL では走らない。trap cleanup EXIT INT と INT も並べると、Ctrl-C の後もスクリプトが先へ進む trap はシェルに 1 つの表で、関数ごとの範囲を持たない。ライブラリの trap - EXIT は呼び出し元の EXIT trap も消し、関数で張った RETURN trap は呼び出し元の return でも発火する set -e の下では、EXIT trap の中の失敗が終了コードを書き換え、trap の残りの行を飛ばす。set -u だけなら trap は止まるが、終了コードは残る 以下は GNU bash 5.3.20 / Linux x86_64 で実測した。 前提: trap は「後で読み直す文字列」をシェルの表に登録する trap '<文字列>' EXIT は、その場では何も実行しない。文字列をシェルの表に登録し、発火したときに新しいコマンドとして読み直す。 表はシェル (プロセス) に 1 つで、シグナル (EXIT・RETURN を含む) ごとに 1 本の文字列を持つ。同じシグナルに張り直すと、前の文字列は置き換わる 登録した文字列は trap -p <シグナル> で読める 張った時点と発火した時点では、変数の値も、呼び出し中の関数も、表の中身も変わりうる。以下の罠はどれもこのずれから起きる set -e 自体の効き方は bash の set -e が効かない場所と効きすぎる場所 に、 ERR trap がパイプの失敗で発火しない件は trap コマンドは pipe と併用すると動作しない に書いた。 ...

2026-11-05 ·  2026-11-05 · 9 分 · 1783 文字

bash のパイプが返す終了コードと SIGPIPE

結論 パイプの終了コードは、既定では末尾のコマンドのもの。set -o pipefail を立てると、最後に非 0 で終わったコマンドのものになる || と if はパイプ全体の終了コードを見る。cmd | grep -v X || true は cmd の失敗も 0 にし、if cmd | grep -q X は取得の失敗を「一致なし」と同じ偽にする 読み手が終わった後に書き手が書くと、書き手は SIGPIPE で 141 になり、pipefail がそれを全体の終了コードにする。| head、| grep -q、stdin を読まない読み手で起き、出力が小さくても書く順番しだいで起きる mapfile < <(cmd) はパイプではないので、cmd の失敗はどこにも残らない 判定に使う出力は先に変数へ取り、here-string で渡す。fallback 付きのコマンドは、成功したときだけ stdout に出す 以下は GNU bash 5.3.15 / GNU coreutils 9.11 / GNU grep 3.12 / Linux x86_64 で実測した。回数を書いたものはタイミングで決まるので、環境によって数字が変わる。 前提: パイプの終了コードはどのコマンドのものか ...

2026-10-27 ·  2026-10-27 · 12 分 · 2495 文字

Go の http.Client が送り直す要求と返すエラー

結論 http.Client は 301 / 302 / 303 の転送に従うと、DELETE・PUT・POST を本文なしの GET にして転送先へ送る。DELETE への 302 が一覧へ転送されると、一覧の GET が 200 を返し、何も消さずに成功として返る。307 / 308 はメソッドと本文を保つ 再利用した接続が応答の前に切られると、HTTP/1.1 の Transport は GET などと Idempotency-Key 付きの要求だけを、新しい接続で送り直す。HTTP/2 では GET でも送り直さず、connection reset by peer を返す 本文を送り直せるのは GetBody があるときだけで、http.NewRequest が GetBody を付けるのは *bytes.Buffer / *bytes.Reader / *strings.Reader だけ。それ以外の io.Reader では、307 は転送されずにそのまま返り、読み切った要求をもう一度 Do すると空の本文がエラーなしに届く HTTP/2 の接続は MaxIdleConnsPerHost の数を超えても閉じられない。取り置き 2 本の設定で、張った 8 本を残して使い回した。MaxConnsPerHost は同時に張りに行く接続を絞るが、HTTP/2 の接続の数の上限にはならない (サーバの同時ストリームが 2 のとき、1 にしても 4-6 本張った) 失敗は *url.Error で返り、Error() にクエリ込みの URL が入る。%w で包んでも、文字列にすれば URL が出る。接続リセットも接続拒否も net.Error を満たすので、タイムアウトかどうかは Timeout() で分ける 以下は go1.26.4 / linux/amd64 で実測した。サーバは net/http/httptest で手元に立て、HTTP/2 は EnableHTTP2 と StartTLS で話させた。各節のコードは main.go 1 つで、go run で動く。 ...

2026-10-26 ·  2026-10-26 · 10 分 · 1942 文字