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

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

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

We will go together through an example to show the typical workflow and associated Git commands. But first, let's have a quick look at the organization of Godot's Git repository.

Gitソースリポジトリ

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

注釈

ドキュメントに貢献する場合は、そのリポジトリは こちら にあります。

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

参考

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

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

  • master ブランチは、次のメジャーバージョンの開発が行われる場所です。開発ブランチとして、それは不安定であり、生産で使用するためのものではありません。ここでPRを優先して行う必要があります。
  • The stable branches are named after their version, e.g. 3.1 and 2.1. They are used to backport bugfixes and enhancements from the master branch to the currently maintained stable release (e.g. 3.1.2 or 2.1.6). As a rule of thumb, the last stable branch is maintained until the next major version (e.g. the 3.0 branch was maintained until the release of Godot 3.1). If you want to make PRs against a maintained stable branch, please check first if your changes are also relevant for the master branch, and if so make the PR for the master branch in priority. Release managers can then cherry-pick the fix to a stable branch if relevant.
  • There might occasionally be feature branches, usually meant to be merged into the master branch at some time.

フォークとクローン作成

最初のステップは、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

This will create a reference named upstream pointing to the original godotengine/godot repository. This will be useful when you want to pull new commits from its master branch to update your fork. You have another remote reference named origin, which points to your fork (USERNAME/godot).

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

注釈

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

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

In the following, we will assume as an example that you want to implement a feature in Godot's project manager, which is coded in the editor/project_manager.cpp file.

ブランチ

デフォルトでは、 `` 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

Be sure to always go back to the master branch before creating a new branch, as your current branch will be used as the base for the new one. Alternatively, you can specify a custom base branch after the new branch's name:

$ git checkout -b my-new-feature master

ブランチの更新

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

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

$ git pull --rebase upstream master

The --rebase argument will ensure that any local changes that you committed will be re-applied on top of the pulled branch, which is usually what we want in our PR workflow. This way, when you open a pull request, your own commits will be the only difference with the upstream master branch.

While rebasing, conflicts may arise if your commits modified code that has been changed in the upstream branch in the meantime. If that happens, Git will stop at the conflicting commit and will ask you to resolve the conflicts. You can do so with any text editor, then stage the changes (more on that later), and proceed with git rebase --continue. Repeat the operation if later commits have conflicts too, until the rebase operation completes.

If you're unsure about what is going on during a rebase and you panic (no worry, we all do the first few times), you can abort the rebase with git rebase --abort. You will then be back to the original state of your branch before calling git pull --rebase.

注釈

If you omit the --rebase argument, you will instead create a merge commit which tells Git what to make of the two distinct branches. If any conflicts arise, they would be resolved all at once via this merge commit.

While this is a valid workflow and the default behavior of git pull, merge commits within PRs are frowned upon in our PR workflow. We only use them when merging PRs into the upstream branch.

The philosophy is that a PR should represent the final stage of the changes made to the codebase, and we are not interested in mistakes and fixes that would have been done in intermediate stages before merging. Git gives us great tools to "rewrite the history" and make it as if we got things right the first time, and we're happy to use it to ensure that changes are easy to review and understand long after they have been merged.

If you have already created a merge commit without using rebase, or have made any other changes that have resulted in undesired history, the best option is to use an interactive rebase on the upstream branch. See the dedicated section for instructions.

ちなみに

If at any time you want to reset a local branch to a given commit or branch, you can do so with git reset --hard <commit ID> or git reset --hard <remote>/<branch> (e.g. git reset --hard upstream/master).

Be warned that this will remove any changes that you might have committed in this branch. If you ever lose commits by mistake, use the git reflog command to find the commit ID of the previous state that you would like to restore, and use it as argument of git reset --hard to go back to that state.

変更を行う

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

