web-dev-qa-db-ja.com

git:2つのブランチをマージします:どの方向ですか?

次の状況があります。

             A --- B --- C --- ... --- iphone
           /
  ... --- last-working --- ... --- master

最終作業とiPhoneの間で、32のコミットが行われました。最後の作業とマスターの間で、多くのコミットが行われました

私が今欲しいのは、iphoneと現在のマスターをマージした新しいブランチです。しばらくしてから、これをマスターにマージする必要があります。

最初に、私はすることを計画しました:

git checkout iphone -b iphone31
git merge master

しかし、私はそれをする方が良いと思った:

git checkout master -b iphone31
git merge iphone

今、私は疑問に思っています。 結果の違いは何ですか?マージの動作は異なりますか?

私はすでに両方を試してみましたが、予想どおり、iphoneはマスターと比較して本当に古いため、多くの競合が発生しました。今、私はそれらをマージする最も簡単な方法について疑問に思います。

マスターから始めて、iphoneの各コミットをマージする方が簡単かもしれません。これをするように:

git checkout master -b iphone31
git merge A
git merge B
git merge C
...
git merge iphone

最後に、このマージが完了すると(つまり、すべての競合が解決されて動作するようになったら)、これを実行したいと思います。

git checkout master
git merge iphone31
60
Albert

代替案について

git checkout iphone -b iphone31
git merge master

そして

git checkout master -b iphone31
git merge iphone

それらは同一の容易さまたは難しさを持ちます。それは、グラスが半分満たされているか、半分空であるかを議論するようなものです。

バージョンツリーの認識

バージョンツリーをどのように見るかは、ある意味では単なるarbitrary意的な認識です。次のようなバージョンツリーがあるとします。

    A----------+
    |          |
    |          |
   \_/        \_/
    B          X
    |          |
    |          |
   \_/        \_/
    C          Y
    |
    |
   \_/
    D
    |
    |
   \_/
    E

そして、CからEへの変更に基づいてYからチェックアウトされた新しいバージョンZを作成したいとしますが、AからCへの変更は含めません。

「ああ、共通の出発点がないので、それは難しいでしょう。」よくないよ。このようにグラフィカルレイアウトでオブジェクトを少し異なるように配置する場合

      /
    C+---------+
    | \        |
    |          |
   \_/         |
    D          B
    |         / \
    |          |
   \_/         |
    E          A
               |
               |
              \_/
               X
               |
               |
              \_/
               Y

今、物事は有望に見え始めています。ここでリレーションシップを変更していないことに注意してください。矢印はすべて前の図と同じように示されており、バージョンAは依然として共通のベースです。レイアウトのみが変更されます。

しかし、今では別のツリーを想像するのは簡単です

    C'---------+
    |          |
    |          |
   \_/        \_/
    D          B'
    |          |
    |          |
   \_/        \_/
    E          A
               |
               |
              \_/
               X
               |
               |
              \_/
               Y

ここでは、タスクは通常バージョンEをマージするだけです。

したがって、必要なものはすべてマージできます。簡単さまたは難易度に影響を与える唯一のことは、開始点または共通ベースとして選択した場所とマージする場所の間で行われた変更の集合です。バージョン管理ツールが提案する自然な開始点を使用することに限定されません。

これは一部のバージョン管理システム/ツールでは簡単ではないかもしれませんが、他のすべてが失敗した場合、バージョンCをチェックアウトしてファイルをfile1として保存し、バージョンEをチェックアウトしてファイルをfile2として保存することでこれを手動で行うことを妨げるものは何もありません、バージョンYをチェックアウトし、ファイルをfile3として保存し、kdiff3 -o merge_result file1 file2 file3

回答

特定の状況では、最小の問題を引き起こす戦略を正確に言うことは困難ですが、何らかの競合を引き起こす多くの変更がある場合、おそらく小さな部分を分割してマージする方が簡単です。

私の提案は、最後の作業とiphoneの間に32のコミットがあるので、たとえば、マスターの分岐から始めて、最初の16のコミットにマージできるということです。問題が大きすぎる場合は、元に戻して、8つの最初のコミットをマージしてみてください。等々。最悪の場合、32個のコミットのそれぞれを1つずつマージすることになりますが、1つのマージ操作で蓄積されたすべての競合を処理するよりもおそらく簡単です(その場合、reallyコードベースの相違)。

ヒント:

バージョンツリーを紙に描き、マージしたいものを矢印でメモします。プロセスを複数のステップに分割する場合は、完了時に物事を消してください。これにより、達成したいこと、これまでに行ったこと、残っていることをより明確に把握できます。

KDiff をお勧めします。これは優れたdiff/mergeツールです。

43
hlovdal

違いがあります。


マージ中は常にターゲットブランチをチェックアウトします(つまり、AをBにマージする場合、チェックアウトB)。


マージの方向は重要ではないとよく言われますが、これは間違っています。結果のコンテンツはマージ方向に関係なく同じですが、異なる側面がいくつかあります。

  1. 結果のmerge-commitにリストされる差分は、方向によって異なります。
  2. ほとんどのブランチビジュアライザーは、マージ方向を使用してどのブランチが「プライマリ」であるかを決定します。

