Activities

メンテナのGit作業時間は、3つのアクティビティに費やされます。

  • コミュニケーション (45%)

    一般的な設計、ユーザーの質問への回答、バグレポートの診断に関するメーリングリストでのディスカッション。パッチのレビューや、コメントや、代替案の提案や、パッチの拒否。

  • インテグレーション(統合) (50%)

    貢献者からの新しいパッチを適用しながら、小さな間違いを見つけて修正し、統合ブランチとテストブランチを混ぜっ返し、結果をプッシュし、リリースを区切り、アナウンスを行います。

  • 自身の開発作業 (5%)

    自身が思う痒い所に手を入れ、提案パッチシリーズを送信します。

The Policy

統合に関するポリシーは、「A Note from the maintainer」メッセージに非公式に記載されています。このメッセージは、各機能のリリースが行われた後、このメーリングリストに定期的に投稿されます。

  • 機能リリースには vX.Y.0 の番号が付けられており、機能、パフォーマンス、使いやすさなど、あらゆる分野でのバグ修正と機能拡張が、デグレ無しで含まれることを目的としています。

  • 機能リリースのリリースサイクルは、8〜10週間と想定されています。

  • メンテナンスリリースにはvX.Y.Zという番号が付けられており、対応する vX.Y.0 機能リリースと以前のメンテナンスリリース vX.Y.W(W<Z) の、バグ修正のみが含まれていることを意味します。

  • 「master」ブランチは、次の機能リリースの準備に使用されます。 つまり、ある時点で、「master」ブランチの先端に vX.Y.0 のタグが付けられます。

  • 「maint」ブランチは、次のメンテナンスリリースの準備に使用されます。 機能リリース vX.Y.0 が作成された後、「maint」ブランチの先端がそのリリースに設定され、バグ修正がブランチに蓄積され、ある時点で、ブランチの先端に vX.Y.1, vX.Y.2, 等のタグが付けられます。

  • 「next」ブランチは、 (1)価値のある目標があり、 (2)日常の使用に適したかなり良好な状態であり、 (3)デグレがないことがまだ実証されていない変更(拡張と修正の両方)を公開するために使用されます。 新しい変更は、「master」にマージされる前に「next」でテストされます。

  • 「seen」ブランチは、「next」に設定された基準をまだパスしていない他の提案された変更を公開するために使用されます。

  • 「master」と「maint」ブランチの先端は、人々がそれらの上に独自のカスタマイズを構築できるようにするために、巻き戻されることはありません。 新しい開発サイクルの早い段階で、「next」は「master」の先端に一度巻き戻されますが、それ以外の場合は、サイクルが終了するまで巻き戻されません。

  • 通常、「master」には「maint」のすべてが含まれ、「next」には「master」のすべてが含まれます。 「seen」には「next」にマージされたすべてのトピックが含まれていますが、「master」で直接再構築されています。

  • 「master」の先端は、タグ付けされたどのリリースよりも安定していることを意味しており、ユーザーはそれに従うことをお勧めします。

  • 「next」ブランチは新しいアクションが行われる場所であり、新しいトピックが「master」にマージされる前にデグレとバグが見つかるように、ユーザーはそれをテストすることをお勧めします。

注意: v1.9.0リリース以前は、バージョン番号の構造が少し異なっていたことに注意してください。 vX.Y.Z は機能リリースであり、vX.Y.Z.W は vX.Y.Z のメンテナンスリリースでした。

A Typical Git Day

