プルリクエスト・ワークフロー

Godotが使用するいわゆる「PRワークフロー」は、Gitを使用する多くのプロジェクトに共通しており、ベテランのフリーソフトウェア貢献者には馴染み深いはずです。アイデアは、少数(もしある場合)だけがmasterブランチに直接コミットすることです。代わりに、寄稿者フォークプロジェクト(つまり、必要に応じて変更できるコピーを作成する)を使用し、GitHubインターフェイスを使用して、フォークの分岐の1つから元のブランチ (多くの場合はupstream) リポジトリの1つのブランチにプルをリクエストします。

結果のプルリクエスト(PR) は、他の貢献者によって確認され、承認、拒否、またはほとんどの場合、変更の実行を要求できます。承認されると、PRはコア開発者の1人によってマージされ、そのコミットはターゲットブランチ (通常はmasterブランチ) の一部になります。

一般的なワークフローと関連するGit コマンドを示す例をまとめます。しかし、まず、GodotのGitリポジトリの組織を簡単に見てみましょう。

Gitソースリポジトリ

GitHubのリポジトリは、組み込みの問題追跡システムとPRシステムを備えたGitコードリポジトリです。

注釈

If you are contributing to the documentation, its repository can be found here.

Gitバージョン管理システムは、ソースコードへの連続した編集を追跡するために使用されるツールです - Godotに効率的に貢献するために、Gitコマンドラインの基本を学ぶことは強く推奨されます。Gitにはグラフィカルインターフェイスがいくつかありますが、通常はGitとPRワークフローに関する悪い習慣をユーザーに奨励するため、使用しないことをお勧めします。特に、GitHubのオンラインエディタをコードの投稿に使用しないことをお勧めします (ただし、小さな修正やドキュメントの変更には許容されます)、ファイルごとに1つのコミットが適用され、変更ごとに1つのコミットが適用され、読み取り不能なGit履歴を持つPRがすぐに発生します(特にピアレビューの後)。

参考

Gitの「ブック」の最初のセクションは、ツールの哲学と、毎日のワークフローで習得する必要があるさまざまなコマンドの優れた紹介です。Git SCMWebサイトでオンラインで読むことができます。

Gitリポジトリ上のブランチは、次のように編成されています:

  • master ブランチは、次のメジャーバージョンの開発が行われる場所です。開発ブランチとして、それは不安定であり、生産で使用するためのものではありません。ここでPRを優先して行う必要があります。
  • 安定したブランチは、バージョンにちなんで名前が付けられます(例:3.12.1)。これらは、master ブランチから現在維持されている安定したリリース (3.1.2 や 2.1.6 など) にバグ修正と機能強化をバックポートするために使用されます。経験則として、最後の安定したブランチは次のメジャー バージョンまで維持されます (例: 3.0 ブランチは Godot 3.1 のリリースまで維持されていました)。維持されている安定したブランチに対してPRを行いたい場合、変更が master ブランチにも関連しているかどうかを確認する必要があります。
  • 機能ブランチが存在する場合がありますが、通常はいつか master ブランチにマージされることを意図しています。

フォークとクローン作成

最初のステップは、GitHubでgodotengine / godotリポジトリをフォークすることです。 そのためには、GitHubアカウントが必要であり、ログインする必要があります。リポジトリのGitHubページの右上隅に、以下に示すように「フォーク」ボタンが表示されます:

../../_images/github_fork_button.png

これをクリックすると、しばらくするとGitHubのユーザ名を名前空間として、Godot repoの自分のフォークにリダイレクトされるはずです:

../../_images/github_fork_url.png

次に、forkのクローンを作成します。つまり、オンラインリポジトリのローカルコピーを作成します(Gitの言葉を使えば、元のリモート)。まだGitをダウンロードしていない場合は、WindowsまたはmacOSを使用している場合はそのWebサイトからダウンロードし、Linuxを使用している場合はパッケージマネージャからインストールします。

注釈

Windowsを使用している場合は、Git Bashを開いてコマンドを入力します。macOSとLinuxユーザーはそれぞれの端末を使用できます。

GitHubからフォークのクローンを作成するには、次のコマンドを使用します:

$ git clone https://github.com/USERNAME/godot

注釈

この例では、「$」文字は、一般的なUNIXシェルのコマンドラインプロンプトを示します。これはコマンドの一部ではないため、入力しないでください。

