コピペで学ぶチュートリアル: GitHub Pull Request の merge conflict 解決手法を試す
導入
GitHub の pull request で merge conflict が発生した際、その解決には 1.GitHub の Web UI 上で行うものと、2.ローカルのコンピュータで行うものがあります。
解決手法の違いについて手を動かして動作確認すると、知識の整理もできると思いますので、本記事ではほぼコピペで貼り付けるだけで簡単に手順を再現できるようにしています。
merge conflict がない場合の update 手法との区別
本記事で対象とするのは pull request の merge conflict を解決する手法なので、merge conflict が発生していないときに pull request を update する手法とは区別してください。後者については以下を参考にしてください。
pull request のマージ手法との区別
本記事で対象とするのは pull request『内』で merge conflict を解決する手法なので、pull request 自体を base ブランチへと merge する手法とは区別してください。後者については以下を参考にしてください。
準備: GitHub レポジトリの作成
まずはローカル環境で git レポジトリを作成します。
mkdir pull-req-conflict-experiments
cd pull-req-conflict-experiments
git init
GitHub 上にレポジトリを作成しましょう。Web ブラウザではなくコマンドを使えば一発で終わります。
gh repo create pull-req-conflict-experiments --public --source=. --remote=origin
上記の gh コマンドは何?
gh コマンド(GitHub CLI)を使えば、GitHub 上でのリポジトリ作成など作業をローカルからコマンドひとつで行えます。まだインストールしていない方は、ぜひインストールを検討してください。gh repo create サブコマンドの説明にもありますが、上記のコマンドのオプションと引数の意味はこちらです。
-
pull-req-udpate-experiments: GitHub 上のレポジトリ名 -
--public: public レポジトリとして作成 -
--source=.: ローカルレポジトリのパスはカレントディレクトリ. -
--remote=originリモートレポジトリを origin に指定
Merge conflict が発生しない場合
本記事は merge conflict 解決手法の動作確認が目的ですが、対比のためにまずは merge conflict が発生しない場合を見ていきましょう。
cat << EOF > experiment1.txt
a
b
c
EOF
git add --all
git commit -m "create experiment1.txt"
git push origin main
ファイルの内容はこちらです。
a
b
c
次に、pull request を作成します。
git switch -c pr-1
sed -i 's/a/aaaaa/' experiment1.txt # ファイル中のaをaaaaaに置き換え
git add --all
git commit -m "update a in pr-1"
git push --set-upstream origin pr-1
gh pr create --title pr-1 --body "" --base main --head pr-1
base ブランチに直接変更を加え、push しましょう。
git switch main
# merge conflict が発生しないよう、`pr-1`とは別のファイル行を変更しています。
sed -i 's/b/bbbbb/' experiment1.txt # ファイル中のbをbbbbbに置き換え
git add --all
git commit -m "update b in main"
git push origin main
base ブランチの push が pull request の file diff に反映されない?
base ブランチの変更を push しただけでは、pull request の file diff は更新されません。下画像のように、3 行目がb1 文字のままであることがわかります。

これは、GitHub では pull request の file diff は base ブランチの変更を取り込まないようになっているからです。base ブランチの変更を取り込むには、別記事「コピペで学ぶチュートリアル: GitHub Pull Request の update 手法 2 種類を試す」の解説している update(rebase) branch の操作が必要です。
Pull request を見るとThis branch has no conflicts with the base branchと表示されているので、Merge pull request ボタンを押しましょう。

ターミナルから pull request をマージしたい場合
gh pr merge pr-1 --merge --delete-branch
Merge conflict を GitHub UI 上で解決
Merge conflict の中でも、同一ファイルの同一行での conflict は GitHub UI 上で解決できます。
GitHub UI 上で解決できる merge conflict と、できない conflict
GitHub 公式: GitHub でのマージ コンフリクトを解決する
GitHub で解決できるマージコンフリクトは、Git リポジトリの別々のブランチで、同じファイルの同じ行に異なる変更がなされた場合など、互いに矛盾する行変更を原因とするもののみです。 その他すべての種類のマージ コンフリクトについては、コマンド ラインでコンフリクトをローカルに解決する必要があります。
変更対象のテキストファイルを作成します。
cat << EOF > experiment2.txt
a
b
c
EOF
git add --all
git commit -m "create experiment2.txt"
git push origin main
ファイルの内容はこちらです。
a
b
c
Pull request を作成します。
git switch -c pr-2
sed -i 's/a/aaaaa/' experiment2.txt # ファイル中のaをaaaaaに置き換え
git add --all
git commit -m "update a in pr-2"
git push --set-upstream origin pr-2
gh pr create --title pr-2 --body "" --base main --head pr-2
base ブランチに直接変更を加え、push しましょう。
git switch main
# わざとmerge conflict が発生するよう、`pr-2`と同一のファイル行を変更しています
sed -i 's/a/aaa/' experiment2.txt # ファイル中のaをaaaに置き換え
git add --all
git commit -m "update b in main"
git push origin main
Pull request を見ると merge conflict が発生し、Merge pull requst ボタンは押せなくなっています。代わりに Resolve conflicts ボタンを押しましょう。

