世界を動かす技術を、日本語で。

Gitノート: Gitの最もクールで、最も愛されていない機能 (2022)

概要

  • git notes はメタデータをコミットなどに付与できる Gitの隠れた機能
  • コミットメッセージの後付け修正 やコードレビュー結果の保存に活用例
  • 使い勝手の悪さや GitHub非対応 で普及が進まず
  • 分散型コードレビュー やオフラインでのメタデータ管理の可能性
  • git notes の今後の発展と普及への課題

git notesとは何か

  • git notes は既存のGitオブジェクト(コミット、blob、treeなど)に 追加情報(メタデータ) を後付けできる機能
  • 既に履歴に記録された コミットメッセージを直接修正できない 場合でも、後から情報を notesとして追記 可能
  • オブジェクト本体を変更せずに メタ情報の追記 ができる仕組み
  • 例:最新コミットにnotesを追加
    • git notes add -m 'Acked-by: <tyler@tylercipriani.com>'
  • git logNotes欄 として表示される

git notesの実用例

  • The Gitプロジェクト ではコミットと メーリングリストの議論スレッド をnotesで紐付け
  • 他の利用例
    • 各コミットやブランチごとの 作業時間の記録
    • レビューやテスト情報 の記録
    • 分散型コードレビュー システムの構築

コードレビュー・テスト結果の保存

  • Gerritのreviewnotesプラグイン により、レビュー情報をnotesで管理
    • オフラインで 誰がレビューしたかどんなテストを通過したかgit logで確認可能
    • 例:
      • git fetch origin refs/notes/review:refs/notes/review
      • git log --show-notes=review
  • コミットに紐づく詳細なレビュー履歴 をリポジトリ内で一元管理

分散型コードレビューの実現

  • Googleのgit-appraise など、git notesを活用した 完全分散型コードレビューシステム の登場
    • レビュー依頼・コメント・マージ も全てローカルで完結
    • オンラインサービス(GitHub等)依存しない 独立性
    • シンプルなWebインターフェースも提供

git notesの課題と現状

  • 使い勝手の悪さ (UIやコマンドの複雑さ)が普及の妨げ
  • GitHubは2014年以降、commit notesの表示を廃止
  • blobやtreeへのnotes追加はさらに煩雑
  • 一部設定でgit logへの表示やfetchの自動化は可能
    • 例:
      • git config --add remote.origin.fetch '+refs/notes/*:refs/notes/*'
      • git config notes.displayRef 'refs/notes/*'
  • 依然として マイナーで忘れられがちな機能

フォージ独立性と今後

  • Git自体が分散型バージョン管理システム である点を活かし、 git notes でプロジェクトの履歴やメタデータも 分散管理 可能
  • 中央集権的なサービス(GitHub等)依存からの脱却 を目指す動き
  • git notesの普及・改善 が進めば、より柔軟でオープンな開発フローの実現も可能性

Hackerたちの意見

Gitノートは、コミット後に頻繁にテキストを追加して他の人に見えるようにしないと、あんまり面白くないよね。Acked-Byやメーリングリストのディスカッションリンクの例は、あんまり良い例じゃない気がする。これらはコミット時にはもう知ってることが多いし。gitのコミットメッセージは基本的に無限の長さが持てるから、フォージで行われたコミットに関するすべてのディスカッションをそのままコミットメッセージにコピーすることもできるよ。私が思うに、もっと良い例としては、後でリバートされたコミットにgitノートを追加することかもしれない。

コミットが作成された後も、ディスカッションは続けられるよ。

Acked-Byやメーリングリストのディスカッションリンクの例は、あまり良い例じゃない気がする。これらはコミットが行われる前にすでに知られている可能性が高いからね。コミットに関するディスカッション(is: review)やコミットの承認は、コミットが行われる前にはできないよ。 > もっと良い例として考えられるのは、後で元に戻されたコミットにgitノートを追加することかな。コミットメッセージの方がこの用途には向いてると思う。ファイルにブレームをかけると、そのファイルの最新の変更が表示されるよね。もしあるコミットが別のコミットの変更を元に戻した場合、その古いコミットを元に戻す新しいコミットがブレームに表示される。