しばらくすると、現在の作業ディレクトリに godot ディレクトリが表示されます。 cd コマンドを使用して移動します:

$ cd godot

まず、フォークした元のリポジトリへの参照を設定します:

$ git remote add upstream https://github.com/godotengine/godot
$ git fetch upstream

これにより、元の godotengine/godot リポジトリを指す upstream という名前の参照が作成されます。これは、フォークを更新するために master ブランチから新しいコミットをプルする場合に便利です。フォークを指す origin という名前の別の remote 参照があります。

ローカルの godot フォルダを保持している限り、上記の手順を実行する必要があるのは1回だけです(必要に応じて移動できますが、関連するメタデータは .git サブフォルダに隠されています)。

注釈

ブランチ、プル、コーディング、ステージング、コミット、プッシュ、リベース...テクノロジー。

Daft PunkのTechnologicに対するこの悪い見方は、Git初心者がワークフローについて持っている一般的な概念を示しています。コピーアンドペーストで学ぶ多くの奇妙なコマンドは、期待どおりに機能することを望んでいます。 そして、あなたが好奇心が強く、迷ったときに検索エンジンに質問することを躊躇しない限り、それは実際に学ぶための悪い方法ではないので、Gitで作業するときに知る基本的なコマンドを提供します。

以下では、 editor/project_manager.cpp ファイルにコーディングされている機能をGodotのプロジェクトマネージャーに実装することを想定しています。

ブランチ

デフォルトでは、 `` git clone`` はフォークの master ブランチ( `` origin`` )に置かれているはずです。 独自の機能開発を開始するために、機能ブランチを作成します:

# Create the branch based on the current branch (master)
$ git branch better-project-manager

# Change the current branch to the new one
$ git checkout better-project-manager

このコマンドは、次のコマンドと同等です:

# Change the current branch to a new named one, based on the current branch
$ git checkout -b better-project-manager

master ブランチに戻す場合は、次の方法を使用します:

$ git checkout master

git branch コマンドを使用して現在どのブランチに入っているかを確認できます:

$ git branch
  2.1
* better-project-manager
  master

ブランチの更新

これは最初は必要ありません(アップストリームリポジトリをフォークした直後)。しかし、次に何かをしたいときには、あなたのフォークの master が上流の master ブランチの後ろにいくつかのコミットがあることに気づくでしょう。その間に他の貢献者からのプルリクエストがマージされているでしょう。

開発する機能と現在のアップストリーム master ブランチとの間に競合が生じないようにするには、アップストリームブランチをプルによってブランチを更新する必要があります。

$ git pull upstream master

ただし、もしローカル コミットがあり、このメソッドがいわゆる「マージ コミット」を作成し、すぐに仲間の貢献者からそれらがPRで望まれないと聞いたとします。マージコミットを作成せずにブランチを更新するには、--rebase オプションを使い、ローカルコミットが更新されたアップストリーム master ブランチの上で再生できるようにする必要があります。ブランチの Git履歴は効果的に変更されますが、それはより大きな利益のためです。

したがって、(ほとんど)常に使用する必要があるコマンドは次のとおりです:

$ git pull --rebase upstream master

If you have already pushed the merge commit without using rebase, or have made any other changes that have resulted in undesired history, you may use a hard reset to revert to a specific commit and try again:

$ git reset --hard [The ID of the last desired commit]

Once you have done this, you may run --rebase to merge master correctly.

If you have already pushed the wrong commits to your remote branch, you will have to force push by using git push --force.

警告

git reset --hard can be a dangerous operation, especially if you have untracked or uncommitted changes. However, if you have committed changes that you reset using git reset --hard, you may still be able to recover them by resetting to a commit ID found with the git reflog command.

変更を行う

次に、通常の開発環境(テキストエディタ、IDEなど) を使用して、例の editor/project_manager.cpp ファイルに変更を加えます。

デフォルトでは、これらの変更はステージングされていません。 ステージング領域は、作業ディレクトリ(変更を行う場所)とローカルgitリポジトリ(コミットおよび .git フォルダ内のすべてのメタデータ)の間のレイヤーです。 作業ディレクトリからGitリポジトリに変更を反映するには、 git add コマンドで変更をステージングし、次に git commit コマンドでコミットする必要があります。

