一般的な場合、私たちは PR を作成する際に共通のワークフローを持っています:
- ブランチを作成する、例えば feature
- PR を提出する
- レビューを依頼する
- レビューの意見に基づいて修正する
- PR をメインブランチにマージする
しかし、この feature ブランチには複数のコミットがある場合、メインブランチにマージする方法が問題になります。GitHub では、3 つのオプションが提供されます:
マージコミットを作成する#
2 つのブランチの履歴を結びつける「マージコミット」を作成します
このプロセスをローカルでシミュレートすることができます。まず、メインブランチで 3 つのコミットを作成し、git log --oneline --graph
コマンドを使用してコミット履歴を確認できます:
* b8472e4 (HEAD -> main) c.txtを追加
* 2db7159 b.txtを追加
* b5b52d8 a.txtを追加
次に、新しいブランチgit checkout -b feature
で 2 つのコミットを作成します:
* 9770b82 (HEAD -> feature) e.txtを追加
* 4e65cc4 d.txtを追加
* b8472e4 (main) c.txtを追加
* 2db7159 b.txtを追加
* b5b52d8 a.txtを追加
メインブランチに戻り、次のコマンドを使用してマージします:
git checkout main
git merge --no-ff feature
再び履歴を確認します:
* 7da24fd (HEAD -> main) featureブランチをmainブランチにマージ
|\
| * 9770b82 (feature) e.txtを追加
| * 4e65cc4 d.txtを追加
|/
* b8472e4 c.txtを追加
* 2db7159 b.txtを追加
* b5b52d8 a.txtを追加
GitHub は、デフォルトのオプションであるgit merge --no-ff
を使用して、2 つのブランチの履歴を結びつけるマージコミットを作成します。
この方法の利点は、すべての履歴とタイムラインが保持されることですが、欠点は、複数のブランチや長期のブランチが複雑になることです。
スカッシュしてマージ#
ブランチ内のすべてのコミットを 1 つのコミットに圧縮してマージします
同様に、ローカルでシミュレートすることもできます。まず、git reset --hard b8472e4
コマンドを使用してマージ前の状態に戻ります:
* b8472e4 (HEAD -> main) c.txtを追加
* 2db7159 b.txtを追加
* b5b52d8 a.txtを追加
git merge --squash feature
コマンドを使用してマージを圧縮し、適切なコミットメッセージを書きます。
最終的なメインブランチのコミット履歴は次のようになります:
* 52c548d (HEAD -> main) d.txtとe.txtを追加
* b8472e4 c.txtを追加
* 2db7159 b.txtを追加
* b5b52d8 a.txtを追加
この方法の利点は、メインブランチの履歴が明確で直線的であり、1 つの機能が 1 つのコミットで解決されることですが、欠点は、マージや個々のコミットのコンテキスト情報が失われ、このブランチが実際にマージされたのかどうかさえわからないことです。
別のスカッシュの方法#
より詳細な操作を実現するために、インタラクティブなリベースコマンドを使用します:
git checkout feature
git rebase -i main
次のようにスクワッシュを選択し、後続のコミットを前のコミットに統合します:
pick 4e65cc4 d.txtを追加
squash 9770b82 e.txtを追加
適切なコミットメッセージを書き、feature ブランチのコミット履歴は次のようになります:
* f37e60f (HEAD -> feature) d.txtとe.txtを追加
* b8472e4 (main) c.txtを追加
* 2db7159 b.txtを追加
* b5b52d8 a.txtを追加
最後に、メインブランチに戻ってマージします(もちろん、個別または組み合わせのコミットを cherry-pick してマージすることもできます):
git checkout main
git merge feature
ボーナス#
コミット履歴を壊してしまった場合、どのように復元しますか?
git reflog
# すべてのgit操作が表示されます。壊れた前のリセットを見つけてください
git reset HEAD@{index}
リベースとマージ#
すべてのコミットがメインブランチの「トップ」にマージされます
図はありませんが、説明からメインブランチの最終結果がわかります:
ブランチの状態を復元し、次の操作を実行します:
git checkout feature
git rebase main
git checkout main
git merge feature
完了後、メインブランチのコミット履歴は次のようになります:
* 9770b82 (HEAD -> main, feature) e.txtを追加
* 4e65cc4 d.txtを追加
* b8472e4 c.txtを追加
* 2db7159 b.txtを追加
* b5b52d8 a.txtを追加
利点は、メインブランチの履歴が明確で直線的であり、余分なマージコミットがないことです。他のブランチのコミット履歴も直接メインブランチの記録に保存されます。
欠点は、スカッシュマージと同様であり、さらに多くの小さなコミットが大局の注意を引く可能性があります。
アドバイス#
すべての選択肢はトレードオフの結果です。以下の重要な要素を考慮する必要があります:
- チームの好み
- コミット履歴:完全性 vs. 明確性?
- より小さなコミット vs. 完全な機能のコミット
- 長期的なブランチの有無
- ...
私のアドバイスは 1 つだけです:より多くのコミットを作成し、より多くの PR を作成し、コードを積み重ねず、より小さなコード断片を使用することです。これにより、コードレビューが容易になり、単体テストが簡単になり、単一責任の原則に合致したコードになります。