メンテナの典型的なGitな一日は、以下のようにして上記のポリシーを実装します:

  • メーリングリストを走査します。 レビューコメント、提案などで返信します。あちこちの議論にクチバシを突っ込みます(Kibitz)。 メーリングリストから潜在的に使用可能なパッチを収集します。 1つのトピックに関するパッチは1つのメールボックスに送信されます(私はGnusでメールを読み、\C-o と入力して、メッセージをmbox形式のファイルに 保存/追加 します)。

  • メーリングリストで提起されたが、誰も解決に乗り出していない問題に対処するために自身でパッチを書きます。 他の貢献者と同じように送信し、他の貢献者からのパッチと同じように受け取ります(上記参照)。

  • 保存したメールボックスのパッチを確認します。タイプミス修正と、プログラムをキレイにするために提案されたログメッセージを編集し、リストから収集した対応を追加します。 パッチを編集して、ディスカッションからの「ええっと、これはこのようになっているはずです」な修正を組み込みます。

  • 収集されたパッチを分類し、 mastermaint の更新を処理します:

    • 明らかに、「maint」の先端に関連する正しい修正は、「maint」に直接適用されます。

    • 明らかに、「master」の先端に関連する正しい修正は、「master」に直接適用されます。

    • 他のトピックはこのステップでは処理されません。

      このステップは 「git am」で実行されます。

      $ git checkout master    ;# or "git checkout maint"
      $ git am -sc3 mailbox
      $ make test

      実際には、 master または maint に直接行くパッチはほとんどありません。

  • 「What’s cooking」メッセージの最後の号を確認し、マージの準備ができているトピックを確認します(topic→master と topic→maint)。 このステップを支援するために、 Meta/cook -w スクリプト( Meta/ には「todo」ブランチのチェックアウトが含まれています)を使用します。

    そして、マージを実行します。 この手順を支援するには、 Meta/Reintegrate -e スクリプト(後述)を使用します。

    $ Meta/cook -w last-issue-of-whats-cooking.mbox
    $ git checkout master    ;# or "git checkout maint"
    $ echo ai/topic | Meta/Reintegrate -e ;# "git merge ai/topic"
    $ git log -p ORIG_HEAD.. ;# final review
    $ git diff ORIG_HEAD..   ;# final review
    $ make test              ;# final review
  • 残りのパッチを処理します:

    • 「master」に適用可能で(つまり、「next」にあり、「master」にないものに依存しません)明らかでないものはすべて、「master」の先端から分岐した新しいトピックブランチ(または「master」より少し古い最後の機能リリース)に適用されます。これには、「master」の機能強化と明らかでない修正の両方が含まれます。 トピックブランチの名前は ai/topic です。ここで、「ai」は作成者の頭文字にちなんで名付けられた2文字の文字列であり、「topic」はトピックの説明的な名前です(つまり、「そのシリーズが何に付いてであるか」です)。

    • 「maint」を対象とした明白でない修正が、「maint」の先端から分岐した新しいトピックブランチ(または最も古く、まだ関連するメンテナンスブランチ)に適用されます。 トピックには、 ai/maint-topic という名前を付けることができます。

    • 既存のトピックに関連する変更はブランチに適用されますが、以下のようになります:

      • 明らかに正しいものが最初に適用されます

      • 疑わしいものは廃棄されるか、先端近くで適用されます

    • 既存のトピックへの置換パッチは、 next にないコミットに対してのみ受け入れられます。

      最初のラウンドは以下のように行われます:

      $ git checkout ai/topic ;# or "git checkout -b ai/topic master"
      $ git am -sc3 mailbox

      既存のトピックを次のラウンドに置き換えるには、以下のようにします:

      $ git checkout master...ai/topic ;# try to reapply to the same base
      $ git am -sc3 mailbox

      切り離されたHEAD(detached HEAD)で新しいラウンドを準備し、それから以下のようにします

      $ git range-diff @{-1}...
      $ git diff @{-1}

      前回のラウンドから何が変わったかを再確認し、最後に以下のようにします

      $ git checkout -B @{-1}

      これで完結させます(最後のステップは、なぜ、すでに「next」にあるトピックが置き換えられず、段階的に更新されるかを示しています)。

      それが最初のラウンドであろうと後続ラウンドであろうと、トピックは単独でも構築されない可能性があり、または、バグが原因で統合ブランチにマージされたときにビルドが破損する可能性があります。メーリングリストには、すでに明白で些細な改善が提案されている可能性があります。 メンテナは、統合ブランチを公開して、他の開発者がテストに使用できるようにする前に、物事を修正するために、そのタイトルに「SQUASH???」を使用して余分のコミットを追加することがよくあります。 これらの変更は、メンテナが100%コミットしているわけではないため(些細なタイプミスなどは、個別の「SQUASH???」コミットとして適用されることなく、修正が必要なパッチに直接押しつぶされる(squash)ことがよくあります)、必要に応じて簡単に削除できます。

  • 必要に応じて、maint を master にマージ:

    $ git checkout master
    $ git merge maint
    $ make test
  • 必要に応じて master を next にマージ:

    $ git checkout next
    $ git merge master
    $ make test
  • 「What’s cooking」の最後の号をもう一度レビューし、「next」にマージする準備ができているトピックがまだ良好な状態であるかどうかを確認します(たとえば、このシリーズのリストに新しい問題が提起されていませんか?)

  • jch ブランチを準備します。これは、 masterseen の間のどこかを表すために使用され、多くの場合、 next よりわずかに進んでいます。

    $ Meta/Reintegrate master..seen >Meta/redo-jch.sh

    結果は、Meta/Reintegrate スクリプトへの入力として「seen」を再構築するためにマージされるトピックをリストするスクリプトです。 まだ「jch」に含まれていてはならない後続のトピックを削除します。 出力の最初のトピックの名前の前に ### match next という行を追加します。これは、「jch」に含まれている必要がありますが、「next」には含まれていません。

    $ Meta/Reintegrate master..jch >Meta/redo-jch.sh

    結果は、Meta/Reintegrate スクリプトへの入力として「seen」を再構築するためにマージされるトピックをリストするスクリプトです。 まだ「jch」に含まれていてはならない後続のトピックを削除します。 出力の最初のトピックの名前の前に ### match next という行を追加します。これは、「jch」に含まれている必要がありますが、「next」には含まれていません。

    • これで、トピックを「next」にマージする準備が整いました。 先端が「next」にマージされていないブランチごとに、以下の3つのいずれかが発生する可能性があります:

      • コミットはすべてnextにマージする価値があります。 トピックをnextにマージします。

      • 新しい部品は混合品質ですが、初期の部品はnextにマージする価値があります。 初期の部品をnextにマージします。

      • nextにマージする価値のあるものはありません。何もしません。

        この手順は、以前に作成した Meta/redo-jch.sh スクリプトを使用して支援されます。 すでに「next」にあるトピックにパッチが適用された場合、スクリプトはそれを ai/topic~1 としてリストします。 更新された「next」に新しいパッチを含めるには、 ~1 の部分を削除します。除外したいモノの行はそのままにしてください。 next になかったトピックを next にマージする必要がある場合は、リストの最後に追加します。 それから以下のようにします:

        $ git checkout -B jch master
        $ Meta/redo-jch.sh -c1

        これは jch ブランチを最初から再構築します。 -c1 は、 ### で始まる最初の行(つまり、以前に追加した ### match next 行)でマージを停止するようにスクリプトに指示します。

        この時点で、結果をビルドテストします。 セマンティックの競合が明らかになる場合があります(たとえば、トピックが変数の名前を変更し、別のトピックが古い名前で変数への新しい参照を追加したとか)。その場合、最初に適切なマージ修正(merge-fix)を準備し(appendix参照)、そしてゼロからから「jch」ブランチを再構築し。 「master」の先端から始めます。

        それから同じ事を「next」に行います

        $ git checkout next
        $ sh Meta/redo-jch.sh -c1 -e

        -e オプションを使用すると、トピックの履歴からのマージメッセージと「What’s cooking」のコメントを編集できます。 同じトピックのセットが「master」にマージされるため、結果のツリーは「jch」と一致する必要があります。 そうでなければ、ミスマージがあります。 理由を調査し、ミスマージが検出されて修正されるまで続行しないでください。

        $ git diff jch next

        全てOKになったら、 redo-jch.sh スクリプトで以下のようにクリーンアップします

        $ sh Meta/redo-jch.sh -u

        これにより、スクリプト内にリストされてる、「master」にすでにマージされているトピックを削除します。 これにより、「# match next」マーカーが失われる可能性があります。 もし「# match next」マーカーが失われたら、適切な場所に再度追加します。

    • コミットはすべてnextにマージする価値があります。 トピックをnextにマージします。

    • 新しい部品は混合品質ですが、初期の部品はnextにマージする価値があります。 初期の部品をnextにマージします。

    • nextにマージする価値のあるものはありません。何もしません。

      この手順は、以前に作成した Meta/redo-jch.sh スクリプトを使用して支援されます。
      すでに「next」にあるトピックにパッチが適用された場合、スクリプトはそれを `ai/topic~1` としてリストします。
      更新された「next」に新しいパッチを含めるには、 `~1` の部分を削除します。
      除外したいモノの行はそのままにしてください。
      `next` になかったトピックを `next` にマージする必要がある場合は、リストの最後に追加します。
      それから以下のようにします:
      $ git checkout -B jch master
      $ sh Meta/redo-jch.sh -c1
      これは `jch` ブランチを最初から再構築します。 `-c1` は、 `###` で始まる最初の行(つまり、以前に追加した `### match next` 行)でマージを停止するようにスクリプトに指示します。
      この時点で、結果をビルドテストします。 セマンティックの競合が明らかになる場合があります(たとえば、トピックが変数の名前を変更し、別のトピックが古い名前で変数への新しい参照を追加したとか)。その場合、最初に適切なマージ修正(merge-fix)を準備し(appendix参照)、そしてゼロからから「jch」ブランチを再構築し。 「master」の先端から始めます。
      それから同じ事を「next」に行います
      $ git checkout next
      $ sh Meta/redo-jch.sh -c1 -e
      `-e` オプションを使用すると、トピックの履歴からのマージメッセージと「What's cooking」のコメントを編集できます。 同じトピックのセットが「master」にマージされるため、結果のツリーは「jch」と一致する必要があります。 そうでなければ、ミスマージがあります。 理由を調査し、ミスマージが検出されて修正されるまで続行しないでください。
      $ git diff jch next
      次に、残りの「jch」をビルドします:
      $ git checkout jch
      $ sh Meta/redo-jch.sh
      全てOKになったら、 redo-jch.sh スクリプトで以下のようにクリーンアップします
      $ sh Meta/redo-jch.sh -u
      これにより、スクリプト内にリストされてる、「master」にすでにマージされているトピックを削除します。 これにより、「### match next」マーカーが失われる可能性があります。 もし「### match next」マーカーが失われたら、適切な場所に再度追加します。
    • 「seen」を再構築します。

      $ Meta/Reintegrate jch..seen >Meta/redo-seen.sh
      スクリプトに、まだ「表示」(seen)されていない新しいトピックを追加して、結果を編集します。 それから以下のようにします
      $ git checkout -B seen jch
      $ sh Meta/redo-seen.sh

      これが全てがOKになったら、redo-seen.sh スクリプトで以下のようにしてクリーンアップします

      $ sh Meta/redo-seen.sh -u

      実行して再確認(double check)します

      $ git branch --no-merged seen

      予期しない残りのトピックがないことを確認します。

      この時点で、結果をビルドテストします。 セマンティックの競合が明らかになる場合があります(たとえば、トピックが変数の名前を変更し、別のトピックが古い名前で変数への新しい参照を追加したとか)。その場合、最初に適切なマージ修正(merge-fix)を準備し(appendix参照)、そしてゼロからから「seen」ブランチを再構築し。 「jch」の先端から始めます。

  • 「What’s cooking」メッセージを更新して、既存のトピック、新しく追加されたトピック、および段階的なトピックの更新を確認します。

    このステップは 「git am」で実行されます。

    $ Meta/cook

    このスクリプトは、 master..seen 間の履歴を検査し、トピックブランチの先端を見つけ、見つかったものを Meta/whats-cooking.txt の現在の内容と比較し、 Meta/whats-cooking.txt を更新します。 ファイルにリストされていないが master..seen にあるトピックは、「New topics」セクションに追加されます。ファイルにリストされ、 master..seen に見つからなくなったトピックは、「Graduated to master」セクションに移動されます。 コミットによって状態が変更されたトピック(たとえば、以前は「seen」のみでしたが、現在は「next」にマージされていますとか)は、変更マーカー << および >> で更新されます。

    <<>> で囲まれた行を探します。 これらは、この統合ラウンドによって置き換えられた古いファイルのコンテンツを保持します。 それらを確認(verify)した後、古い部分を削除します。 各トピックの説明を確認し、必要に応じてその完成度と計画を更新します。 更新された計画を確認するには、以下を実行します

    $ Meta/cook -w

    これは、 "Will merge to next" などの、トピックに付けられたコメントを取得します(サポートされているフレーズの種類については、 Meta/cook を参照してください)。

  • 4つ(5つ)すべての統合ブランチをコンパイル、テスト、およびインストールします。 Meta/Dothem スクリプトは、このステップで役立つでしょう。

  • master ブランチが更新された場合は、ドキュメントをフォーマットします。 Meta/dodoc.sh スクリプトがこのステップに役立つでしょう。

  • 統合ブランチを公共の場所にプッシュします。 Meta/pushall スクリプトがこのステップに役立つでしょう。