現在の作業を確認するために、ステージングする前、ステージング中、コミット後に確認するために知っておくべきさまざまなコマンドがあります。

  • git diff には、現在のステージングされていない変更、つまり作業ディレクトリとステージング領域の違いが表示されます。
  • git checkout -- <files> は、指定されたファイルに対するステージングされていない変更を取り消します。
  • git add <files> は、リストされたファイルの変更をステージングします。
  • git diff --staged は、現在のステージングされた変更、すなわちステージング領域と最後のコミットの違いが表示されます。
  • git reset HEAD <files> は、リストされたファイルをステージ解除します。
  • git status は、現在ステージングされ、ステージングされていない変更が何であるかを示します。
  • git commit はステージングされたファイルをコミットします。 テキストエディタが開き(Gitの GIT_EDITOR 環境変数または core.editor 設定で使用するエディタを定義できます)、コミットログを書き込むことができます。 git commit -m "Cool commit log" を使用して、ログを直接書き込むことができます。
  • git log には、現在のブランチの最後のコミットが表示されます。ローカルコミットを行った場合は、上部に表示する必要があります。
  • git show は、最後のコミットの変更を表示します。コミットハッシュを指定して、そのコミットの変更を確認することもできます。

That's a lot to memorize! Don't worry, just check this cheat sheet when you need to make changes, and learn by doing.

この例では、シェルの履歴がどのように見えるかを次に示します:

# It's nice to know where you're starting from
$ git log

# Do changes to the project manager with the nano text editor
$ nano editor/project_manager.cpp

# Find an unrelated bug in Control and fix it
$ nano scene/gui/control.cpp

# Review changes
$ git status
$ git diff

# We'll do two commits for our unrelated changes,
# starting by the Control changes necessary for the PM enhancements
$ git add scene/gui/control.cpp
$ git commit -m "Fix handling of margins in Control"

# Check we did good
$ git log
$ git show
$ git status

# Make our second commit
$ git add editor/project_manager.cpp
$ git commit -m "Add a pretty banner to the project manager"
$ git log

これにより、 better-project-manager ブランチに master ブランチにはない2つの新しいコミットが必要になります。 ただし、それらはまだローカルのみであり、リモートフォークはそれらを認識せず、アップストリームリポジトリも認識しません。

変更をリモートにプッシュする

そこで git push を使用します。Gitでは、コミットは常にローカルリポジトリで行われます(コミットがリモートリポジトリを直接変更するSubversionとは異なります)。 新しいコミットをリモートブランチにプッシュして、それらを世界と共有する必要があります。 この構文は次のとおりです:

$ git push <remote> <local branch>[:<remote branch>]

この例では、ローカル ブランチと同じ名前を付ける場合は、リモート ブランチに関する部分を省略できます:

$ git push origin better-project-manager

Gitはユーザー名とパスワードを尋ね、変更内容はリモートに送信されます。GitHubでフォークのページを確認すると、コミットが追加された新しいブランチが表示されます。

プルリクエストの発行

フォークのブランチをGitHubにロードすると、 「このブランチはgodotengine:masterより2コミット先です」 という行が表示されます( master ブランチがアップストリーム master ブランチと同期していない場合、潜在的にいくつかのコミットが遅れます)。

../../_images/github_fork_make_pr.png

その行には、「プルリクエスト」リンクがあります。クリックすると、godotengine/godotアップストリームリポジトリでプルリクエストを発行できるフォームが開きます。2つのコミットを表示し、「マージ可能」を示す必要があります。そうでない場合(例えば、より多くのコミットがある場合、またはマージの競合があると言う)、PRを作成しないでください。IRCに移動し、サポートを依頼しましょう

PRに明示的なタイトルを使用し、必要な詳細をコメント領域に配置します。スクリーンショット、GIF、またはzipプロジェクトをドラッグアンドドロップして、作業内容を示すことができます。「プルリクエストを作成」をクリックし、じゃじゃーん!

プルリクエストの変更

他の貢献者によってレビューされていますが、貢献者が要求したか、テスト中に自分で問題を発見したため、まだマージされていないPRを変更する必要があります。

良いニュースは、プルリクエストを行ったブランチに基づいて操作するだけでプルリクエストを変更できることです。たとえば、そのブランチで新しいコミットを行い、フォークにプッシュすると、PRが自動的に更新されます:

# Check out your branch again if you had changed in the meantime
$ git checkout better-project-manager