By default, those changes are unstaged. The staging area is a layer between your working directory (where you make your modifications) and the local Git repository (the commits and all the metadata in the .git folder). To bring changes from the working directory to the Git repository, you need to stage them with the git add command, and then to commit them with the git commit command.

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

  • 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 commit --amend lets you amend the last commit with your currently staged changes (added with git add). This is the best option if you want to fix a mistake in the last commit (bug, typo, style issue, etc.).
  • git log には、現在のブランチの最後のコミットが表示されます。ローカルコミットを行った場合は、上部に表示する必要があります。
  • git show は、最後のコミットの変更を表示します。コミットハッシュを指定して、そのコミットの変更を確認することもできます。

覚えておくべきことがたくさんあります!心配する必要はありません。変更が必要な場合はこのチートシートを確認し、実行して学習してください。

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

# 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でフォークのページを確認すると、コミットが追加された新しいブランチが表示されます。

プルリクエストの発行

When you load your fork's branch on GitHub, you should see a line saying "This branch is 2 commits ahead of godotengine:master." (and potentially some commits behind, if your master branch was out of sync with the upstream master branch).

../../_images/github_fork_make_pr.png

On that line, there is a "Pull request" link. Clicking it will open a form that will let you issue a pull request on the godotengine/godot upstream repository. It should show you your two commits, and state "Able to merge". If not (e.g. it has way more commits, or says there are merge conflicts), don't create the PR, something went wrong. Go to IRC and ask for support :)

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

However, be aware that in our PR workflow, we favor commits that bring the codebase from one functional state to another functional state, without having intermediate commits fixing up bugs in your own code or style issues. Most of the time, we will prefer a single commit in a given PR (unless there's a good reason to keep the changes separate), so instead of authoring a new commit, considering using git commit --amend to amend the previous commit with your fixes. The above example would then become:

# 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
# --amend will change the previous commit, so you will have the opportunity
# to edit its commit message if relevant.
$ git commit --amend
# As we modified the last commit, it no longer matches the one from your
# remote branch, so we need to force push to overwrite that branch.
$ git push --force origin better-project-manager

The interactive rebase

If you didn't follow the above steps closely to amend changes into a commit instead of creating fixup commits, or if you authored your changes without being aware of our workflow and Git usage tips, reviewers might request of your to rebase your branch to squash some or all of the commits into one.

Indeed, if some commits have been made following reviews to fix bugs, typos, etc. in the original commit, they are not relevant to a future changelog reader who would want to know what happened in the Godot codebase, or when and how a given file was last modified.

To squash those extraneous commits into the main one, we will have to rewrite history. Right, we have that power. You may read that it's a bad practice, and it's true when it comes to branches of the upstream repo. But in your fork, you can do whatever you want, and everything is allowed to get neat PRs :)

We will use the interactive rebase git rebase -i to do this. This command takes a commit ID or a branch name as argument, and will let you modify all commits between that commit/branch and the last one in your working branch, the so-called HEAD.

While you can give any commit ID to git rebase -i and review everything in between, the most common and convenient workflow involves rebasing on the upstream ``master`` branch, which you can do with:

$ git rebase -i upstream/master

注釈

Referencing branches in Git is a bit tricky due to the distinction between remote and local branches. Here, upstream/master (with a /) is a local branch which has been pulled from the upstream remote's master branch.

Interactive rebases can only be done on local branches, so the / is important here. As the upstream remote changes frequently, your local upstream/master branch may become outdated, so you can update it with git fetch upstream master. Contrarily to git pull --rebase upstream master which would update your currently checked out branch, fetch will only update the upstream/master reference (which is distinct from your local master branch... yes it's confusing, but you'll become familiar with this little by little).

This will open a text editor (vi by default, see Git docs <https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration#_core_editor>__ to configure your favorite one) with something which may look like this:

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つだけであることが確認されます。

だがしかし!履歴を書き直した結果、ローカルブランチとリモートブランチが分岐しました。実際に、上記の例のコミット: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://akien-mga@github.com/akien-mga/godot'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart.

This is a sane behavior, Git will not let you push changes that would override remote content. But that's actually what we want to do here, so we will have to force it:

$ 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

You can also delete the remote branch from the GitHub PR itself, a button should appear once it has been merged or closed.