別の機能ブランチに基づいた機能ブランチをマージする 質問する

別の機能ブランチに基づいた機能ブランチをマージする 質問する

数日前、master完全に直線的な履歴を持つブランチがありました。その後、 という機能ブランチを作成しました。このブランチを と呼びますfeat/feature-a。そのブランチで作業した後、 にマージされるようコードレビューに提出しましたmaster

がレビューされている間にfeat/feature-a、 によって導入されたコードに依存する別の機能に取り組みたいと思いましたfeat/feature-a。そこで、ブランチfeat/feature-bから ブランチを作成しましたfeat/feature-a

に取り組んでいる間にfeat/feature-bfeat/feature-aがマスターにマージされました。そのため、マスターには によって導入されたコードが含まれるようになりましたfeat/feature-a。マスターにマージしたいのですfeat/feature-bが、次のようなマージ競合が多数発生します。

<<<<<<< HEAD
=======
    // Some code that was introduced by feat/feature-b
>>>>>>> c948094... My commit message from feat/feature-b

feat/feature-a私の推測では、変更を自分のブランチに取り入れたためfeat/feature-b、それらの変更を「複製」しようとして、マージ競合が発生していると考えられます。

これらは手動で解決できますが、数十のファイルに複数回存在するため、より良い解決策があれば知りたいです。

ベストアンサー1

要約: 使用git rebase --onto <target> <limit>

としてコメントで役に立たない提案実際のマージであれば、このようなことは起きないはずです。ここで「実際のマージ」とはどういう意味か、また、問題のコミットのグラフを描くと、どのように分岐するかの図を示します。まず、次のようなものから始めます。

...--E---H         <-- master
      \
       F--G        <-- feat/feature-a
           \
            I--J   <-- feat/feature-b

ここでは2つのコミット(正確な数は重要ではないが)があり、のみfeat/feature-bIと呼ばれる、Jここ; 2つのコミットが にあります両方機能ブランチはFおよび と呼ばれG、コミットは1つあります。のみmaster、 と呼ばれます。H(コミットEおよびそれ以前のコミットは3つすべて枝。

を に実際のマージしてと をmaster取り込むとします。グラフ形式では、次のようになります。FG

...--E---H--K      <-- master
      \    /
       F--G        <-- feat/feature-a
           \
            I--J   <-- feat/feature-b

実際のマージはK、親コミット履歴ポインタとして、コミットHmaster)とG( )の両方を持っていることに注意してください。そのため、Gitは後で、マージが「 から開始する」ことを意味することをfeat/feature-a認識します。(より正確には、コミットはJGGマージベース後でマージするためです。

このマージはうまくいきました。しかし、以前はそうではありませんでした。代わりに、マージを行った人はいわゆる「squash merge」機能を使用していました。squash-mergeは同じものをもたらしますが、変更実際のマージとは異なり、マージはまったく生成されません。代わりに、マージされたコミットの数に関係なく、それらの作業を複製する単一のコミットが生成されます。この場合、 と の作業を複製するFためG、次のようになります。

...--E---H--K      <-- master
      \
       F--G        <-- feat/feature-a
           \
            I--J   <-- feat/feature-b

Kからへのバックポインタがないことに注意してくださいG

したがって、マージ(実際のまたはsquash-not-really-a-"merge") の場合feat/feature-b、Git は で始まるべきだと考えますE。(技術的には、 は、以前の実際のマージの場合のようにEではなく、マージ ベースですG。) ご覧のとおり、これによりマージ競合が発生します。(多くの場合、それでも「正常に機能」しますが、このケースのように、機能しない場合もあります。)

それは将来的には問題ないかもしれないが、今の問題はそれをどう修正するかだ。

ここでやりたいことはコピー独占的feat/feature-bにコミットする新しいコミットは、 の後に続きますK。つまり、図は次のようになります。

              I'-J'  <-- feat/feature-b
             /
...--E---H--K        <-- master
      \
       F--G          <-- feat/feature-a
           \
            I--J     [no longer needed]

これを行う最も簡単な方法はリベースこれらのコミットは、リベース以来手段コピー。問題は、単純なgit checkout feat/feature-b; git rebase masterコピーでは多すぎるコミットします。

解決策は伝えることですgit rebase コピーすることを約束するこれを行うには、引数を からmasterfeat/feature-aまたはコミットの生のハッシュID G、つまり最初の1つのコミットを識別するもの)に変更します。ないコピーする)しかし、それはgit rebaseすでに存在する場所にコピーすることを意味するので、それは良くありません。新しい問題は、 を追加することです--onto。これにより、「コピー先」の部分と「何をコピーするか」の部分を分離できます。

git checkout feat/feature-b
git rebase --onto master feat/feature-a

(これは、名前がfeat/feature-acommit を指していることをG前提としています。そうでない場合は、 commit に名前を付ける別の方法を見つける必要がありますG。コミット ハッシュを見つけるには、独自のグラフを描画したり、出力を詳しく調べたりする必要があるかもしれませんgit log)。


1 Git スタイルの逆方向の「最初」です。最新のコミットから始めて、古いコミットへの接続を逆方向にたどります。Git はすべてを逆方向に行うため、ここで逆方向に考えると役立ちます。:-)

おすすめ記事