1
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確認コマンドで誤判定する原因と対処

1
Posted at

📝 この記事は forge.workstyle.tech に掲載した記事の転載です。

シェルスクリプトやAIエージェントにgitの状態を確認させたとき、表示だけを見て逆の結論を出してしまうことがあります。シェルはコマンドを実行するための環境で、grepはファイルから文字列を探すコマンドです。私は使い捨てのリポジトリで、rev-parse、grep、stash、git statusの確認で起きることを再現しました。この記事では、出力だけに頼らず結果を確かめる方法を紹介します。

先に結論

gitの確認では、出力が空でないことや件数だけでは、対象の有無やコマンドの成功を判断できない場合があります。終了コードと実行されたコマンド、比較しているパスを確かめ、時刻を証拠にする場合はGIT_OPTIONAL_LOCKS=0を付けて状態を確認します。

rev-parseで対象の存在をどう確かめるか

git rev-parse(Gitの名前やパスを解決するコマンド)の出力が空でないことだけでは、指定したパスが存在するとは判定できません。次の例では、HEAD(現在のコミットを指す名前)に存在しないパスを指定しています。

$ v=$(git rev-parse "HEAD:存在しないpath" 2>/dev/null); echo "値=[$v] exit=$?"
値=[HEAD:存在しないpath] exit=128

解決できなかった引数が標準出力にそのまま出ています。終了コード(コマンドの成否を示す数値)は128ですが、変数vは空ではありません。そのため、[ -n "$v" ]だけで判定すると「在る」と読んでしまいます。

存在を確かめるには、出力の有無ではなくコマンドの終了コードを見ます。

git cat-file -e <rev>:<path>

記録した再現では、存在しない場合の終了コードは128、存在する場合は0でした。ツリーの内容を一覧にして確認するなら、git ls-treeを使います。大事なのは、何か出力されたかではなく、指定した対象を解決できたかです。

grepが0件やエラーに見えるとき何を調べるか

grepの件数が0に見えたりエラーになったりしたら、実際に何が呼ばれているかを確認します。ある環境では、grepが別のgrep実装を呼ぶシェル関数で上書きされていました。

$ grep -c alpha s.txt
error: unknown option '-G'
exit=1
$ command grep -c alpha s.txt
1
exit=0

ファイルにはalphaが1件あります。しかし最初の呼び出しはエラーになり、終了コードも1です。終了コード1を「0件」と読めば、中身があるのに見逃します。確認のためのコマンドが、確かめたい状態を隠してしまいます。

何が呼ばれるかは、次のコマンドで確かめられます。

type grep

シェル関数ではなく通常のコマンドを呼ぶには、command grepを使う方法があります。絶対パスで呼び出してもかまいません。件数を見る前に、実行したgrepが想定したものかを確認します。

stashの差を比べるとき何を数えるか

stash(作業内容を一時保存するGitの機能)を確かめるときは、比較する対象を言葉にします。stash@{0}はstashした時の作業ツリー(作業中のファイル一式)です。stash@{0}^2はその時のindex(ステージした内容)、stash@{0}^1はHEAD、stash@{0}^3は未追跡ファイルです。

ある比較では、ツリー全体に3ファイル・434行の差がありました。そこから「stashは控えにならない」と結論しました。しかし、その3ファイルは確認対象の20件に含まれていませんでした。

パスごとにファイルの内容を比較し直すと、20件の内訳は同一が15件、両方で削除が5件、食い違いは0件でした。対象の20件については、stashが完全な控えでした。

数字が違っていたのは、同じ対象を数えていなかったからです。差の大きさを見直す前に、比較対象の範囲を突き合わせます。「何と何の、どのパスを数えているか」を先に言葉にすると、数字の意味を取り違えにくくなります。

git statusの後に時刻が変わることはあるか

記録した環境では、読み取りだけのつもりで実行したgit statusがindexのmtime(ファイルの更新時刻)を書き換えました。

時刻が証拠になる場面では、通常のgit statusではなく次を使います。

GIT_OPTIONAL_LOCKS=0 git -C <path> status --porcelain

「状態を表示するコマンドだから、何も書き換えない」とは限りません。確認したあとに時刻を証拠として扱うなら、確認コマンド自身が時刻に影響しないかも考えます。

同じことで困ったら

gitやシェルの確認結果に迷ったら、次の順で確かめます。

  • 何のパスやファイルを確認したいかを決める。
  • 出力だけで決めず、終了コードも確認する。
  • grepのようなコマンドが関数で置き換わっていないか、type grepで調べる。
  • 比較では、対象のパスと件数の範囲を揃える。
  • 時刻を証拠にするなら、GIT_OPTIONAL_LOCKS=0 git -C <path> status --porcelainで状態を確認する。

元記事: https://forge.workstyle.tech/blog/git-checks-that-mislead/?utm_source=qiita&utm_medium=crosspost&utm_campaign=git-checks-that-mislead

1
0
1

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
1
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?