「Gitの基礎 - rebaseとmergeの使い分け」の版間の差分
(→概要) |
編集の要約なし |
||
1行目: | 1行目: | ||
== 概要 == | == 概要 == | ||
Gitにおいて、mergeコマンドとrebaseコマンドは、異なるブランチの変更を統合するためのコマンドである。<br> | |||
しかし、統合の方針に違いがある。<br> | |||
<br> | <br> | ||
ここでは、masterブランチの過去のコミットから派生した機能開発用のtopicブランチの変更を、masterブランチに取り込むという状況で、2つの方法を比較する。<br> | |||
<br><br> | <br><br> | ||
24行目: | 24行目: | ||
* rebase | * rebase | ||
*: 付け替える各コミットにおいて、潜在的にコンフリクトが発生する可能性がある。 | *: 付け替える各コミットにおいて、潜在的にコンフリクトが発生する可能性がある。 | ||
<br><br> | |||
== mergeを使用すべき場合 == | |||
* フィーチャーブランチの履歴を残す場合 | |||
* 既にリモートで共有されているブランチの場合 | |||
* コンフリクト解決を1度で済ませる場合 | |||
* チーム開発で作業の証跡を残す場合 | |||
<br><br> | |||
== rebaseを使用すべき場合 == | |||
* 直線的な履歴が必要な場合 | |||
* ローカルでのみ使用しているブランチの場合 | |||
* 細かい作業履歴を整理する場合 | |||
* master / mainブランチの最新変更を取り込む場合 | |||
<br><br> | |||
== Publicブランチでのrebase禁止 == | |||
既にプッシュされたブランチをrebaseする場合、他の開発者の環境で問題が発生する。<br> | |||
<br> | |||
# 絶対に避けるべき操作 | |||
git checkout main | |||
git rebase feature | |||
<br><br> | |||
== rebase後のプッシュ == | |||
<code>--force-with-lease</code>オプションとは、他の開発者が同じブランチを更新していないことを確認できる安全なオプションである。<br> | |||
<br> | |||
# rebase後に強制プッシュが必要 | |||
git push --force-with-lease origin feature | |||
<br><br> | |||
== マージ戦略の一貫性 == | |||
チーム内で統一されたマージ戦略を実施することが重要となる。<br> | |||
<br> | |||
# マージコミットを必ず作成する場合 | |||
git merge --no-ff feature | |||
# rebaseした後にマージする場合 | |||
git checkout feature | |||
git rebase main | |||
git checkout main | |||
git merge --ff-only feature | |||
<br><br> | |||
== 推奨される方法 == | |||
==== 作業開始時の基点更新 ==== | |||
git checkout main | |||
git pull | |||
git checkout -b feature | |||
# 作業開始 | |||
<br> | |||
==== 定期的な同期 (rebaseの場合) ==== | |||
git checkout feature | |||
git rebase main | |||
# コンフリクト解決 | |||
git push --force-with-lease origin feature | |||
<br> | |||
==== マージ前の履歴整理 ==== | |||
# コミットを整理 | |||
git rebase -i HEAD~3 | |||
# スカッシュマージ | |||
git merge --squash feature | |||
<br><br> | <br><br> | ||
2024年11月18日 (月) 07:18時点における版
概要
Gitにおいて、mergeコマンドとrebaseコマンドは、異なるブランチの変更を統合するためのコマンドである。
しかし、統合の方針に違いがある。
ここでは、masterブランチの過去のコミットから派生した機能開発用のtopicブランチの変更を、masterブランチに取り込むという状況で、2つの方法を比較する。
merge / rebaseの主な違い
履歴の形状
- merge
- ブランチの履歴がそのまま保持されて、マージコミットによって結合点が作られる。(木構造)
- rebase
- コミットを付け替えることにより、直線的な履歴になる。
コミットの同一性
- merge
- 元のコミットのSHA-1ハッシュが保持される。
- rebase
- 新しいコミットが作られるため、SHA-1ハッシュが変更される。
コンフリクト解決
- merge
- マージ時に1回だけコンフリクトを解決する。
- rebase
- 付け替える各コミットにおいて、潜在的にコンフリクトが発生する可能性がある。
mergeを使用すべき場合
- フィーチャーブランチの履歴を残す場合
- 既にリモートで共有されているブランチの場合
- コンフリクト解決を1度で済ませる場合
- チーム開発で作業の証跡を残す場合
rebaseを使用すべき場合
- 直線的な履歴が必要な場合
- ローカルでのみ使用しているブランチの場合
- 細かい作業履歴を整理する場合
- master / mainブランチの最新変更を取り込む場合
Publicブランチでのrebase禁止
既にプッシュされたブランチをrebaseする場合、他の開発者の環境で問題が発生する。
# 絶対に避けるべき操作 git checkout main git rebase feature
rebase後のプッシュ
--force-with-lease
オプションとは、他の開発者が同じブランチを更新していないことを確認できる安全なオプションである。
# rebase後に強制プッシュが必要 git push --force-with-lease origin feature
マージ戦略の一貫性
チーム内で統一されたマージ戦略を実施することが重要となる。
# マージコミットを必ず作成する場合 git merge --no-ff feature # rebaseした後にマージする場合 git checkout feature git rebase main git checkout main git merge --ff-only feature
推奨される方法
作業開始時の基点更新
git checkout main git pull git checkout -b feature # 作業開始
定期的な同期 (rebaseの場合)
git checkout feature git rebase main # コンフリクト解決 git push --force-with-lease origin feature
マージ前の履歴整理
# コミットを整理 git rebase -i HEAD~3 # スカッシュマージ git merge --squash feature
mergeを使用して統合する
topicブランチで機能を開発する間に、他のチームメンバによってmasterブランチがいつくか先に進んでいるとする。
このまま、materブランチにtopicブランチをmergeすると、コンフリクト(競合、衝突)が発生する。
masterブランチ上でコンフリクトを解決するのは好ましくない。なぜなら、解決の結果が正常に動作するかどうか実証する必要があるからである。
そこで、コンフリクトの解決の実証をローカルのtopicブランチで行うため、masterブランチ側の変更をtopicブランチに取り込む。
(ここで、コンフリクトを解決したものとする)
次に、このtopicブランチの変更をmasterブランチに取り込む。(masterブランチをチェックアウトする)
そして、topicブランチをmergeする。(この場合、--no-ffオプションを付加しない限りは、fast forward mergeになる)
これで、topicブランチのコミットと完全に同一のコミット(sha-1が同じ)が、masterブランチのコミットとして記録される。
ここでは、masterブランチへの最終mergeを自分で行ったが、チーム開発に於いては、masterブランチの管理者に対してプルリクエストを送る場合もある。
rebaseを使用して変更の起点を移動する
rebaseは、再度(re)起点を定める(base)という意味合いがある。
まず、統合前の状態では上記と同様に、masterブランチとtopicブランチの間にコンフリクトが発生することが分かっているので、
それを避けるため、topicブランチの起点を、masterブランチの最新のコミットで置き換える。
コンフリクトがあれば個別に解決する必要がある。rebaseでは、topicブランチのコミットを1つずつ順番にmasterブランチの最新の先端に付け加える処理が発生する。
例えば、topicブランチ上で、masterブランチとコンフリクトが発生するファイルを何度も繰り返し変更している場合は、その都度、競合が発生する可能性がある。
そのため、コミットの多いtopicブランチをrebaseする場合は、作業が煩雑になる。(事前に、topicブランチの履歴をrebase -i等を使用して簡略化すると緩和できる)
ここでは、rebaseした後のコンフリクトの解決と実証が正常に終了したものとする。
次に、この変更をmasterブランチに取り込む。
masterブランチをチェックアウトして、git merge --no-ff topic-branchコマンドを使用する。(必ず、--no-ffオプションを付加すること)
この時、topicブランチのコミットログがmasterブランチ側に残らない用に配慮する。(--no-ffオプションにより、fast forwardを行わない独立したコミットが作成される)
masterブランチのコミットログを見ると、基本的には直列の履歴かつ変更コミットが機能ごと(トピックブランチごと)に独立して見える。
備考
masterブランチを進める観点で見ても、作成される履歴に違いが出る。
一般的には、どのような履歴を残すかによって使い分けるが、masterブランチなどの統合ブランチの履歴をシンプルに保つ等の目的で、
以下のような方針で運営するケースもよく見られる。
masterブランチ等の統合用ブランチに、別ブランチをmerge(または、pull request)する前に、必ず最新のmasterブランチでrebaseしてからmerge(または、pull request)する。
これらは、チームの運営方針に合わせる。