プルリクエストを潰すとgitのマージアルゴリズムが壊れますか?


15

現在、gitコードの管理にVSTSを使用している会社で働いています。マイクロソフトのブランチの「推奨」方法は、「スカッシュマージ」です。つまり、そのブランチのすべてのコミットが、すべての変更が組み込まれた1つの新しいコミットになります。

問題は、あるバックログアイテムのあるブランチでいくつかの変更を行った後、すぐに別のバックログアイテムの別のブランチで変更を行いたい場合、それらの変更は最初のブランチの変更セットに依存しますか?

そのバックログアイテムのブランチを作成し、最初のブランチをベースにすることができます。ここまでは順調ですね。ただし、2番目のブランチのプルリクエストを作成するときが来ると、最初のブランチは既にmasterにマージされており、スカッシュマージとして行われているため、gitは多くの競合にフラグを立てます。これは、gitが2番目のブランチの基になった元のコミットを表示せず、1つの大きなスカッシュマージを表示するだけであるため、マスターに2番目のブランチをマージするために、スカッシュマージのトップであり、多くの競合が発生します。

だから私の質問は、これを回避する方法はありますか(1つの機能ブランチを別の機能ブランチのベースにすることがないため、ワークフローが制限されます)、またはスカッシュマージはgitのマージアルゴリズムを破壊するだけですか?

回答:


15

Gitで、コミット

  • 不変である、
  • そして有向非巡回グラフを形成します。

スカッシュはコミットを結合しません。代わりに、他の複数のコミットからの変更を含む新しいコミットを記録します。リベースも同様ですが、コミットを結合しません。既存のコミットと同じ変更で新しいコミットを記録することを履歴の書き換えと呼びます。しかし、既存のコミットは不変なので、これは「代替の履歴を書く」と理解されるべきです。

マージは、共通の祖先コミットから始まる2つのコミットの履歴(ブランチ)の変更を結合しようとします。

それでは、履歴を見てみましょう。

                                 F  feature2
                                /
               1---2---3---4---5    feature1 (old)
              /
-o---o---o---A---o---o---S          master

Aは共通の祖先、1〜5は元の機能ブランチ、Fは新しい機能ブランチ、Sは1〜5と同じ変更を含むスカッシュコミットです。ご覧のとおり、FとSの共通の祖先はAです。gitに関する限り、Sと1–5の間に関係はありません。したがって、マスターを一方の側でSとマージし、feature2をもう一方の側で1〜5とマージすると競合します。これらの競合を解決することは難しくありませんが、不必要で退屈な作業です。

これらの制約のため、マージ/スカッシュを処理するための2つのアプローチがあります。

  • 履歴書き換えを使用するか、その場合、同じ変更を表す複数のコミットを取得します。次に、2番目の機能ブランチを押しつぶされたコミットにリベースします。

                                     F  feature2 (old)
                                    /
                   1---2---3---4---5    feature1 (old)
                  /
    -o---o---o---A---o---o---S          master
                              \
                               F'       feature2
    
  • または、履歴の書き換えを使用しません。この場合、追加のマージコミットが発生する可能性があります。

                                     F  feature2
                                    /
                   1---2---3---4---5    feature1 (old)
                  /                 \
    -o---o---o---A---o---o-----------M  master
    

    feature2とmasterがマージされると、共通の祖先はコミット5になります。

どちらの場合も、いくつかのマージ作業があります。この努力は、上記の2つの戦略のどちらを選択するかにあまり依存しません。しかし、それを確認してください

  • ブランチは短期間で、マスターブランチからの距離を制限します。
  • マスターを機能ブランチに定期的にマージするか、マスターの機能ブランチをリベースしてブランチの同期を維持します。

チームで作業する場合、現在誰が何に取り組んでいるかを調整することが役立ちます。これにより、開発中の機能の数を小さく保つことができ、マージの競合の数を減らすことができます。


2
あなたの答えは、最初feature1にマスターにスカッシュマージし、feature2後でマージしたい場合に何が起こるかに取り組んでいないようです。その場合、最初のアプローチでは、git feature1が押しつぶされたコミットの上にコミットを再適用しようとするため、競合が発生しませんか?
ジェズ

@JezそれはまさにあなたがPRをつぶすときに起こることです。私は最近git clone、ブランチから分岐し、メンテナーが最初のブランチを押しつぶしたため、OSSプロジェクトでPRを手動で書き換える必要がありました(レポを実行し、変更したファイルをコピーすることによって!)。私の仕事では、彼らはスカッシュマージも行います。これは、機能がマージされるまで、機能bに依存する機能で作業できないことを意味します。aa
アカウント

1
そして、それはgitが設計されているように、そうでなければ機能するものの本当に迷惑な破損ではありませんか?マイクロソフトやGithubのようなさまざまな組織が実際にこれらのスカッシュマージを推奨しているように見えますが、それらは私には愚かに思えます。
ジェズ

1
@Jez元のシナリオでは、1〜5のコミットをマージするとSでの同じ変更と競合するため、feature2をマスターにマージすると競合が発生します。すべて(解決策2)。
アモン

スカッシュマージが適しているかどうかは、バージョン管理履歴に記録する内容によって異なります。機能ブランチに多数のWIPコミットがある場合、スカッシュにより、マスターブランチに完全な機能を持つ1つの大きなコミットが配置されます。機能ブランチのすべての中間コミットを保持する場合は、リベースまたはマージを使用します。
アモン

11

スカッシュマージは、スカッシュによって削除されたコミットを含むブランチのマージアルゴリズムを中断します。別の言い方をすれば、リベースはバイラルです。1つのブランチをリベースする場合、そのブランチに依存する他のブランチをリベースする必要があります。rerereを使用する場合、ローカルリポジトリで手動で解決したマージの競合を再度手動で解決する必要はありませんが、他の人が解決した競合を解決することはできません。

だからこそ、ここに書かれていないルールは、他の誰もあなたの機能ブランチに依存しない限り、つぶすことは大丈夫です、それはおそらく90%の場合です。それ以外の場合、まっすぐにマージすると、すべての人が問題を回避できます。


マスター履歴でスカッシュコミットと機能ブランチの両方をそのままにして、個別のスカッシュのみのブランチを作成する1つの方法。feature-xyzブランチがあるとします。あなたは、作成することができますfeature-xyz-squashedと同じコミットから始まるブランチをfeature-xyz、ブランチgit cherry-pickからコミットfeature-xyzまでfeature-xyz-squashed、そこにそれらをスカッシュ、およびマージfeature-xyz-squashedしますmaster。その場合、マージしないでくださいfeature-xyz。上記の方法は理にかなっている場合があります(たとえば、パスワードが入り込んだコミットを含めたくない場合)が、回避策であり、ほとんどベストプラクティスではありません。
9000
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.