概要
長期間開発を続けていると、developブランチとの差分が大きくなり、単純なマージが難しくなることがある。
調べていく中で、
- ファイル単位で取り込めるのか
- コミット単位で取り込めるのか
- merge以外の選択肢はあるのか
- 他ブランチがdevelopへ統合された単位で確認できるのか
といった疑問が出てきた。
この記事では、それぞれの方法を整理しながら、変更量が多い場合の確認方法についてまとめる。
背景
ある時点でdevelopを取り込もうとしたところ、差分がかなり大きくなっていた。
そのままマージしてもよかったが、
- どの変更が入るのか把握できていない
- 不要な変更まで取り込む可能性がある
- 競合が大量発生するかもしれない
という懸念があった。
そこで、Gitが提供している複数の取り込み方法を整理した。
問題
まず疑問だったのは次の点である。
- Gitで特定ファイルだけマージできるのか
-
restoreはマージなのか -
restoreでは競合が発生するのか -
cherry-pickはどういう場面で使うのか -
developへマージされた変更をまとめて確認できるのか
調査内容
調査1: 特定ファイルだけ取り込めるのか
最初は「ファイル単位でマージできるのか」と考えた。
調べてみると、Gitのmerge自体にはファイル指定機能はない。
代わりに、別ブランチのファイルを現在のブランチへ取り込む方法がある。
git fetch origin
git restore --source origin/develop -- src/App.cs
旧形式では以下でも同様の操作が可能。
git checkout origin/develop -- src/App.cs
複数ファイルも指定できる。
git restore --source origin/develop -- src/App.cs src/Config.cs
調査2: restoreはマージなのか
最初はrestoreを「ファイル単位のマージ」だと思っていた。
しかし挙動を確認すると違った。
git restore --source origin/develop -- src/App.cs
これはマージではなく、
- 指定ブランチのファイルを取得する
- 現在のファイルへ反映する
という操作だった。
つまり、履歴上はマージにはならない。
原因
restoreとmergeを同じものとして考えていたことが原因だった。
実際には役割が異なる。
| 操作 | 役割 |
|---|---|
| merge | ブランチ統合 |
| cherry-pick | コミット単位取り込み |
| restore | ファイル取得 |
調査3: restoreで競合は起きるのか
競合についても確認した。
結果として、通常の意味でのマージ競合は発生しない。
理由は単純で、自動マージをしていないからである。
git restore --source origin/develop -- src/App.cs
実行すると対象ファイルは置き換わる。
そのため、
- コンフリクトマーカーは出ない
- 自動マージは行われない
- ローカル変更は失われる可能性がある
という特徴がある。
事前確認として以下を実行しておくと安全である。
git diff -- src/App.cs
調査4: コミット単位で取り込む方法
変更の一部だけ欲しい場合はcherry-pickが利用できる。
まず対象コミットを確認する。
git fetch origin
git log --oneline HEAD..origin/develop
古い順に確認する場合。
git log --oneline --reverse HEAD..origin/develop
対象コミットの内容を見る。
git show <commit-id>
取り込む場合。
git cherry-pick <commit-id>
これにより、ブランチ全体ではなく特定コミットだけを反映できる。
コミット単位とは何か
例えば以下の履歴があるとする。
develop
A
B
C
D
通常のマージ。
git merge origin/develop
結果。
A
B
C
D
すべて取り込まれる
一方、cherry-pickの場合。
git cherry-pick A
git cherry-pick C
結果。
A
C
だけ取り込まれる
ただし、依存関係のあるコミットを見落とすと問題が起きる可能性があるため、変更内容の確認は必須である。
調査5: developへマージされた単位で確認できるか
大量のコミットを個別に追うのは大変である。
そこで、ブランチ単位で確認できないか調べた。
マージコミットだけ表示する。
git log --oneline --merges HEAD..origin/develop
本流の履歴を確認する。
git log --oneline --merges --first-parent origin/develop
特定マージの内容を確認する。
git show <merge-commit-id>
マージされたブランチ側のコミット一覧を確認する。
git log --oneline <merge-commit-id>^1..<merge-commit-id>^2
個別コミットを見る前に、まずマージ単位で確認する方が状況を把握しやすいと感じた。
解決方法
変更量が多い場合は、いきなりマージする前に以下を確認する。
git fetch origin
git log --oneline --merges --first-parent origin/develop
そのうえで、
- 全体を取り込みたい →
merge - 特定修正だけ取り込みたい →
cherry-pick - 特定ファイルだけ取り込みたい →
restore
という使い分けを行う。
結果
各コマンドの役割を整理できた。
特に、
-
restoreはマージではない -
restoreは通常の競合を起こさない -
cherry-pickはコミット単位で取り込める - マージコミット単位で変更を確認できる
という点が理解できた。
学んだこと
-
mergeはブランチ単位の統合である -
restoreはファイル取得でありマージではない -
restoreはローカル変更を上書きする可能性がある -
cherry-pickは必要なコミットだけを反映できる - 依存コミットの存在には注意が必要
-
git log --merges --first-parentで本流の変更を追いやすい - 変更量が多い場合はマージコミット単位で確認すると理解しやすい
まとめ
developとの差分が大きくなった場合でも、必ずしもmergeだけが選択肢ではない。
変更の粒度によって、
- ブランチ単位なら
merge - コミット単位なら
cherry-pick - ファイル単位なら
restore
を使い分けることができる。
また、大量の変更を把握する際は、個別コミットを見る前にマージコミット単位で確認すると整理しやすかった。