現在の役割では、gitノートをかなり活用してるよ。内部コードレビューを追跡するための実験から始めたんだけど、コミットメッセージを埋め尽くしたり、すべてにPRを作ったりしないで済むからね。各コミットには、それがどのチケットに関連しているか、インフラの制約、修正の場合はインシデントスレッドへのリンクなどのコンテキストをタグ付けしてる。全部リポジトリに保存されてるから、なぜ行が変わったのかを知るためにSlackやJiraをグレップする必要がなくなるんだ。スケールで使い始めると、プラットフォームのUIがどれだけ必要ないかに気づくよ。ビルドの再現性についてはよく話すけど、意図についてはあまり話さないよね。もしかしたら、ここから始まるのかも。

それはコミットメッセージじゃないの?それとも「このコミットがバグ#123を引き起こしたことに気づいた」みたいに、未来にリンクさせることが目的なのかな?

これはUIの問題であって、知識不足の問題じゃないよ。もしGitHubのUIがノートを表示したら、すぐにもっと使われるようになると思う。

うん、GitHubがこれをサポートしてくれたらいいのに。

あまり知られていないもう一つの機能はgitトレーラーだよ: https://alchemists.io/articles/git_trailers これは、コミットを作成する際に含めることができるキーとバリューの構造データなんだ。一部のシステムではメタデータを付加するために使われてるよ。例えば、Gerritはこれを使ってChange-Idを付加してる。

別のシステムからのもう一つの似た機能:PostgreSQLのCOMMENT https://www.postgresql.org/docs/17/sql-comment.html これを使うと、PostgreSQLのさまざまなデータベースオブジェクトにテキストを付加できるんだ。もっと構造化されたキーとバリューのデータベースオブジェクトメタデータを編集できる機能があればいいのに。

うん、トレーラー大好き。特定のことにノートを使おうとしたことがあったけど、ちょっと面倒だった記憶があるな(具体的にどんな障害があったかは覚えてないけど)。トレーラーは私のニーズにぴったり合ってたよ。

これは素晴らしい!チケット番号とかを使ってCIを強化できそうだね。

最近、GitHubがコミットメッセージの中に[skip ci]を含める代わりにこれを使っていることを知ったんだけど、下流の利用者がコミットメッセージを簡単に削除できるためだと思う。https://docs.github.com/en/actions/managing-workflow-runs-an... なぜ最後のトレーラーであることを義務付けているのかはわからないけど、正規表現の理由かな。

基本的には流れに乗るようにしてるけど、イシュー追跡システムとの統合にもっと自然な場所があるのに、それがイシュー追跡システムのハッピーパスから遠く離れているのがイライラする。問題は、イシュー追跡システムが流行に従うことによって悪化していると思う。今週の流行を使っていることが多いし、その流行は機能が完全ではないことが多い。そして新機能は氷河のようにゆっくり追加される。要するに、追跡目的でリニアチケットのタイトルから派生したブランチ名にイライラしているってことかな。もっと意味のあるPRタイトルを持ちたいから、コミットをイシューに関連付けるために他のメタデータを使いたいんだ(リニアブランチ名をタイトルとして使わなきゃいけないから)。とはいえ、これはネットで愚痴るにはちょうどいいサイズの問題だと思う。

面白いね、この機能は知らなかった。私は従来のコミットが大好きなんだけど、トレーラーはそんなメタデータを追加するのに良い方法だと思う。手動でコミットメッセージに追加するのは、--trailerフラグを使うのと機能的には同じ?

余談だけど、GOOGで働いてた時のGerritがすごく恋しい。でも、2020年代のデプロイメントストーリーはちょっとクソだよね。ローカルでインスタンスを立ち上げようとしたけど、GitHubホストのリポジトリと統合しようとしたら、ただフラストレーションが溜まった。GHよりもコミットの変更追跡をうまく扱える、もっとアクティブに開発されていてGHとの統合がしやすいものってある?GHのコードレビュー機能が大嫌いなんだ。

