I. 概要通常の PR マージ プロセスに加えて、競合、コミットの追加の必要性、PR 送信後のコミットのマージの必要性など、比較的複雑な問題を解決する方法についても詳しく説明する予定です。 この記事は、主に以下の 4 つの部分に分かれて構成されています。
さあ、始めましょう! II. オープンソース プロジェクトに参加する理由「なぜオープンソースに参加するのか」という点や、オープンソースプロジェクトに参加するメリットについて長々と議論するつもりはありません。その代わりに、「コーディングスキルの向上」という観点から「なぜオープンソースプロジェクトに参加するのか」についてお話ししたいと思います。 面接中、私は履歴書に特定の言語の熟練度について記載している応募者に、次のような質問をする習慣があります。 オープンソースプロジェクトのソースコードを読んだことがありますか?あるいは、オープンソースコミュニティに参加したり、オープンソースプロジェクトにPRを送信したりしたことがありますか? 答えが「はい」の場合、たとえば、候補者が Kubernetes モジュールのソースコードの一部を読んだと述べ、私がさらに候補者が実際にそれを読んで理解したこと、またはタイプがバグ修正/機能の PR を実際に送信したことを確認した場合、成熟したオープンソース プロジェクトのソースコードの一部を理解できること、またはタイプがバグ修正/機能の PR を送信できることで、すでにすべてが語られていると考えるため、プログラミング言語に関する質問はもう行いません。 私自身の Golang の学習は、大きく分けて 2 つの段階に分けられます。
Kubernetesプロジェクトのソースコードを眺めていた頃、典型的な社内プロジェクトと、世界最高峰のプログラマーの英知を結集したオープンソースプロジェクトとの間には、大きな隔たりがあることを痛感しました。そして、優れたオープンソースプロジェクトのソースコードを研究することが、プログラマーのコーディングスキル向上に重要であることも痛感しました(もちろん、Google社内にもオープンソースではない優れたコードが存在することは間違いありませんが、今日は特別なケースについて議論する必要はないでしょう)。 オープンソースプロジェクトのソースコードを注意深く読むと、必ず小さな欠陥が見つかります。そんな時、PR(プルリクエスト)を送信して自分のコードをオープンソースプロジェクトにマージし、「世界の隅々まで」実行してもらうことは、実に魅力的な体験です。最初のPRを無事にマージできた時は、まるでパンドラの箱を開けたような気分です。まるで別世界に入り、オープンソースコミュニティとの繋がりを築き始め、オープンソースの魅力を体験できるのです! III. PRのやり方を紹介したい理由当社では以下の 2 つのプロジェクトをオープンソース化しています。 1. CNCFプロジェクトDevStream[1] CNCF プロジェクト DevStream 2. Apache DevLake[2] DevStreamプロジェクトとDevLakeプロジェクトは、新しいコントリビューターから定期的にPRを受け取ります。しかし、多くのコントリビューターは、最初のPRを送信する際に、競合、コミットレコードの過多または乱雑さ、署名されていないコミット、非標準のコミットメッセージ、さまざまなCIプロセスチェックにおけるエラーなど、1つ以上の問題に遭遇することがよくあります。 新しいコントリビューターがPRを提出するのを見ると、私たちは当然のことながら大変嬉しく、温かく迎え入れ、様々な問題の解決方法を伝えます。しかし、コントリビューターの数が増えるにつれて、オープンソースコミュニティはほぼ毎日、「PRを適切に提出するにはどうすればいいか?」という質問に答える必要に迫られています。必要なドキュメントを提供していないのではないかと疑問に思う方もいるかもしれません。実はそうではありません。詳細なドキュメントは用意していますが、人は本質的に怠け者です。新しいコントリビューターの多くは、PRを提出する前にドキュメントを注意深く読む意欲がありません。オープンソースプロジェクトに不慣れで、プロジェクトの構造やドキュメントの構成に馴染みのない多くの新しいコントリビューターは、これらのドキュメントの存在すら知らないかもしれません。つまり、様々な理由から、ほとんどの新しいコントリビューターは「まずPRを提出して、結果は後で考える」という選択をしてしまうのです。 今回は、GitHubにおけるPRの送信プロセス全体、そして様々な問題点とその解決策を解説しながら、「正しいPRの送信方法」を徹底的に解説したいと思います。オープンソースプロジェクトに初めて参加する初心者の方々の参考になれば幸いです。また、DevStreamやDevLakeコミュニティの参入障壁をさらに下げることができれば幸いです。 IV. オープンソース プロジェクトに参加したいのですが、どうすればよいですか?オープンソース プロジェクトに参加しようと決めた理由が何であれ、学習のため、興味のため、達成感のため、あるいはオープンソース プロジェクトに必要な機能を組み込むためなど、今日オープンソース プロジェクトに PR を送信することに決めたのであれば、始めましょう。 4.1 適切なオープンソースプロジェクトを見つけるすでにオープンソース コミュニティに参加することを決めている場合は、このセクションをスキップしてください。 オープンソースを始めたいけれど、どのコミュニティに参加すればいいかまだわからないという方には、いくつか提案があります。
たとえば、興味のあるオープンソース プロジェクトは次の場所で見つけることができます。
もちろん、CNCFサンドボックスプロジェクトDevStream[6]やApacheインキュベータプロジェクトApache DevLake[7]からオープンソースの世界に直接アクセスすることもできます。 4.2 貢献ポイントの特定オープンソースプロジェクトへの参加方法は様々です。最も一般的な方法は、機能開発やバグ修正に関するPRを提出することです。しかし、ドキュメントの改善、テストケースの改善、バグのフィードバックなども非常に価値のある貢献となります。しかし、この記事ではPRとして提出する必要がある貢献ポイントから始めます。DevStreamプロジェクトを例に挙げると(他のプロジェクトでも同様ですが)、プロジェクトのGitHubリポジトリ[8]のホームページにはIssuesというエントリがあります。このエントリには、下図に示すように、既知のバグ、提案(新しい要件とも言えます)、予定されている補足ドキュメント、緊急に改善が必要な単体テストなどが記録されます。 DevStreamの問題 問題セクションでは、通常「good first issue」というラベルの付いた問題が表示されます。このラベルをクリックすると、さらに絞り込み、すべてのgood first issueを直接表示できます。これらは、コミュニティが新規貢献者向けに特別に用意した、比較的シンプルな入門レベルの問題です。 DevStream の最初の問題 そうです、ここから始めましょう。これらの最初の課題をざっと見て、興味があるのにまだ割り当てられていない課題がないか確認しましょう。そして、下記にコメントを残し、プロジェクト管理者がタスクを割り当てるのを待ってからコーディングを始めましょう。例えば、以下のような感じです。 DevStreamで問題を報告 画像に示すように、問題がまだ申請されていない場合は、メッセージを残して管理者がタスクを割り当てるのを待ち、その後開発を開始できます。 5. PR を送信するにはどうすればよいですか?一般的に、オープンソース プロジェクトのコードベースには、貢献を開始する方法を説明する CONTRIBUTING.md ファイルまたは同様の名前のドキュメントがルート ディレクトリに存在します。以下に例を示します。 DevStream 貢献 DevStream Contributing[9]ドキュメントには開発ワークフロー[10]が掲載されていますが、これは実際にはPRワークフローの紹介です。しかし、今日はPRワークフローについてより詳しくお話ししたいと思います。 5.1 ステップ1: プロジェクトリポジトリをフォークするGitHub上のプロジェクトにはすべて「フォーク」ボタンがあります。まずはオープンソースプロジェクトを自分のアカウントにフォークする必要があります。DevStreamを例に挙げましょう。 フォークDevStream 「フォーク」ボタンをクリックし、アカウントに戻ってフォークされたプロジェクトを見つけます。 DevStreamフォーク このプロジェクトはあなた自身のアカウントで管理されているため、自由に変更する権限があります。次に行う必要があるのは、コードの変更をフォークしたリポジトリに移動し、プルリクエストを介してコミットをアップストリームプロジェクトにマージすることです。 5.2 ステップ2: プロジェクトリポジトリをローカルマシンにクローンするプロセスはどのオープンソースプロジェクトでもほぼ同じです。いくつかのコマンドを直接記述したので、コピー&ペーストして実行できます。もちろん、コマンド内の一部の変数は実際のニーズに合わせて変更する必要があります。例えば、DevStreamプロジェクトでは、次のようにいくつかの環境変数を設定できます。
WORKING_PATHを"~/gocode" にエクスポートします。 同様に、DevLake の場合、コマンドは次のようになります。 WORKING_PATHを"~/gocode" にエクスポートします。 USER を GitHub ユーザー名に置き換えることを忘れないでください。WORKING_PATH も柔軟に設定できます。コードを配置するパスを記述するだけです。 次に、いくつかの一般的なコマンド行を使用してクローン操作を完了します。
mkdir -p ${WORKING_PATH} コードをクローンするために SSH を設定している場合は、`git clone` コマンドで使用される URL を `[email protected]:${USER}/${PROJECT}.git` に変更できます。 この手順を完了すると、ローカルに表示されるリモート情報は次のようになります。
origin git @github.com:daniel-hutao/devstream.git (フェッチ) ローカル コードの変更は origin にのみコミットし、その後 origin からアップストリームにプル リクエストを送信する必要があることに注意してください。 5.3 ステップ3: ローカル支店コードを更新するフォークとクローン操作を完了したばかりであれば、ローカルコードは間違いなく新しい状態です。しかし、この「今」の状態は一度きりです。コードを書き始めるたびに、ローカルブランチのコードが新しいことを確認する必要があります。古いコードに基づいて開発を行うと、競合の無限のサイクルに陥ってしまうからです。
gitフェッチ アップストリーム もちろん、メインブランチに直接コードを書くのはお勧めしません。最初のPRをメインブランチから送信するのは全く問題ありませんが、同時に2つのPRを送信する必要がある場合はどうでしょうか?つまり、開発作業を完了するには、feat-xxxやfix-xxxなど、より読みやすいブランチを追加することをお勧めします。
gitチェックアウト-b feat-xxx こうして、上流のメインブランチのコードと同一の機能ブランチ feat-xxx ができました。これで、安心してコードを書き始めることができます! 5.4 ステップ4: コードを書く言うことはあまりないので、ただ書いてください。 5.5 ステップ5: コミットとプッシュ
git add <ファイル> もちろん、これらのコマンドとパラメータの意味を理解し、柔軟に調整する必要があります。例えば、`git add --all` を使って追加ステップを完了したり、プッシュ時に `-f` パラメータを追加してリモートブランチを強制的に上書きしたりできます(リモートブランチが既に存在するものの、コミット履歴が期待どおりでない場合)。ただし、`git commit` には `-s` パラメータを追加する必要があることに注意してください。 コミットに IDE を使用することに慣れている場合は、次のようにしてもまったく問題ありません。 GolandとのDevStreamコミット ここでコミットメッセージの仕様に注意することが重要です。要件はプロジェクトごとに異なる可能性があるためです。例えば、DevStreamの仕様[11]は次のような形式になっています。 <type>[オプションのスコープ]: <description> 以下にいくつかの例を挙げます。
コミットとプッシュのステップは、IDE内で1つのステップで実行することも、別々に実行することもできます。私は柔軟性を高めるために、別々に実行することを好みます。また、コマンドライン操作も好みます。
オブジェクトをカウントしています: 80 、 完了。 この時点で、ローカルコミットはリモートリポジトリにプッシュされています。 5.6. ステップ6: PR(広報ファイル)を開くプッシュ操作が完了すると、GitHub を開くと、プル リクエストを開くことができることを知らせる黄色の通知ボックスが表示されます。 比較とプルリクエスト このボックスが表示されない場合は、feat-1 ブランチに直接切り替えて、下の「貢献」ボタンをクリックして PR を開始するか、「問題」の横にある「プル リクエスト」をクリックして対応するページに移動できます。
DevStream プルリクエスト ここでは適切なタイトル(デフォルトはコミットメッセージと同じ)を入力し、テンプレートに従ってPRの説明を入力します。PRテンプレートはオープンソースプロジェクトごとに若干異なるため、基本的な間違いを避けるために上記の内容を注意深く読む必要があります。 たとえば、DevStream テンプレートは現在 4 つの部分に分かれています。
このテンプレートは複雑ではないので、直接入力するだけです。
DevStream プルリクエストテンプレート 次に、右下にある「プルリクエストを作成」をクリックして、PRの作成を完了します。ただし、ここではこのボタンをクリックできません。これは、デモに使用している変更が意味をなさず、アップストリームのコードベースにマージできないためです。それでも、PR作成後の効果をお見せしたいと思います。pr655[12]を例に挙げましょう。 DevStream プルリクエスト 655 これは先月提出したPRで、基本的にテンプレートと同じ形式です。テンプレートの内容に加えて、「テスト」セクションが追加されていることに気づいたかもしれません。そうです、テンプレートは静的なものではなく、コミュニケーションコストを削減するためのものです。「より明確な方向性」を示している限り、適宜調整していただいて構いません。今回は、レビュー担当者にローカルで徹底的にテストしたことを伝え、自信を持ってマージできるようにするために、「テスト」セクションに詳細なローカルテスト結果記録を追加しました。 PRを送信すると、PRリストに表示されます。この時点で、すべてのCIチェックがパスしているかどうかを確認する必要があります。もしパスしなかった場合は、速やかに修正する必要があります。DevStreamを例に挙げると、CIチェックはおおよそ以下のようになります。 DevStream CI チェック 5.7 ステップ7: PRのマージPR が完璧で議論の余地がない場合は、プロジェクト管理者が短時間で PR を直接マージし、PR のライフサイクルが終了します。 しかし、確かに「しかし」があります。最初のPRは必ずしもスムーズに進むとは限りません。次に、よくある問題とその解決策について詳しくご紹介します。 6. PR を送信した後、問題 A、B、C、D、E、F、G が発生しました...😭多くの場合、PRは提出後すぐにはマージされません。レビュアーから様々な修正提案があったり、PR自体にフォーマット上の問題があったり、CIチェックで直接エラーが報告されたりすることがあります。どうすれば解決できるでしょうか?続きをお読みください。 6.1. レビュアーから修正案がいくつか提示されました。PRを更新するにはどうすればよいですか?PRを送信した後、コミットを追加する必要があることがよくあります。例えば、送信後にコードにまだ問題が残っていることがわかり、変更を加えたい場合や、レビュアーから改善の提案があり、コードを更新する必要がある場合などです。 私たちは通常、次のような慣例に従っています。レビュー開始前は、コードを更新する際に新しいコミットレコードを作成しないようにします。つまり、コミットレコードが明確で意味のあるものになるように、可能な限りマージします。レビュー開始後は、レビュアーのコメントに応じて生成された新しいコミットはマージする必要がなくなり、2回目のレビュー作業をより的確に行うことができます。 ただし、コミュニティによって要件は異なります。オープンソースプロジェクトによっては、PRに含めることができるコミットを1つだけに制限している場合もあります。実際の状況に応じて柔軟に判断してください。 PR の更新方法に戻ると、ローカルでコードの変更を続け、最初のコミットと同じ手順に従って、次のコマンドを実行するだけです。 git add <ファイル> この時点では、`origin` の `feat-xxx` ブランチにプッシュしているにもかかわらず、GitHub は実際にはすべての新しいコミットをマージされていない PR に追加します。そうです、プッシュを続けるだけで PR は自動的に更新されます。 コミットをマージする方法については、次のサブセクションで詳しく説明します。 6.2 コミットが多すぎる場合やレコードが整理されていない場合にコミットをマージするにはどうすればよいでしょうか?多くの場合、コミットをマージする必要があります。例えば、最初のコミットで100行のコードを変更した後、1行変更し忘れたことに気づき、別のコミットをコミットした場合、2番目のコミットは「面白みに欠ける」ものになってしまうため、マージする必要があります。 6.2.1 Gitコマンドラインによるコミットのマージ 例えば、ここに同じ名前のコミットが2つあります。2つ目のコミットでは句読点が1つだけ変更されています。 マージされるコミット この時点で、 rebase コマンドを使用して 2 つのコミットをマージできます。 git rebase -i HEAD~2 このコマンドを実行すると、デフォルトでVim編集モードの編集ページが開きます。内容は以下のようになります。 3114c0f ドキュメントを選択: ちょうど fortest 2 番目の選択肢を 's' に変更し、保存して終了する必要があります (Vim の `wq` コマンドを使用)。 3114c0f ドキュメントを選択: ちょうど fortest 次に、2 番目の編集ページに入ります。 # これは 2 つのコミットの組み合わせです。 このセクションでは、マージされたコミットメッセージを編集します。冗長な部分を削除し、以下の数行のみを残します。 ドキュメント: ちょうどテスト 次に、Vimで保存して終了します。この時点で、ログが表示されます。 [分離したHEAD 80f5e57] ドキュメント: just fortest この時点で、`git log` コマンドを使用して、コミット履歴が期待どおりかどうかを確認できます。 リベース さて、コミットがマージされたことをローカルで確認できたので、リモート リポジトリにプッシュして PR も更新できます。 git push -f origin feat-xxx ここでは、強制的に更新するために -f パラメータが必要です。コミットのマージは本質的に競合であり、リモートリポジトリの古いコミットレコードを上書きする必要があります。 6.2.2 IDEでのコミットのマージ もちろん、コミットをマージするためにグラフィカルな方法を使用することもできます。
すると、次のページが表示されます。 ゴランドとのスカッシュ これはコミットメッセージを編集するためのグラフィカルインターフェースです。お好みに合わせて変更し、右下の「OK」ボタンをクリックすれば完了です。 ゴランドとのスカッシュ ご覧のとおり、 2 つのコミットが「マージ」され、「完全に作り変えられた」新しいコミットになりました。 6.3. PR 中に発生した競合をどうやって解決するか?競合はオンラインでもローカルでも解決できます。1 つずつ見ていきましょう。 6.3.1 オンライン紛争解決 競合はできる限り避け、コードを書く前にローカルコードを更新する習慣を身につけるべきです。しかし、競合を完全に避けることはできません。PRが数日間ブロックされ、誰かが同じコード行を修正して先にマージしてしまうこともあります。この場合、PRには以下のような競合が発生します(繰り返しますが、現時点では上流プロジェクトで競合を発生させることはできないため、以下のデモで使用した競合は私のリポジトリで作成したものです)。 紛争が起こった このページを見るといつも背筋が凍ります。「競合を解決」ボタンをクリックすると、競合の詳細が表示されます。 競合ファイル これで、競合している行が具体的に確認できました。次のステップは、競合を解決することです。`<<<<<<<`、`>>>>>>>`、`=======` タグをすべて削除し、必要なコンテンツだけを残します。手順は以下のとおりです。 紛争解決 次に、右上隅の「解決済みとしてマーク」をクリックします。 解決済みとしてマーク 最後に、「マージをコミット」をクリックします。 これにより競合が解決され、新しいコミットが生成されたことがわかります。 紛争解決 紛争は解決されました。 6.3.2 ローカル紛争解決 多くの場合、競合が多すぎる場合や複雑すぎる場合は特に、競合をローカルで解決する必要があります。 同様に、競合を構築し、今回はローカルで解決を試みます。
紛争が起こった
# まず、メインブランチに戻ります この時点で、ローカルのメインブランチはリモート(または上流)のメインブランチと完全に整合性が取れています。次に必要なのは、メインブランチのコードを独自のフィーチャーブランチにマージし、競合を解決することです。 gitチェックアウト feat-1
まず、ヘッドを巻き戻してその上で作業を再生します... 競合を解決する必要があります。README.md を開き、競合しているセクションを見つけて修正するだけです。ここでの方法は、前のセクションで紹介したオンライン競合解決方法と全く同じなので、詳細は省略します。 コードには最終的な内容のみが保持され、その後 git コマンドが実行されます。 紛争解決 まだ完全に確信が持てない場合は、`git log` コマンドを使用してコミット履歴を確認してください。 コミット履歴 ここで、「conflict test 2」はメインブランチへのコミット記録です。「conflict test 1」より少し遅い時間ですが、こちらが先にマージされています。リベース操作後、この記録が最初に表示され、その次にフィーチャーブランチの「conflict test 1」が続きます。これは一見調和が取れているように見えます。この変更をリモートリポジトリにプッシュし続けます。このコマンドは既に何度も使用されています。 git push -f origin feat-xxx GitHubに戻ってPRを確認すると、競合が解決され、追加のコミットレコードが生成されていないことがわかります。つまり、このPRのコミット履歴は非常にクリーンで、競合が発生しなかったかのようになっています。 オンラインで競合を解決するのが適切か、ローカルで解決するのが適切かは、競合解決の記録を保持する必要性に対する人々の考え方によって異なります。コミュニティによって理解は異なります。確立されたオープンソースコミュニティでは、オンラインでの競合解決で生成されるマージ記録は実質的に「役に立たない」ため、ローカルでの競合解決を好む場合があります。DevStreamとDevLakeのコミュニティに関しては、後者のアプローチを推奨しますが、必須ではありません。 6.4 CI チェックが失敗します: コミット メッセージ関連の問題を修正するにはどうすればよいですか?コミットメッセージのガイドラインについては以前説明しましたが、初めてPRを送信する際には間違いを犯しやすいものです。例えば、「feat: xxx」はCIチェックを通過する可能性がありますが、「feat: Xxx」は通過しません。誤って間違ったコミットメッセージでPRを送信してしまったとしたら、どうすれば修正できるでしょうか?
gitコミット--amend このコマンドを実行すると編集ページに入り、コミットメッセージを自由に更新できます。変更を加えたら、プッシュを続けます。 git push -f origin feat-xxx これにより、PR 内のコミット メッセージが更新されます。 6.5 CI チェックが失敗しました: DCO(sign) の問題を修正するにはどうすればよいでしょうか?多くのオープンソース プロジェクトでは、すべてのマージされたコミットに次のような行が含まれている必要があります。 ダニエル・フー <[email protected]> したがって、コミット メッセージは次のようになります。 特技: ここで説明 この行の情報は、基本的に対応するコミットの作成者の署名です。このような署名を追加するのは非常に簡単で、`git commit` コマンドの後に `-s` パラメータを追加するだけです。例えば、`git commit -s -m "some description here"` と実行すれば、コミットに署名が追加されます。 しかし、最初のPRのコミットにSigned-off-byを追加するのを忘れた場合はどうなるでしょうか?この場合、対応するオープンソースプロジェクトにDCOチェックが設定されている場合[13]、PRはCIチェックで適切に署名されていないことが「検出」されます。 まず、署名されていないコミットを作成します。 画像の説明を追加してください。 署名なしでコミット DCO にエラーをスローさせる方法を示すためにこれを DevStream プロジェクトのコードベースに直接プッシュすることはできませんが、PR を送信すると、結果は次のようになります。 DCO エラーでコミット これをどうやって解決するか見てみましょう:
このシンプルなコマンドは、Signed-off-by メッセージを最新のコミットに直接追加します。このコマンドを実行すると、以下のデフォルト画像に示すように、コミットメッセージの編集ページに直接移動します。 ドキュメント: dco テスト この時点でコミットメッセージを変更できます。変更しない場合は、保存して終了するだけで、署名情報は自動的に追加されます。 署名後、何が起こるでしょうか?もちろん、強制的に押し進める段階です。 git push -f origin feat-xxx これにより、PR 内の DCO エラーが自然に修正されます。 VII. 最後におっと、ちょっと長くなっちゃったね。さて、今回はここまで!
|