1 ポイント 投稿者 GN⁺ 2025-01-20 | 1件のコメント | WhatsAppで共有
  • Git の help.autocorrect=1 は、タイプミスしたコマンドを修正したあと 0.1秒 だけ待って実行するため、ユーザーが確認したり取り消したりするには、実質的に即時実行に近い
  • この挙動は Git のデフォルトではなく、既定の設定では不明なコマンドは実行されず、似たコマンドだけを表示して終了する
  • 2008年の議論で help.autocorrect はブール値ではなく decisecond 単位の待機時間 に変わり、1true ではなく 0.1秒待機として解釈されるようになった
  • 現在の help.autocorrect0、正の数、immediatepromptnever をサポートしており、確認してから実行したいなら prompt のほうが安全な選択肢である
  • Git のコマンド推定は修正された Levenshtein distance ベースで、Scott Chacon は 1 とブール文字列をより自然に扱うパッチを提案している

git pushygit push として実行されるまで

  • @dhhgit pushy と入力したとき、Git はこれを git push と推定し、実行前に 0.1秒 しか待たない
  • 0.1秒は、ユーザーがコマンドを読み、正しいか判断し、取り消すには短すぎる時間である
  • この挙動は Git のデフォルト動作ではなく、help.autocorrect 設定が有効なときに現れる

デフォルト動作と help.autocorrect の誕生

  • Git のデフォルトの反応は、不明なコマンドを実行せず、文字列の類似度に応じて候補となるコマンドを表示して終了することだ
    • 例: git pushy では push が最も近いコマンドとして表示されることがある
  • もともと Git は、不明なコマンドに対して単に「Git コマンドではない」とだけ返していた
  • 2008年、Johannes Schindelin は既知のコマンドを走査して最も近いコマンドを探し、近い候補が1つだけなら実行する小さなパッチを提案した
  • Alex Riesen はこれを help.autocorrect 設定で制御するパッチを出し、当初この設定は ブール値 だった
    • Git の設定でブール値は 1true として解釈できる
    • そのため、当時の文脈では help.autocorrect=1 は自動修正を有効にする値のように見えた

1 が 0.1秒になった理由

  • Junio Hamano は、help.autocorrect を単なるブール値ではなく、実行前に待つ decisecond 数 にしようと提案した
    • decisecond は 0.1秒単位である
    • ユーザーに ^C を押す機会を与える意図があった
  • その後、受け入れられたパッチで設定値は待機時間に変更された
  • その結果、help.autocorrect=1 は「機能を有効にする」ではなく、「1 decisecond、つまり 0.1秒待ってから実行する」という意味になった
  • なぜ decisecond が適切な時間単位として選ばれたのかは、議論には残っていない

現在選べる設定値

  • Git のドキュメントでは、help.autocorrect は次の値をサポートしている
    • 0: デフォルト値で、候補コマンドだけを表示する
    • 正の数: 指定した decisecond 後に候補コマンドを実行する
    • immediate: 候補コマンドを即座に実行する
    • prompt: 候補を表示して実行するか確認する
    • never: 候補コマンドを実行も表示もしない
  • 確認してから実行したいなら、prompt は大半のユーザーにとってより合理的である
  • 設定例は次のとおり
git config --global help.autocorrect prompt
  • 以後 git pushy のようなコマンドを入力すると、push を実行するか [y/N] で確認する

Git が似たコマンドを選ぶ方法

  • Git はやみくもにコマンドを推定するのではなく、ある時点を超えると距離が遠すぎると判断して候補を出さない
  • 動作例は次のとおり
    • git bassrebase に十分近いため、実行確認が表示される
    • git bassa はもはや rebase と見なせないと判断され、候補は実行されない
    • git dmamrm の両方を似たコマンドとして表示する
    • git dmaam 1つにマッチする
  • このロジックは修正された Levenshtein distance アルゴリズムに基づいている
    • ある文字列を別の文字列に変えるのに必要な文字編集コストを計算する
    • 一部の操作はほかの操作より高いコストとして扱われる
  • Git にはハードコードされた cutoff があり、既知のコマンドとのコストが大きすぎると、ユーザーの入力ミスが大きいと見なして推定しない

1 の解釈を変えようとするパッチ

  • help.autocorrect=1 は人間の感覚では事実上 immediate と同じなので、Scott Chacon は 1 を 0.1秒待機ではなく 即時実行 として解釈する小さなパッチを書いた
  • Junio Hamano は "1" だけを特別扱いするのではなく、yesnotrueoff のような ブール文字列値 も適切に解釈しようと求めた
  • パッチの第2版は、こうしたブール文字列の処理まで含める方向で進んでいる
  • パッチが取り込まれれば、今後の Git バージョンでは help.autocorrect をめぐる混乱が減る可能性がある