詳しく説明するには、この誇張された例を想像してください。

  • 1000回のコミットでMASTERから分岐し、DEVELOPという名前を付けました(または、追跡分岐シナリオでは、かなり長い間フェッチしていません)。
  • 1つのコミットをDEVELOPに追加します。この変更に競合はありません。
  • 変更をMASTERにプッシュします。
  • 誤ってMASTER into DEVELOPをマージします(つまり、DEVELOPはマージ中にチェックアウトされます)。次に、新しいマスターとしてDEVELOPをプッシュします。
  • DEVELOPは参照ポイントであるため、結果のmerge-commitの差分には、MASTERで発生した1000件のコミットがすべて表示されます。

このデータは役に立たないだけでなく、何が起こっているのかを読み取るのが困難です。ほとんどのビジュアライザーは、DEVELOP行がずっとプライマリであり、1000件のコミットが取り込まれているように見えます。

私の提案はこれです:マージ中は常にターゲットブランチをチェックアウトします(つまり、AをBにマージする場合、チェックアウトB)。

  • 並列ブランチで作業していて、メインブランチから定期的に変更を取り込む場合は、並列ブランチをチェックアウトします。 diffは理にかなっています-メインブランチw.r.tで行われた変更がブランチに反映されます。
  • 並列作業が完了し、変更をメインブランチにマージする場合は、メインブランチをチェックアウトして、パラレルブランチとマージします。 diffは再び意味を成します-並列の変更がメインブランチに対してw.r.tであるかを示します。

私の意見では、ログの読みやすさが重要です。

12
Ryuu

最良のアプローチは、他の人があなたのコードのリモートコピーを持っているかどうかに本当に依存します。 masterブランチがローカルマシンにのみある場合、rebaseコマンドを使用して、機能ブランチからコミットをマスターに対話的に適用できます。

git checkout master -b iphone-merge-branch
git rebase -i iphone

これにより、新しいiphone-merge-branchブランチのコミット履歴が変更されることに注意してください。これは、変更をチェックアウトにプルしようとする他のユーザーに問題を引き起こす可能性があります後で。対照的に、mergeコマンドは変更を新しいコミットとして適用します。これは、ブランチの履歴に影響を与えないため、コラボレーションする場合により安全です。 rebaseの使用に関するいくつかの有用なヒントについては、 この記事 を参照してください。

コミット履歴の同期を維持する必要がある場合は、マージを実行することをお勧めします。 git mergetoolを使用して、視覚的なdiffツールを使用して競合を1つずつインタラクティブに修正できます(これに関するチュートリアルは ここにあります ):

git checkout master -b iphone-merge-branch
git merge iphone
git mergetool -t kdiff3

3番目のオプションは、プロセスを完全に制御する場合、git cherry-pickを使用することです。 gitk(またはお気に入りの履歴ビューア)を使用して、iphoneブランチでコミットハッシュを表示し、それらを書き留めて、マージブランチに個別に選択します-競合を修正します。このプロセスの説明は こちらにあります です。このプロセスは最も遅くなりますが、他の方法がうまくいかない場合は、最適なフォールバックオプションになる可能性があります。

gitk iphone
<note down the 35 commit SHA hashes in this branch>
git checkout master -b iphone-merge-branch
git cherry-pick b50788b
git cherry-pick g614590
...
6
seanhodges

あなたは言う:

iphoneブランチはmasterと比較して非常に古いです

両方をマージして新しいブランチを作成しますか?

マスターブランチとiPhoneブランチの目的は、今では非常に異なっていました(iPhoneは非常に古いため)。 iPhoneを祖先のマスターとマージする新しいブランチの方が良いでしょうか?考えてみてください。

マージの目的と分岐の目的 と読むことを強くお勧めします。

それでもiPhoneとマスターをマージしたいと思う場合は、その記事を読んだ後、@ seanhodgesが競合を本当にうまく処理する方法を説明します。

2
ardsrk

実験前に作業のローカルバックアップを作成しておくと、何が起こったのか理解できなくなった場合に再起動できます。

参照用に保持する場合は、リベース中に失わないように、作業のバックアップブランチを作成します。

git checkout iphone -b iphone_backup

マスターから新しいブランチを作成する

git checkout master -b iphone31

その上でリベースします。

git rebase -i --onto iphone31 iphone

上記のSeanは、一度に1つのコミットを適用するリベースについて正しいです。しかし、リベースするブランチの既存のコミットについては心配しないでください-それらは変更されません。 Rebaseは、新しい(適応した)コミットをそれらの上に置きます。一度に1つのコミットを行うと、競合をより適切に制御できます。しかし重要なことは、マスターの履歴が変わらないように、マスターの上で作業をリベースする必要があります。

あるいは、あなただけができる

git rebase -i master iphone

iPhoneブランチのバックアップを気にしない場合。詳細と代替手段については、リベースドキュメントをご覧ください http://git-scm.com/docs/git-rebase .

1
Thinkeye