コピペで学ぶチュートリアル: GitHub Pull Request 3種類のmerge手法を試す
導入
GitHub の Pull Request をマージする方法には 3 種類、Merge pull request(Create a merge commit)、Squash and merge、Rebase and merge があります。
それぞれの違いについては公式ドキュメントでも、Qiita などでも解説されています。
-
GitHub Docs (公式):
-
Qiita:
すでに様々な場所で解説されている内容ですが、あらためて手を動かして動作確認すると、新しい発見があり、知識の整理もできると思います。そこで本記事では、これら 3 種類の違いを説明しつつ、コピペで貼り付けるだけで簡単に手順を再現できるようにしています。
準備: GitHub レポジトリの作成
まずはローカル環境で git レポジトリを作成します。
mkdir pull-req-merge-experiments
cd pull-req-merge-experiments
git init
GitHub 上にレポジトリを作成しましょう。Web ブラウザではなくコマンドを使えば一発で終わります。
gh repo create pull-req-merge-experiments --public --source=. --remote=origin
上記の gh コマンドは何?
gh コマンド(GitHub CLI)を使えば、GitHub 上でのリポジトリ作成など作業をローカルからコマンドひとつで行えます。まだインストールしていない方は、ぜひインストールを検討してください。gh repo create サブコマンドの説明にもありますが、上記のコマンドのオプションと引数の意味はこちらです。
-
pull-req-merge-experiments
: GitHub 上のレポジトリ名 -
--public
: public レポジトリとして作成 -
--source=.
: ローカルレポジトリのパスはカレントディレクトリ.
-
--remote=origin
リモートレポジトリを origin に指定
Merge pull request(Create a merge commit)
まずは 3 種類のマージ手法のうち、Merge commit から説明します。
最初のコミットで編集対象のファイル pull-req-merge-commit.txt
を作成します。
cat << EOF > pull-req-merge-commit.txt
a
b
c
EOF
git add --all
git commit -m "create pull-req-merge-commit.txt"
git push origin main
1 つめの Pull Request を作成しましょう。
git switch -c pr-merge-commit-1
sed -i 's/a/aaaaa/' pull-req-merge-commit.txt # ファイル中のaをaaaaaに置き換え
git add --all
git commit -m "update a in pr-merge-commit-1"
# GitHubにPull Requestを作成
git push --set-upstream origin pr-merge-commit-1
gh pr create --title pr-merge-commit-1 --body "" --base main --head pr-merge-commit-1
1 つめの Pull Request の File diff
次に 1 つめの Pull Request とコンフリクトを起こさないように 2 つめの Pull Request を作成します。
git switch -c pr-merge-commit-2 main
sed -i 's/b/bbbbb/' pull-req-merge-commit.txt # ファイル中のbをbbbbbに置き換え
git add --all
git commit -m "update b in pr-merge-commit-2"
sed -i 's/c/ccccc/' pull-req-merge-commit.txt # ファイル中のcをcccccに置き換え
git add --all
git commit -m "update c in pr-merge-commit-2"
# GitHubにPull Requestを作成
git push --set-upstream origin pr-merge-commit-2
gh pr create --title pr-merge-commit-2 --body "" --base main --head pr-merge-commit-2
2 つめの Pull Request の File diff
両方の Pull Request の状態を図解
これで merge commit の動作を確認するための Pull Request が 2 つ作成されたので、1 つめ Pull Request をマージします。
gh pr merge pr-merge-commit-1 --merge --delete-branch
1 つめの Pull Request マージ結果
これによって Merge commit が作成されます。
Merge commit の File diff は、1 つ目のブランチの直前のコミットの File diff と同じ内容です。
続いて 2 つめの Pull Request をマージします。
gh pr merge pr-merge-commit-2 --merge --delete-branch
# GitHub 側で更新された main ブランチを pull
git switch main
git pull origin main
2 つめの Pull Request マージ結果
これによって 2 つめの Merge commit が作成されます。
しかし GitHub 上でここまでの git log を確認すると、本来は分岐が発生しているにも関わらず、一直線にコミットが並んでいるように見え、かつ Merge commit2 つが連続しています。
この場合、下記のコマンドで確認すると分岐の様子がわかりやすくなります。
git log --oneline --decorate --graph
* 012ef98 Merge pull request #2 from richardimaoka/pr-merge-commit-2
|\
| * 30d1b5b update 3 in pr-merge-commit-2
| * 474cc81 update 2 in pr-merge-commit-2
* | 4f7c52c Merge pull request #1 from richardimaoka/pr-merge-commit-1
|\ \
| |/
|/|
| * 93924f6 update 1 in pr-merge-commit-1
|/
* 5ae629f create pull-req-merge-commit.txt
2 つめの Merge commit の File diff は、2 つ目のブランチの全てのコミットの File diff を合わせた内容です。
no-ff
オプションを使ったマージを試す
ローカル で GitHub Docs の「プルリクエストをマージする」を見ると、次の記述があります。これを確認してみましょう。
(create a merge commit では) プルリクエストは --no-ff オプションを使用してマージされますが…
ローカル環境で no-ff マージを試す手順
この no-ff
オプションの動作を確認して、本当に GitHub の merge commit と同じ動作なのかを試します。
まずは最初のコミットで編集対象のファイルを作成します。先程の git レポジトリをそのまま利用できます。
touch local-no-ff.txt
git add --all
git commit -m "create local-no-ff.txt"
作成したファイルを更新するコミットを行います。
git switch -c local-no-ff-1
echo a > local-no-ff.txt
git add --all
git commit -m "add a in branch local-no-ff-1"
echo b >> local-no-ff.txt
git add --all
git commit -m "add b in branch local-no-ff-1"
図解にするとこのような感じです。
git コミットの結果
no-ff
オプションを付けてgit merge
コマンドを実行しましょう
git switch main
git merge --no-ff --no-edit local-no-ff-1
git branch -d local-no-ff-1
git push origin main
no-ff
をつけてマージした結果はこちらのようになります。GitHub 上でのマージと同じように、Merge commit が作成されます。
git マージの結果
git log でも確認してみましょう。
git log --oneline --decorate --graph
* 0d471f0 Merge branch 'local-no-ff-1'
|\
| * 4d651ad add 2 in branch local-no-ff-1
| * 47b1b49 add 1 in branch local-no-ff-1
|/
* d6035c2 create local-no-ff.txt
ローカル環境でデフォルトのマージを試す手順
no-ff
との比較とのため、fast-forward マージ、つまりオプションを付けない git でのデフォルトのマージを試してみます。
まずは最初のコミットで編集対象のファイルを作成します。先程の git レポジトリをそのまま利用できます。
touch local-ff.txt
git add --all
git commit -m "create local-ff.txt"
作成したファイルを更新するコミットを行います。
git switch -c local-ff-1
echo a > local-ff.txt
git add --all
git commit -m "add a in branch local-ff-1"
echo b >> local-ff.txt
git add --all
git commit -m "add b in branch local-ff-1"
図解にするとこのような感じです。
git コミットの結果
git switch main
git merge local-ff-1
git branch -d local-ff-1
git push origin main
デフォルトのマージを行った結果はこちらのようになります。Merge commit は作成されず、main ブランチの先にもうひとつのブランチのコミットを連続してコミットしたような形になります。
git マージの結果
git log でも確認してみましょう。
git log --oneline --decorate --graph
* 6b1858f add 2 in branch local-ff-1
* 2a85d20 add 1 in branch local-ff-1
* 07beb58 create local-ff.txt
Squash merge
2 つめのマージ手法 Squash merge を説明します。
編集対象のファイルを作成します。
cat << EOF > pull-req-squash-merge.txt
a
b
c
EOF
git add --all
git commit -m "create pull-req-squash-merge.txt"
git push origin main
1 つめの Pull Request を作成しましょう。
git switch -c pr-squash-merge-1
sed -i 's/a/aaaaa/' pull-req-squash-merge.txt # ファイル中のaをaaaaaに置き換え
git add --all
git commit -m "update a in pr-squash-merge-1"
# GitHubにPull Requestを作成
git push --set-upstream origin pr-squash-merge-1
gh pr create --title pr-squash-merge-1 --body "" --base main --head pr-squash-merge-1
1 つめの Pull Request の File diff
次に 1 つめの Pull Request とコンフリクトを起こさないように 2 つめの Pull Request を作成します。
git switch -c pr-squash-merge-2 main
sed -i 's/b/bbbbb/' pull-req-squash-merge.txt # ファイル中のbをbbbbbに置き換え
git add --all
git commit -m "update b in pr-squash-merge-2"
sed -i 's/c/ccccc/' pull-req-squash-merge.txt # ファイル中のcをcccccに置き換え
git add --all
git commit -m "update c in pr-squash-merge-2"
# GitHubにPull Requestを作成
git push --set-upstream origin pr-squash-merge-2
gh pr create --title pr-squash-merge-2 --body "" --base main --head pr-squash-merge-2
2 つめの Pull Request の File diff
両方の Pull Request の状態を図解
これで merge commit の動作を確認するための Pull Request が 2 つ作成されましたので、1 つめ Pull Request をマージします。
gh pr merge pr-squash-merge-1 --squash --delete-branch
1 つめの Pull Request マージ結果
Merge commit は作成されません。また 1 つめのブランチには 1 つしか新たなコミットがなかったので、squash commit も行なわれません。
main ブランチの先に 1 つめのブランチのコミットを連続してコミットしたような形になります。
File diff とブランチはこのようになります。
続いて 2 つめの Pull Request をマージします。
gh pr merge pr-squash-merge-2 --squash --delete-branch
# GitHub 側で更新された main ブランチを pull
git switch main
git pull origin main
2 つめの Pull Request マージ結果
2 つめのブランチには複数のコミットが行われたので、squash commit が作成されます。
GitHub 上で git log を確認すると、squash コミット のみが追加されたことがわかります。
ローカルでも git log を確認してみましょう。
git log --oneline --decorate --graph
* f54088c pr-squash-merge-2 (#4)
* 6a003d3 update 1 in pr-squash-merge-1 (#3)
* f205052 create pull-req-squash-merge.txt
File diff とブランチはこのようになります。
Rebase merge
3 つめのマージ手法 Rebase merge を説明します。
編集対象のファイルを作成します。
cat << EOF > pull-req-rebase-merge.txt
a
b
c
EOF
git add --all
git commit -m "create pull-req-rebase-merge.txt"
git push origin main
1 つめの Pull Request を作成しましょう。
git switch -c pr-rebase-merge-1
sed -i 's/a/aaaaa/' pull-req-rebase-merge.txt # ファイル中のaをaaaaaに置き換え
git add --all
git commit -m "update a in pr-rebase-merge-1"
# GitHubにPull Requestを作成
git push --set-upstream origin pr-rebase-merge-1
gh pr create --title pr-rebase-merge-1 --body "" --base main --head pr-rebase-merge-1
1 つめの Pull Request の File diff
次に 1 つめの Pull Request とコンフリクトを起こさないように 2 つめの Pull Request を作成します。
git switch -c pr-rebase-merge-2 main
sed -i 's/b/bbbbb/' pull-req-rebase-merge.txt # ファイル中のbをbbbbbに置き換え
git add --all
git commit -m "update b in pr-rebase-merge-2"
sed -i 's/c/ccccc/' pull-req-rebase-merge.txt # ファイル中のcをcccccに置き換え
git add --all
git commit -m "update c in pr-rebase-merge-2"
# GitHubにPull Requestを作成
git push --set-upstream origin pr-rebase-merge-2
gh pr create --title pr-rebase-merge-2 --body "" --base main --head pr-rebase-merge-2
2 つめの Pull Request の File diff
両方の Pull Request の状態を図解
これで rebase merge の動作を確認するための Pull Request が 2 つ作成されましたので、1 つめ Pull Request をマージします。
gh pr merge pr-rebase-merge-1 --rebase --delete-branch
1 つめの Pull Request マージ結果
main ブランチの先に 1 つめのブランチのコミットを連続してコミットしたような形になります。
File diff とブランチはこのようになります。
続いて 2 つめの Pull Request をマージします。
gh pr merge pr-rebase-merge-2 --rebase --delete-branch
# GitHub 側で更新された main ブランチを pull
git switch main
git pull origin main
2 つめの Pull Request マージ結果
main ブランチの先に 2 つめのブランチのコミットを連続してコミットしたような形になります。
ローカルでも git log を確認してみましょう。
git log --oneline --decorate --graph
* c17e7a9 update 3 in pr-rebase-merge-2
* 5cd3768 update 2 in pr-rebase-merge-2
* 49dce5e update 1 in pr-rebase-merge-1
* e9bc068 create pull-req-rebase-merge.txt
File diff とブランチはこのようになります。
Discussion