web-dev-qa-db-ja.com

チェリーピックの問題:以前のコミットからの変更も適用されます

私のプロジェクトでは、数か月前にバージョンをリリースしました。そのリリースの後、私はマスターブランチで多くの変更を行いました。

前回のリリースで発生したバグに遭遇した場合は、マスターブランチで修正してから、前回のリリースで作成したブランチにチェリーピックします。そうすれば、マスターブランチで未完成の作業をリリースすることなく、バグ修正のみを含む新しいリリースを提供できます。

リリースブランチの特定のバグ修正をチェリーピックしようとすると、マージの競合が発生しました。

私が理解しているように、特定のコミットをチェリーピックすると、ターゲットブランチに新しいコミットが導入され、チェリーピックコミットで変更が行われます。

しかし、マージの競合を修正しようとすると、gitがマスターブランチに変更を適用したようですが、これはチェリーピックコミットによってリリースブランチに導入されていません。チェリーピックコミットは、競合するファイルに数行しか導入しませんでした。しかし、競合を解決しようとすると、他のいくつかの行もファイルに導入され、異なるコミットでマスターブランチに追加されたことがわかります。

チェリーピックコミット以外のコミットからの変更がリリースブランチに導入される理由を誰かが説明できますか?

10
Lahiru Chandima

私が理解しているように、特定のコミットをチェリーピックすると、ターゲットブランチに新しいコミットが導入され、チェリーピックコミットで変更が行われます。

それは正しいです。ただし、これらの変更は適切に適用されない場合があります。その場合、次のようになります。

...マージの競合が発生しました。

この時点で、_git cherry-pick_は3方向マージを実行しています。これには、マージベースを選択する必要があります。

マージベースとして使用されるファイルまたはコミットは、Gitのバージョンによって部分的に異なります。 Modern Gitは、_git cherry-pick_を実行する場合、チェリーピックされたコミットの マージベース として使用しますが、少なくとも1つの形式(_git am_を使用)を使用しますまたは、_git apply_オプションを指定した_--3way_は、_git rebase_が引き続き実行できます)_git diff_出力のindex行を使用して、はるかに古いバージョンのファイルを選択できます。結局、これはおそらくそれほど重要ではないはずです。

いずれの場合も、マージは事実上、基本コミットから2つの「ヒント」(厳選されたコミット、およびHEADコミット)のそれぞれに対して_git diff_を実行します。チェリーピックを適用しようとしています)。何が起こっているかを視覚的に確認するには、通常どおり、コミットグラフを描画することから始める必要があります(リポジトリがないため、-を描画します。 differentグラフで、十分に近いことを願っています。ただし、独自に描画するか、Gitに描画させるか、gitkなどを使用する必要があります。)

_          o--@         <-- branch (HEAD)
         /
...--o--o
         \
          I--P--C--o   <-- otherbranch
_

ここでは、さまざまなコミットに1文字の名前を付けました。Cは、チェリーピックしようとしているコミットであり、Pはその親です。ここでは、現在の(HEAD)コミットを_@_としてマークしましたが、実際の作業はすべてインデックスとワークツリーで行われます。 (幸い、_git cherry-pick_を使用して複数のチェリーを1つの大きなピックにアセンブルする場合を除き、_-n_ではインデックスとワークツリーが「クリーン」である必要があります。そのため、インデックスとワークツリーはcommit _@_とにかく。)そして、commitIを「重要」としてマークしました。

ここで、ベースとしてP、コミットの1つとしてC、もう1つのコミットとして_@_を使用して3方向マージを実行するとどうなるかを考えてみましょう。 PCに変更するための命令を計算すると、適用したい差分が得られます。これは非常に簡単です。しかし、Pを_@_に変更するための命令を計算するときは、まあ、うん。

Iにいくつかの重要な変更を加えました。これらの変更はPの一部です。つまり、マージベースにあります。しかし、それらは_@_ではnotです。マージの意味するところは、Gitはこれらの重要な変更を私たちがやろうとしていることと見なすということですn-。そして実際、彼らはそうです! I自体をチェリーピックしなかったため、Iからの変更を適用するには、これらのCの変更を元に戻す必要があります。

私たちが「元に戻す」Iの変更が_@_にない場合は常にandCからの変更には影響しませんが、問題ありません。すでに元に戻されています。 。何らかの偶然または目的で、それらのIの1つが_@_で(おそらく_@_の親を介して)is変更された場合、それらは私たちが試しているセットにも含まれていませんそもそも元に戻すので、やはり大丈夫です。これらの変更が競合、または単に隣接(のコンテキスト)、P- to -Cが変更されたときに、問題が発生します。

この場合、Gitは、2つのマージ競合領域の1つで、いくつかのIの変更を表示します。それらは、私たちがチェリーピックしようとしている部分です。それらは必ずしも適用されているわけではなく、私たちが解決しなければならない対立の一部にすぎません。また、_merge.conflictStyle_を_diff3_に設定すると(私が一般的に推奨するもの)、Iの変更は、マージベースの一部として表示されます。これは、マージベースis commitPであるためです。はそれ自体がIに基づいています(つまり、Pのスナップショットには、Iの作成中に変更した場合を除いて、Pのコードが含まれています)。

ですから、あなたが何を求めているのかは私には完全にはわかりませんが、isマージ競合領域で、チェリーピッキングしているビットとは関係のない変更を確認するのは正常です。

10
torek

異なるコミットでマスターブランチに追加された他のいくつかの行もファイルに導入されていることがわかります

そのコミットに望ましくない変更がないことを確認してください。

$ git show <commit-sha>

コミットをチェリーピックします。

$ git checkout <release-branch>
$ git cherry-pick <commit-sha>

手動で競合をより適切に解決します。

または、コミットの変更を保持する場合はtheirsを受け入れ、それ以外の場合はoursを受け入れます(リリースブランチの変更を保持します)

$ git checkout --theirs -- .
Or,
$ git checkout --ours -- .

$ git status                     # see the changes
$ git add .
$ git commit -m 'Fix conflicts'
$ git Push Origin HEAD

Alternative:このソリューションは、ターゲットのコミットからリリースブランチまでのファイルの変更をほとんど行わない場合に効果的です。

チェリーピックコミットは、競合するファイルに数行しか導入しませんでした

$ git checkout <release-branch>
$ git checkout <commit-sha> <file> .      # change the <file> to commit's version

$ git add .
$ git commit -m 'Fix the bug'
$ git Push Origin HEAD
2
Sajib Khan