1件のコメント

 
GN⁺ 2025-01-20
Hacker News のコメント
  • 70年代に Hal Finney が Mattel Intellivision 向けの BASIC インタプリタを 2KB ROM に収めようとしていて、1バイトたりとも貴重だった。
    構文エラーを表示するために、すべてのエラーメッセージを EH? に縮めていて、本人はそれをかなり誇らしく思っていたというのが今でも笑える。

    • EH? は昔ながらの インタラクティブフィクション ゲームのテキストパーサー応答としてもよく似合いそう。
      少し関連して、昔の BASIC の亜種には PRINT 文を ? と書けるものもあったが、メモリ節約というよりは REPL での入力時間を減らす目的に近かった気がする。
    • 無駄が多いね。ed はすべてのエラーに ? 1文字しか使わないから 3倍節約できる。
    • インタプリタの応答として Eh? を使った最も古い例は、RAND の JOSS で見た。
      [https://en.wikipedia.org/wiki/JOSS#/media/File:JOSS_Session....](https://en.wikipedia.org/wiki/JOSS#/media/File:JOSS_Session.jpg)
      https://en.wikipedia.org/wiki/JOSS
      メモリは約 5KB だったが、Intellivision と比べると機械の重量は約 5,000 ポンドだった。
    • かなり格好いい。Hal が当時 パーソナルコンピュータのハッカー だったとは知らなかったし、Hal の話を聞くといつも Bitcoin を思い浮かべる。
    • Wordle 派生ゲームの xordle を運営していて、1つの盤面に Wordle パズルが2つ入っている。
      だから、ある単語を推測して5文字すべてが緑になっても、2つの正解のどちらでもないことがある。そういうとき右側に単に Huh? と表示するようにしたら、そこをみんな気に入っている。
  • 根本原因は 設定名の付け方が悪かったこと だと思う。
    もともとの設定が help.autocorrect_enabled のようにブール値っぽい名前だったなら、整数値を受け取って 0.1 秒単位として解釈しようという要求は筋が通らなかったはずだ。代わりに help.autocorrect_accept_after_dsec のような別設定が必要になっただろうし、dsec はあまりに馴染みがないので、使う人なら調べざるを得なかったはずだ。
    コードレビューではいつもこういうことを求めている。曖昧になり得るなら、変数名に 単位 を入れるべきだ。たとえば int timeoutint timeout_msec にすべき。
    設定名ではこれが100倍重要になる。設定は公開インターフェースの一部なので、後から変えられないからだ。

    • 同感。ただし、これが型システム側に押し込まれる場合は今でも悩ましい。
      C++ の文脈では、大まかな基準として void FooBar(std::chrono::milliseconds timeout); は問題ない。関数シグネチャなので、見るときに型も一緒に見えるからだ。
      だが変数で timeout はよくない。99% の場合、実際の利用箇所では auto timeout = gl_timeout;FooBar(timeout); のように見える。
      C++ では auto をよく使うので、重要なときに正確な型を追跡するのがかなり面倒になる。
      C++ で作業するときは IDE や言語サーバー付きのエディタを使っているが、5秒ごとにマウスオーバーして型を確認する時間はない。
    • その通りで、そこに人生で文字どおり一度も使ったことのない時間単位である 0.1秒単位(デシ秒) を使ったことが、状況をさらに悪化させた。
      「1ms 後に実行します」というメッセージを見たら、すぐに設定を確認していただろう。
    • そうしていると、systemd のように TimoutSec=60TimeoutSec=1min も使える状態になる。
      むしろ単位を名前に入れないほうがよかったと思う。ただし、その特殊なケースを除けば完全に同意する。
    • そうはしているが、ハンガリアン記法 の匂いがするという感覚は拭いにくい。
      これまで見つけた最善の代案は、値に単位を受け付けることだ。たとえば 5 seconds5s を許可し、単なる 1 は不正な値として扱う方式だ。
    • コードレビューでは、そういう int の使用自体を指摘し、代わりに単位を正しく伝える 値クラス を勧めている。
      たとえば Second(2)Millisecond(2000) のような形だ。
      もちろんこれは言語の機能と慣習による。
  • xmobar も似ているが、もっと問題のある場所で 0.1秒単位 を使っている。各セクションをどれくらい頻繁に更新するかを宣言する設定だ。
    サンプル設定の数字が小さすぎてミリ秒のはずはなさそうなので、合理的に秒だと仮定してしまい、その結果、意図より10倍頻繁にコマンドを実行することになる。
    この問題に起因する偶発的な負荷急増を何度も見た。

  • かなり悪い設計に見える。
    ただし1番は記事の読み違いの結果で、「以前の値」は git には存在したことがなかった。

    1. 既存の設定値を黙って別の意味に再解釈する変更、たとえば 1=true1=0.100ms の確認遅延 に変えるようなことは、ほぼ常に避けるべきだ。既存の値があったなら消したくなるだろうが、設定名も変えるのが正しい。
    2. help.autocorrect の設定引数が時間であり、ほとんどのユーザーにとって標準的ではない単位で測られるというのは単純に悪い。有効化はブール値で受け取り、確認時間は小数値で調整できるようにすべきだ。
    • 1番については時系列を誤解していると思う。
      その変更は2008年に、そのオプションをブール値として追加しようとした初期パッチのコードレビュー中に起こったもので、メインの git ツリーにコミットされる前だった。
    • 「設計」と言うと、意図的で広い文脈の計画を思い浮かべる。
      これは設計というより、有機的に育った脇枝 に近い。
  • これは 機能過多(creeping featurism) の良い例だと思う。
    よく見ても不要な複雑さを持ち込み、悪く見れば、それに依存する人たちが自分のしていることにあまり注意を払わなくなる。

    • git はまさにそれだ。優雅なデータ構造の上に、年月とともに積み重なった不要なゴミの層が何重にも覆いかぶさっている。
    • なぜ自動実行を許可したがるのか理解できない。
      上矢印を押してミスを直すのはそんなに難しいのか? 「だいたい合っている」ものを自動で実行するのは、意図しないことをしてしまうためのレシピだ。
  • ミスはここにあった。既存の設定を別の意味に再利用するのではなく、新しい設定を追加すべきだった
    help.autocorrect はオン/オフ、help.autocorrect.milliseconds はどれだけ待つか、というふうに分けるべきだった
    他のシステムにも似たようなミスがある。たとえば MySQL の innodb_flush_log_at_trx_commit は 0 なら無効、1 なら有効で、2 は後から特殊な意味として追加された

    • 本当の問題は、1 が何を意味するのか推測しようとする型のない設定言語にある
      今では 1 はブール値の真のままにしつつ、他の整数は 0.1 秒単位にしており、さらに深みにはまっている
      YAML の悪名高い no == false と同じような怪しいロジックだ
    • どこで使うのが最適かは分からないが、0.1 秒もあり得ない値ではない
      記録上最速のランダム反応時間は 1.5ds 程度で、偶然にも平均的なゲーマーの反応時間もそのあたりだが、非ランダムな反応はずっと速くなり得る。たとえば拍子に合わせて反応する場合がそうだ
      そこまで速くしたいなら可能で、呼び出し速度も比較的安定しているはずだ。おおよそ数ミリ秒のレベルだろう
  • 1年前に自動修正機能を有効にして 3 秒に設定したが、2つのことに気づいた

    1. 危険な動作と安全な動作を区別しない
    2. 入力ミスしたコマンドでシェル履歴を汚染する
      この記事を読んで、1年ぶりにオフにするきっかけになった
    • むしろ、よくやるタイプミスにはエイリアス(alias) を作るほうがいい
      もちろんシェル履歴は「汚染」するけれど
    • 2番については、実際に汚染しているのは自分自身だ
      履歴をさかのぼって、どうせ動くからという理由で最後の間違ったコマンドをそのまま再利用しても同じことだ
  • 0.1 秒単位は本当に変わった選択だ
    遅延時間はミリ秒か秒で指定するほうがよく、どちらもコンピューティングでははるかによく使われる

    • しばらくかなり混乱した。deciseconds が git 専用の単位で、「決定を下すのに必要な秒」のような decision-seconds を意味するのかと思った xD
      ちなみに英語は母語ではないが、メートル法を使う文明化された地域の出身だ
    • 人間の反応時間には悪くないが、珍しい単位だ
      0 秒と 1 秒の差は何かを待つときには明らかに長いが、n ミリ秒と n+1 ミリ秒の差は実用的に使うには細かすぎる
  • 反応時間は刺激の種類によって異なり、聴覚は視覚より少し速く、触覚はそれよりさらに少し速くて 90〜180ms 程度だ
    だから git がエラーメッセージの代わりに一発殴ってくれるなら、かろうじて反応する時間があるかもしれない

    • その殴る装置が殴られた感じを出すには慣性を作る必要がある
      10ms でそれは十分だろうか?
  • この記事を読んで、ソフトウェア考古学と「プログラマー考古学者」という言葉を思い出した
    後者の概念については Vernor Vinge に感謝すべきだ

    • これは本当に引っかかる点だ。ハッカーたちが「ソフトウェア考古学」の概念をあの本が導入したかのように持ち上げていたので、『A Fire Upon The Deep』を読むのをとても楽しみにしていた
      ところがその概念はプロローグで少しほのめかされるだけで、その後はまったく出てこない。考古学の効果は筋書きには関係しているが、「ソフトウェア考古学者」は物語の中で意味のある人物ではなかった
      釣られたような気分だった
    • ソフトウェア考古学の危険は、結局**糞石(コプロライト)**を発見することになる点だ、とよく言われる
    • 今、自分が「年寄り」と呼ばれているような気がしてならない