Observations

行うべきいくつかの観測。

  • 各トピックは個別にテストされ、他のトピックと一緒に、最初に「seen」、次に「jch」、それから「next」で調理(cooking)されます。 それが成熟するまで、その一部は「master」にマージされません。

  • すでに「next」にあるトピックは、「next」にある間に修正を取得できます。 このようなトピックには、「next」への多くのマージがあります(つまり、 git log --first-parent next は、同じトピックに対して多くの「nextへマージする ai/topic ブランチ」(Merge branch ai/topic to next)を表示します。

  • maint の明白でない修正は、 next で調理され、 master にマージされて、それがOKであることを確認してから、 maint にマージされます。

  • next が空になった場合(つまり、すべてのトピックが安定して master にマージされ、 git diff master next が空の場合)でも、next には、 master には決して存在しない大量のマージコミットがあります。

  • 原則として、 git log --first-parent master..next はマージのみを表示するべきです(実際には、マージではない fixupコミット と revert があります)。

  • next にないトピックブランチの先端近くのコミットは、破棄または置換または書き換えのいいカモ(fair game)です。 next にすでにマージされているコミットはそうではありません。

  • next ブランチにあることは、そのトピックが次の機能リリースに含まれることを保証するものではありません。「master」ブランチにあるのは通常、次の機能リリースに含まれます。

  • 「SQUASH???」修正の性質上、元の作成者が提案された変更に同意する場合は、次のラウンドで適切なパッチにそれらを押しつぶして(squash)もかまいません(提案された変更が十分に小さい場合、作成者は「Helped-by」を気にする必要はありません)。元の作者が提案に同意しない場合は、次のラウンドからそれらを削除することもできますが、作者は議論のどこかで理由を述べることが期待されます。

Appendix

Preparing a "merge-fix"

2つのトピックのマージは、字面的には競合しなくても、意味レベルでは競合が発生する場合があります。 古典的な例は、あるトピックで変数とそのすべての使用法の名前を変更し、別のトピックで変数の新しい使用法を古い名前で追加する場合です。 これらの2つのトピックがマージされた場合、後者のトピックによって新しく追加された変数への参照は、結果的に古い名前を引き続き使用します。

redo-jch および redo-seen スクリプトで使用される Meta/Reintegrate スクリプトは、この問題を回避するための大雑把ですが使用可能な方法を実装しています。 スクリプトがブランチ$Xをマージするとき、「refs/merge-fix/$X」が存在するかどうかをチェックし、存在する場合、その効果は機械的マージの結果に押しつぶされ(squash)ます。 言い換えると、

$ echo $X | Meta/Reintegrate

これは以下のシーケンスとほぼ同等です:

$ git merge --rerere-autoupdate $X
$ git commit
$ git cherry-pick -n refs/merge-fix/$X
$ git commit --amend

この「prepare a merge-fix」(マージ修正の準備)ステップの目標は、セマンティックの競合を修正するために、機械的なマージの結果に押しつぶす(squash)ことができるコミットを提案することです。

ブランチ「ai/topic」を統合ブランチにマージした結果に、たとえば seen~4 のようなセマンティックの競合があることがわかった後、切り離されたHEAD(detached HEAD)で問題のあるマージをチェックし、作業ツリーを編集してセマンティックの競合を修正し、修正を記録するための別のコミットを行います:

$ git checkout seen~4
$ git show -s --pretty=%s ;# double check
Merge branch 'ai/topic' to seen
$ edit
$ git commit -m 'merge-fix/ai/topic' -a

次に、「refs/merge-fix/ai/topic」を参照して、この結果を示します:

$ git update-ref refs/merge-fix/ai/topic HEAD

それから、Meta/Reintegrate にマージをやり直すように依頼して、結果を再確認(double check)します:

$ git checkout seen~5 ;# the parent of the problem merge
$ echo ai/topic | Meta/Reintegrate
$ git diff seen~4

今回は、refs/merge-fix/ai/topic を準備したため、結果のマージは、セマンティックの競合の修正を含むように調整されているはずです。

注意: これは、競合するブランチがマージされる順序が変更されないことを前提としていることに注意してください。 ai/topic ブランチをマージする理由がこのマージ修正を必要とする場合、統合ブランチに以前にマージされた別のブランチが、ai/topicブランチが作成した基本的な仮定を変更したためです(たとえば、ai/topicブランチは、変数を参照するサイトを追加しましたが、 他のブランチはその変数の名前を変更し、既存の使用サイトを調整したとか)、他のブランチの前に ai/topic ブランチをマージするように redo-jch(またはredo-seen)スクリプトを変更した場合、ai/topicのマージ中に上記のmerge-fixを適用しないで、代わりに他のブランチをマージするときに適用する必要があります。他のブランチに適用するには、修正を移動する必要があります。おそらく以下のようになります:

$ mf=refs/merge-fix
$ git update-ref $mf/$the_other_branch $mf/ai/topic
$ git update-ref -d $mf/ai/topic