# Fix a mistake
$ nano editor/project_manager.cpp
$ git add editor/project_manager.cpp
$ git commit -m "Fix a typo in the banner's title"
$ git push origin better-project-manager

これでうまくいくはずですが、...

PRワークフローのマスタリング: リベース

上記の状況では、Gitの歴史に関して特に熱心な仲間は、最後の2つのコミット(プロジェクトマネージャーに関連する2つ)をsquashまたはmeldリベースするように頼むかもしれません。 2番目のコミットは基本的に最初のコミットの問題を修正するためです。

PRがマージされると、PR作成者がミスをしたことは、変更ログの読者には関係ありません。 代わりに、ある作業状態から別の作業状態に移行するコミットのみを保持する必要があります。

これら2つのコミットをまとめてつぶすには、履歴を書き換える必要があります。 私たちにはその力があります。 あなたはそれが悪い習慣であると読むかもしれません、そしてそれはアップストリームレポのブランチに関しては本当です。 しかし、あなたのフォークでは、あなたがやりたいことが何でもでき、すべてがきちんとしたPRを得ることができます:)

これを行うには、インタラクティブなリベース git rebase -i を使用します。 このコマンドは引数としてコミットハッシュを受け取り、そのコミットハッシュとブランチの最後の1つ、いわゆるHEADとの間のすべてのコミットを変更できるようにします。 この例では、最後の2つのコミットに基づいてアクションを実行するため、次のことを行います:

# The HEAD~X syntax means X commits before HEAD
$ git rebase -i HEAD~2

これにより、次のテキストエディタが開きます:

pick 1b4aad7 Add a pretty banner to the project manager
pick e07077e Fix a typo in the banner's title

エディタには、これらのコミットを実行する方法についての指示も表示されます。特に、「pick」とはそのコミットを使用すること(何もしない)を意味し、「squash」と「fixup」を使用してコミットを親コミットにマージできることを示します。「squash」と「fixup」の違いは、「fixup」はコミットログを圧縮されたコミットから破棄する点です。この例では、「Fix a typo(誤字を修正する)」コミットのログを保持することには関心がないので、以下を使用します:

pick 1b4aad7 Add a pretty banner to the project manager
fixup e07077e Fix a typo in the banner's title

エディタを保存して終了すると、リベースが発生します。2番目のコミットが1番目のコミットにマージされ、 git loggit show によって、前の2つのコミットから変更されたコミットが1つだけであることが確認されます。

注釈

タイプミスを修正するときに git commit --amend を使用すると、このリベースを回避できます。 このコマンドは、この例のように新しいコミットを作成する代わりに、ステージングされた変更を最後のコミット( HEAD )に直接書き込みます。 つまり、新しいコミットでリフィックスし、それを「fixup」としてマークすることと同じです。

だがしかし!履歴を書き直した結果、ローカルブランチとリモートブランチが分岐しました。実際に、上記の例のコミット:1b4aad7は変更され、新しいコミットハッシュを取得します。リモートブランチにプッシュしようとすると、エラーが発生します:

$ git push origin better-project-manager
To https://github.com/akien-mga/godot
 ! [rejected]        better-project-manager -> better-project-manager (non-fast-forward)
error: failed to push some refs to 'https://[email protected]/akien-mga/godot'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart.

これは正常な動作です。Gitでは、リモートコンテンツをオーバーライドする変更をプッシュできません。 しかし、実際にはここでやりたいことなので、強制する 必要があります:

$ git push --force origin better-project-manager

じゃじゃーん!Gitは、リモートブランチをローカルで持っていたもので喜んで置換します(したがって、 git log を使用して、それが目的のものであることを確認してください) これにより、それに応じてPRも更新されます。

Gitブランチの削除

プルリクエストがマージされた後、最後にやることが1つあります。PRのGitブランチを削除します。 ブランチを削除しなくても問題はありませんが、削除することをお勧めします。 これを2回行う必要があります。1回はローカルブランチ用、もう1回はGitHub上のリモートブランチ用です。

プロジェクトマネージャブランチをローカルで削除するベターな方法では、次のコマンドを使用します:

$ git branch -d better-project-manager

あるいは、ブランチがまだマージされておらず、とにかく削除したい場合は、 -d の代わりに -D を使用します。

次に、GitHub のリモートブランチを削除するには、次のコマンドを使用します:

$ git push origin -d better-project-manager