36

通常の Git マージの競合では、スリーウェイ マージで使用されるファイルの 3 つのバージョンは、おおよそ次のとおりです。

  • LOCAL: 私のブランチのバージョン
  • REMOTE: 他のブランチのバージョン
  • BASE: 2 つのブランチの共通の祖先からのバージョン (特に、私のブランチの HEAD と他のブランチの HEAD の共通の祖先)

Git のチェリー ピックがマージの競合を生成する場合、正確に言えば、共通の祖先は存在しないため、これらはどのように決定されるのでしょうか? リベースについても同じことが言えます。

4

1 に答える 1

45

チェリーピック

私が自分自身を誤解していない限り、「git cherry-pick <commit C>」を実行すると、次のようになります。

  • LOCAL: マージするコミット (つまり、ブランチの HEAD)
  • REMOTE: 選択したコミット (例: <commit C>)
  • BASE: 選択したコミットの親 (つまり、C^、つまり C の親)

BASE が C^ であるべき理由がすぐにわからない場合は、以下の「理由」セクションを参照してください。

一方、例を挙げてみましょう。チェリー ピックでは、BASE が共通の祖先になる可能性がありますが、多くの場合そうではないことがわかります。コミット グラフが次のようになっているとします。

E <-- master
|
D 
| C <-- foo_feature(*)
|/
B
|
A

あなたはブランチ foo_feature にいます(したがって、アスタリスク)。「git cherry-pick <commit D>」を実行すると、cherry-pick の BASE は C と D の共通の祖先であるコミット B になります (C は LOCAL になり、D は REMOTE になります)。代わりに「git cherry-pick <commit E>」を実行すると、BASE はコミット D になります (C は LOCAL になり、E は REMOTE になります)。

リベース

バックグラウンド コンテキストの場合、rebase はほぼ反復されたチェリー ピッキングです。特に、マスターの上にトピックをリベースする (つまり、「git checkout topic; git rebase master」) とは、おおよそ次のことを意味します。

git checkout master # switch to master's HEAD commit
git checkout -b topic_rebased # create new branch rooted there
for each commit C in master..topic # for each topic commit not already in master...
    git cherry-pick C # bring it over to the new branch
finally, forget what "topic" used to mean and now defined "topic" as the HEAD of topic_rebased.

このプロセス中に適用されるラベルは、通常のチェリー ピック ルールの拡張です。

  • LOCAL: チェリーピックしているコミット
    • これは新しい topic_rebased ブランチの HEAD です
    • 最初のコミットのみ、これは master の HEAD と同じになります
  • REMOTE: 選択したコミット (例: <commit C>)
  • BASE: 選択したコミットの親 (C^、つまり C の親)

これは、混乱を避けるために、LOCAL と REMOTE について留意すべきことを意味します。

リベースを開始したときにブランチ トピックにいたとしても、リベースの進行中LOCAL がトピック ブランチのコミットを参照することはありません。 代わりに、LOCAL は常に、作成される新しいブランチ (topic_rebased) のコミットを参照します。

(これを心に留めておかないと、厄介なマージの最中に、「待って、なぜこれらはローカルの変更だと言っているのですか? 私は、それらが私のブランチではなくマスターで行われた変更であることを誓います. 」と自問し始めるかもしれません。)

より具体的に言うと、以下に例を示します。

コミットグラフがあるとしましょう

D <-- foo_feature(*)
|
| C <-- master
B |
|/
|
A

現在、ブランチ foo_feature (「*」で示されています) にいます。「git rebase master」を実行すると、リベースは 2 つのステップで進みます。

最初に、B からの変更が C の上で再生されます。この間、C は LOCAL、B は REMOTE、A は BASE です。A は B と C の実際の共通の祖先であることに注意してください。この最初のステップの後、おおよそ次のようなグラフが得られます。

   B' <-- foo_feature
D  |
|  |
|  C <-- master
B /
|/
|
A

(実生活では、この時点で B と D は既にツリーから剪定されている可能性がありますが、潜在的な共通の祖先を見つけやすくするために、ここに残しています。)

次に、D からの変更が B' の上で再生されます。この間、B' は LOCAL、D は REMOTE、B は BASE です。B は何の関連する共通の祖先でもないことに注意してください。(たとえば、現在の LOCAL と REMOTE、B' と D の共通の祖先ではありません。また、元のブランチ ヘッドの C と D の共通の祖先でもありません)。このステップの後、おおよそ次のようなブランチがあります。

   D' <-- foo_feature
   |
   B'
D  |
|  |
|  C <-- master
B /
|/
|
A

完全を期すために、リベースの最後までに B と D がグラフから削除され、次のようになることに注意してください。

D' <-- foo_feature
|
B'
|
C <-- master
|
A

BASEがそのまま定義されているのはなぜですか?

上記のように、cherry-pick と rebase の両方で、BASE はプルされるコミット C の親 (C^) です。一般に、C^ は共通の祖先ではないため、なぜ BASE と呼ぶのですか? ? (通常のマージでは、BASE共通の祖先です。そして、マージにおける git の成功の一部は、適切な共通の祖先を見つける能力によるものです。)

基本的に、通常の3 方向マージアルゴリズムを介して「パッチ」機能を実装する方法としてこれを行います。特に、これらの「斑点のある」プロパティを取得します。

  • <commit C> がファイルの特定のリージョンを変更しない場合、ブランチのそのリージョンのバージョンが優先されます。(つまり、「パッチ」が変更を要求しない領域にはパッチが適用されません。)
  • <commit C> がファイルの特定の領域を変更し、ブランチがその領域をそのままにしておく場合、<commit x> からのその領域のバージョンが優先されます。(つまり、「パッチ」が変更を要求する領域にパッチが適用されます。)
  • <commit C> がファイルの特定の領域を変更したが、ブランチがその領域も変更した場合、マージの競合が発生します。
于 2012-04-07T20:27:10.197 に答える