自分のブランチでユニットテストを実行したコミットをマークするためにGitノートを使ってた(だから、スクリプトはそれらをスキップする)。オープンソースのアップストリームで作業する時に、ブランチを完璧に整えるためにgit rebase -iを使うのが役立った。今はGitトレーラーにその情報を入れる方がいいみたい。変更IDについては、Git自体がそれを持ってたらいいのに。そうすればツールもそれを理解できるしね。コミットメッセージでコミットを特定するのは脆弱だし、特にMRのためにそれを更新するかもしれない時にはね。コミットIDは本当にユニークにコミットを特定するけど、同じ変更が他のコミットの上に移動される可能性がある時には、あまり役に立たない識別子だよ。追記:ああ、実際にはそれらはコミットの一部で、ノートはそうじゃないから、私の使い方には良い代替にはならなさそうだね。

今年の1月にリリースされたバージョン10からForgejoでもサポートされてるよ: https://forgejo.org/2025-01-release-v10-0/#new-features https://codeberg.org/forgejo/forgejo/pulls/4753

それいいね、シェアしてくれてありがとう!

2020年頃にマニュアルページのノートを発見したけど、主にローカルリポジトリの機能だったから使わなかったんだ。デフォルトではプッシュやフェッチされないしね。もちろん、プッシュやフェッチするように設定することもできるけど、それはチームの決定だから、私のチームはそれに投票しなかったんだ。

「Gitの一番クールで、あまり愛されていない機能」ってたくさんあるよね。例えば、bisect、pickaxe、reflog、range-diff、archive、annotated tagsとか。残念ながら、GitをただのグローリファイドなGoogle Driveだと思っている人が多いから、忘れられがちなんだよね…

Gitノートは冗長だと思う。どうせ機能を追跡するには、もっと高レベルなプロジェクト管理ツールが必要だから。ロードマップ、機能の階層、技術的でない詳細とかね。大きなトラッカーやJiraを考えてみて。まあ、それはそれでいいと思うよ。Unixの哲学は、一つのことに集中して、それをうまくやることだから。

多くの場合、そういう機能は実際にはあまり役に立たないし、作業馬の周りの無駄なものに過ぎないんだよね。

LibreOfficeプロジェクトでは、Apache OpenOfficeリポジトリへの各コミットを追跡するためにGitノートが使われてたよ(それをLibreOfficeのGitリポジトリのブランチとしてミラーしてた)。そのコミットが関係ないもの(例えば、LibreOfficeがずっと前により良いものに置き換えたビルドシステムの変更)か、LibreOfficeに既に存在する変更を重複しているか(多くの場合、数年前のもの)、または、最も珍しいケースとして、LibreOfficeに受け入れられたか(どのコミットがチェリーピックしたか)を記録してた。今でもここで見れるよ:https://cgit.freedesktop.org/libreoffice/core/log/?h=aoo/tru...(そのGitフロントエンドはまだノートを表示してる)。(数年前にこれらの変更の追跡はやめたみたい。おそらく、Apache OpenOfficeの変更ペースがほとんどなくなったから、これらの少数の変更をチェリーピックする意味がなくなったんだろうね。)

Our World In Dataでの公共データパイプラインの良いユースケースがあった。そこでは、一つのリポジトリがパイプラインを持っていて、もう一つのgit-lfsリポジトリがそのパイプラインのビルド出力を持ってた。コードパイプラインへのコミットに追加されたGitノートは、構築されたデータを特定するハッシュを記録してた。全体的に見てエレガントに感じたし、設定後はメンテナンスも必要なかったけど、正直言って、実際には使われなかった。過去を振り返る必要が予想よりも少なかったし、Gitノートがデフォルトで隠れてるのも認識には役立たなかったんだよね。

OPは、使われていないのはインターフェースが使いづらいからだと言ってるけど、リリースについてのメモをテキストで取ってるなら、そのメモをコミット時にGit Notesに簡単に流せると思うんだよね。これって簡単じゃない?何か見落としてるかな?