このような画面が表示されるので、merge conflict を解決していきます。

<<<<<<< と ======= と >>>>>>> を消せば、右上の Mark as resolved ボタンが押せるようになります。

続いて、Commit merge ボタンを押しましょう。

Resolve conflicts が完了すると、Merge branch 'main' into pr-2 と pull request の Commits に表示されています。

Pull request を見るとThis branch has no conflicts with the base branchと表示されているので、Merge pull request ボタンを押しましょう。

ターミナルから pull request をマージしたい場合
gh pr merge pr-1 --merge --delete-branch
Merge pull request 後に main ブランチの git log をみるとこうなっています。

上画像では履歴が一直線に見えて分岐がわからないので、git log コマンドで分岐の様子を確認しましょう。
git switch main
git pull origin main
git log --oneline --decorate --graph
* ba28bc0 (HEAD -> main, origin/main, origin/HEAD) Merge pull request #1 from richardimaoka/pr-2
|\
| * e734fc2 Merge branch 'main' into pr-2
| |\
| |/
|/|
* | 9b3f23c update b in main
| * 70888e8 update a in pr-2
|/
* e22e7cc create experiment2.txt
Merge conflict をローカルで解決
例えば、pull request の base ブランチでファイルを更新し、同じファイルを head ブランチで削除した場合の merge conflict は GitHub UI からは解決ができず、ローカルで解決する必要があります。
GitHub UI 上で解決できる merge conflict と、できない conflict
GitHub 公式: GitHub でのマージ コンフリクトを解決する
GitHub で解決できるマージコンフリクトは、Git リポジトリの別々のブランチで、同じファイルの同じ行に異なる変更がなされた場合など、互いに矛盾する行変更を原因とするもののみです。 その他すべての種類のマージ コンフリクトについては、コマンド ラインでコンフリクトをローカルに解決する必要があります。
変更対象のテキストファイルを作成します。
cat << EOF > experiment3.txt
a
b
c
EOF
git add --all
git commit -m "create experiment3.txt"
git push origin main
次に、pull request を作成します。
git switch -c pr-3
rm experiment3.txt
git add --all
git commit -m "delete experiment3.txt in pr-3"
git push --set-upstream origin pr-3
gh pr create --title pr-3 --body "" --base main --head pr-3
main ブランチに直接 commit
git switch main
sed -i 's/a/aaa/' experiment3.txt # ファイル中のaをaaaに置き換え
git add --all
git commit -m "update a to aaa in main"
git push origin main
Pull request を見ると Merge pull request ボタンも、Resolve conflicts ボタンも押せなくなっています。

上画像に表示されているUse the comand lineのところ(字が小さくて見えづらいですが)をクリックすると、以下の説明が出てきます。

Step 1 から 3 までを実行しましょう。Pull request で発生された merge conflict がローカルでも再現されます。
git pull origin main # そもそものpush元なので、今回に関しては実行しても何も起きない
git checkout pr-3 # もしくはgit switch pr-3
git merge main # ここでmerge conflict発生
上記 3 つめのgit mergeコマンドの結果以下が表示されます。
CONFLICT (modify/delete): experiment3.txt deleted in HEAD and modified in main.
Version main of experiment3.txt left in tree.
Automatic merge failed; fix conflicts and then commit the result.
git statusで merge conflict の状態を確認しましょう。
git status
Unmerged paths:
deleted by us: experiment3.txt
experiment3.txtはこのようになっています。
cat experiment3.txt
aaa
b
c
このままmainブランチでのexperment3.txtの内容を取捨選択する形で conflict を解決しましょう。
git add --all
git commit -m "resolve conflict by taking main"
git push origin pr-3
experment3.txt を delete して conflict を解決する場合
直前のコマンドの代わりに、こちらを実行してください。
rm experment3.txt
git add --all
git commit -m "resolve conflict by taking main"
git push origin pr-3
Pull request の Commits はこのようになります。

コミットresolve conflict by taking mainは、直前のコミットで削除したファイルを復元しています。

Pull request を見るとThis branch has no conflicts with the base branchと表示されているので、Merge pull request ボタンを押しましょう。

ターミナルから pull request をマージしたい場合
gh pr merge pr-3 --merge --delete-branch
最後に、git log で分岐の様子を確認しましょう。
git switch main
git pull origin main
git log --oneline --decorate --graph
* bb0ee29 (HEAD -> main, origin/main, origin/HEAD) Merge pull request #2 from richardimaoka/pr-3
|\
| * 6c39372 (origin/pr-3) resolve conflict by taking main
| |\
| |/
|/|
* | b2d25a9 update a to aaa in main
| * f878dae delete experiment3.txt in pr-3
|/
* e383322 create experiment3.txt
* ba28bc0 Merge pull request #1 from richard
Discussion