web-dev-qa-db-ja.com

マージ:Hg / Git対SVN

私はよく、Hg(およびGitなど)がSVNよりもマージに優れていると読みましたが、Hg/GitがSVNが失敗する(またはSVNが手動の介入を必要とする)ものをマージできる場所の実際的な例を見たことはありません。 Hg/Gitが喜んで進んでいる間にSVNがどこで失敗するかを示すブランチ/変更/コミット/ ...操作のステップバイステップリストを投稿できますか?実用的で、例外的ではないケースをお願いします...

背景:数十人の開発者がSVNを使用してプロジェクトに取り組んでおり、各プロジェクト(または同様のプロジェクトのグループ)が独自のリポジトリにあります。リリースブランチと機能ブランチを適用する方法を知っているので、頻繁に問題に遭遇することはありません(つまり、そこにいたことがありますが、克服することを学びました Joelの問題 of "one Programmer 「チーム全体にトラウマを引き起こす」または「ブランチを再統合するために2週間で6人の開発者が必要です」)。リリースブランチは非常に安定しており、バグ修正の適用にのみ使用されます。 1週間以内にリリースを作成できるほど十分に安定したトランクが必要です。また、単一の開発者または開発者のグループが作業できる機能ブランチがあります。はい、再統合後に削除されるため、リポジトリが乱雑になりません。 ;)

だから、私はまだSVNに対するHg/Gitの利点を見つけようとしています。実際に体験したいのですが、Hg/Gitに移行できる大きなプロジェクトはまだないので、作成されたファイルがわずかしか含まれていない小さな人工プロジェクトで遊んでいます。そして、Hg/Gitの印象的な力を感じることができるいくつかのケースを探しています。

141
stmax

私自身はSubversionを使用していませんが、 Subversion 1.5のリリースノート:マージトラッキング(基礎) では、マージトラッキングが完全に機能する方法とは次のような違いがあるようです- [〜 #〜] dag [〜#〜] GitやMercurialなどのバージョン管理システム。

  • トランクからブランチへのマージは、ブランチからトランクへのマージとは異なります。何らかの理由で、トランクへのブランチのマージには、--reintegrateオプションをsvn mergeに必要とします。

    GitやMercurialのような分散バージョン管理システムでは、トランクとブランチに技術的な違いはありません:すべてのブランチは等しく作成されます(socialしかし、違い)。どちらの方向へのマージも同じ方法で行われます。

  • マージトラッキングを考慮するには、-gおよび--use-merge-historyに新しいsvn logsvn blame)オプションを提供する必要があります。

    GitとMercurialでは、履歴(ログ)と非表示を表示するときにマージトラッキングが自動的に考慮されます。 Gitでは、--first-parent(Mercurialにも同様のオプションが存在すると思います)のみで最初の親をフォローし、git logの追跡情報を「破棄」するように要求できます。

  • 私が理解していることからsvn:mergeinfoプロパティは競合に関するパスごとの情報を保存します(Subversionはチェンジセットベースです)が、GitとMercurialでは、複数の親を持つことができるオブジェクトをコミットするだけです。

  • 「既知の問題」Subversionのマージトラッキングのサブセクションでは、繰り返し/循環/リフレクティブマージが適切に機能しない可能性があります。次の履歴では、2番目のマージが正しいことをしない可能性があることを意味します(「A」はそれぞれトランクまたはブランチ、「B」はブランチまたはトランクになります)。

     * --- * --- x --- * --- y --- * --- * --- * --- M2 <-A 
    \\/
    -* ---- M1 --- * --- * ---/<-B 
    

    上記のASCII-artが壊れた場合:ブランチ 'B'はリビジョン 'x'のブランチ 'A'から作成(分岐)され、その後、ブランチ 'A'はリビジョン 'y'でブランチ 'B'にマージされます「M1」をマージし、最後にブランチ「B」をブランチ「A」にマージ「M2」としてマージします。

     * --- * --- x --- * ----- M1-* --- * --- M2 <-A 
     \// 
     \-* --- y --- * --- * ---/<-B 
    

    上記のASCIIアートが壊れた場合:リビジョン 'x'のブランチ 'A'からブランチ 'B'が作成(フォーク)され、 'y'のブランチ 'A'に 'M1'以降としてマージされます。 「M2」としてブランチ「A」に再びマージされました。

  • Subversionは criss-cross merge の高度なケースをサポートしていない場合があります。

     * --- b ----- B1--M1-* --- M3 
    \\//
    \X /
     \/\ /
     \-B2--M2-* 
    

    Gitは、実際には「再帰的」マージ戦略を使用してこの状況をうまく処理します。 Mercurialについてはわかりません。

  • 「既知の問題」には、ファイル名の変更ではマージ追跡が機能しないという警告があります。一方がファイルの名前を変更し(おそらく変更する)、もう一方が(古い名前で)名前を変更せずにファイルを変更する場合。

    GitとMercurialの両方は、実際にはこのようなケースをうまく処理します:Gitを使用したrename detection、Mercurialを使用したrename tracking

HTH

90
Jakub Narębski

私も、Subversionがブランチのマージに失敗し、Mercurial(およびGit、Bazaarなど)が正しいことを行うケースを探していました。

SVNブック 名前を変更したファイルが誤ってマージされる方法を説明します 。これは、Subversion 1.51.61.7 、および 1.8 !私は以下の状況を再現しようとしました:

 cd /tmp
rm -rf svn-repo svn-checkout 
 svnadmin create svn-repo 
 svn checkout file:/// tmp/svn-repo svn -checkout 
 cd svn-checkout 
 mkdirトランクブランチ
 echo 'Goodbye、World!' > trunk/hello.txt 
 svnトランクブランチの追加
 svn commit -m 'Initial import。' 
 svn copy '^/trunk' '^/branches/rename' -m 'ブランチの作成' 
 svn switch '^/trunk'。
 echo 'Hello、World!' > hello.txt 
 svn commit -m 'トランクの更新' 
 svn switch '^/branches/rename'。
 svn rename hello.txt hello.en.txt 
 svn commit -m 'ブランチの名前を変更します。' 
 svn switch '^/trunk'。
 svn merge --reintegrate '^/branches/rename' 

本によると、マージはきれいに終了するはずですが、trunkの更新が忘れられているため、名前が変更されたファイルに間違ったデータが含まれています。代わりに、ツリーの競合が発生します(これは、執筆時点でDebianの最新バージョンであるSubversion 1.6.17で発生しています)。

 ---リポジトリURLの違いを '。'にマージ:
 A hello.en.txt 
 C hello.txt 
競合の概要:
ツリーの競合:1 

競合はまったくありません。更新をファイルの新しい名前にマージする必要があります。 Subversionが失敗しても、Mercurialはこれを正しく処理します。

rm -rf /tmp/hg-repo
hg init /tmp/hg-repo
cd /tmp/hg-repo
echo 'Goodbye, World!' > hello.txt
hg add hello.txt
hg commit -m 'Initial import.'
echo 'Hello, World!' > hello.txt
hg commit -m 'Update.'
hg update 0
hg rename hello.txt hello.en.txt
hg commit -m 'Rename.'
hg merge

マージ前は、リポジトリは次のようになります(hg glog):

 @ changeset:2:6502899164cc 
 |タグ:tip 
 |親:0:d08bcebadd9e 
 |ユーザー:Martin Geisler 
 | date:Thu Apr 01 12:29:19 2010 +0200 
 |概要:名前を変更します。
 | 
 | o変更セット:1:9d06fa155634 
 | /ユーザー:Martin Geisler 
 | date:Thu Apr 01 12:29:18 2010 +0200 
 |概要:更新。
 | 
 o変更セット:0:d08bcebadd9e 
ユーザー:Martin Geisler 
 date:Thu Apr 01 12:29:18 2010 +0200 
要約:初期インポート。

マージの出力は次のとおりです。

 hello.en.txtとhello.txtのhello.en.txtへのマージ
 0ファイルの更新、1ファイルのマージ、0ファイルの削除、0ファイルの未解決
(ブランチマージ、コミットすることを忘れないでください)

つまり、Mercurialはリビジョン1からの変更を取得し、リビジョン2からの新しいファイル名にマージしました(hello.en.txt)。もちろん、リファクタリングをサポートするためにこのケースを処理することは必須であり、リファクタリングはexactlyブランチで行うことです。

119
Martin Geisler

通常の利点(オフラインコミット、 の公開プロセス 、...)について話すことなく、ここに「マージ」の例を示します。のような:

私が見続ける主なシナリオは、...two無関係なタスクが実際に開発されるブランチです
(1つの機能から始まりましたが、この別の機能の開発につながりました。
またはパッチから始まったが、別の機能の開発につながる)。

Mainブランチの2つの機能のうち1つだけをマージする方法は?
または2つの機能をそれぞれのブランチに分離するにはどうしますか?

何らかの種類のパッチを生成しようとする可能性がありますが、問題は機能依存性次の間に存在する可能性があります。

  • パッチで使用されるコミット(またはSVNのリビジョン)
  • 他のコミットはパッチの一部ではありません

Git(およびMercurialもそうです)は、rebase --ontoオプションを提案して、ブランチの一部をリベース(ブランチのルートをリセット)します。

から ジェフロミの投稿

- x - x - x (v2) - x - x - x (v2.1)
           \
            x - x - x (v2-only) - x - x - x (wss)

v2用のパッチと次の新しいwss機能があるこの状況を解決できます。

- x - x - x (v2) - x - x - x (v2.1)
          |\
          |  x - x - x (v2-only)
           \
             x - x - x (wss)

、次のことができます。

  • 各ブランチを単独でテストして、すべてが意図したとおりにコンパイル/動作するかどうかを確認します
  • メインにしたいものだけをマージします。

私が気に入っている(マージに影響する)他の機能は、 squash commits (まだ別のブランチにプッシュされていないブランチで)レポ)を提示するために:

  • よりクリーンな歴史
  • より一貫性のあるコミット(function1のcommit1の代わりに、function2のcommit2の代わりに、function1のcommit3を再度...)

これにより、競合が少なく、はるかに簡単なマージが保証されます。

17
VonC

最近、SVNからGITに移行しましたが、これと同じ不確実性に直面しました。 GITの方が優れているという逸話的な証拠がたくさんありましたが、例を見つけるのは困難でした。

ただし、GITは、SVNよりもマージのほうがはるかに優れています。これは明らかに逸話ですが、従うべき表があります。

見つけたもののいくつかを以下に示します。

  • SVNは、そうすべきではないと思われる状況で、多くのツリーの競合をスローしていました。私たちはこの問題の最下位に到達することはありませんでしたが、GITでは発生しません。
  • 優れていますが、GITはかなり複雑です。トレーニングに時間をかけます。
  • 私たちは気に入ったSVNをカメに慣れました。亀のGITはそれほど良くないので、これはあなたを先送りにするかもしれません。ただし、現在はTortoise SVNまたはGIT GUIを好むGITコマンドラインを使用しています。

GITを評価していたときに、次のテストを実行しました。これらは、マージに関してはGITが勝者であることを示していますが、それほどではありません。実際には、その差ははるかに大きくなりますが、SVNが不適切に処理する状況を再現することができなかったと思います。

GIT vs SVN Merging Evaluation

7
cedd

他の人はこれのより理論的な側面をカバーしています。もっと実用的な視点を貸してもいいでしょう。

現在、「機能ブランチ」開発モデルでSVNを使用している会社で働いています。あれは:

  • トランクで作業を行うことはできません
  • 各開発者は独自のブランチを作成できます
  • ブランチは、引き受けたタスクの期間中継続する必要があります
  • 各タスクには独自のブランチが必要です
  • トランクへのマージは承認される必要があります(通常はbugzilla経由)
  • 高レベルの制御が必要な場合、ゲートキーパーによってマージが行われる場合があります

一般に、それは動作します。 SVNはこのようなフローに使用できますが、完全ではありません。 SVNには、邪魔をして人間の行動を形作るいくつかの側面があります。それはいくつかの否定的な側面を与えます。

  • ^/trunkよりも低い地点から分岐する人々には、かなりの数の問題がありました。このゴミはツリー全体の情報レコードをマージし、最終的にマージトラッキングを中断します。偽の競合が現れ始め、混乱が支配します。
  • トランクからブランチへの変更の取得は比較的簡単です。 svn mergeはあなたが望むことをします。変更を元に戻すには、マージコマンドに--reintegrateが必要であると言われています。このスイッチを本当に理解したことは一度もありませんが、これはブランチを再びトランクにマージできないことを意味します。これは、それがデッドブランチであり、作業を続行するには新しいブランチを作成する必要があることを意味します。 (ノートを参照してください)
  • ブランチを作成および削除するときにURLを介してサーバー上で操作を行うビジネス全体は、人々を本当に混乱させ、怖がらせます。彼らはそれを避けます。
  • ブランチの切り替えは間違えやすく、ツリーの一部をブランチAに向け、残りの部分をブランチBに向けます。そのため、すべての作業を1つのブランチで行うことを好みます。

起こりやすいのは、エンジニアが1日目にブランチを作成することです。彼は作業を開始し、それを忘れます。しばらくして上司がやって来て、仕事をトランクにリリースできるかどうかを尋ねます。再統合とは次のことを意味するため、エンジニアはこの日を恐れています。

  • 彼の長命のブランチをトランクにマージし、すべての競合を解決し、別のブランチにあるはずだったが、そうではなかった無関係なコードをリリースしました。
  • 彼のブランチを削除する
  • 新しいブランチを作成する
  • 彼の作業コピーを新しいブランチに切り替える

...そして、エンジニアはこれをできる限り少なくしているため、各ステップを実行するための「魔法の呪文」を思い出せません。間違ったスイッチとURLが発生し、突然混乱し、「エキスパート」を取得します。

最終的にはすべて落ち着き、人々は欠点に対処する方法を学びますが、新しいスターターはそれぞれ同じ問題を経験します。最終的な現実(彼が始めたときに述べたものとは対照的に)は次のとおりです。

  • トランクで作業は行われません
  • 各開発者には1つの主要なブランチがあります
  • ブランチは、作業をリリースする必要があるまで続きます
  • チケットのバグ修正は、独自のブランチを取得する傾向があります
  • 許可されたときにトランクへのマージが行われます

...しかし...

  • 他の何かと同じブランチにあるので、そうすべきではないときに、作業がトランクになります。
  • 人々はすべてのマージを(簡単なものでさえ)避けているため、人々はしばしば自分の小さなバブルで作業します
  • 大きなマージが発生する傾向があり、限られた量の混乱を引き起こします。

ありがたいことに、チームは対処するのに十分なほど小さいですが、スケールしません。つまり、これはCVCSの問題ではありませんが、DVCSほどマージが重要ではないため、それほど滑らかではありません。その「摩擦の結合」は、「機能分岐」モデルが故障し始めることを意味する動作を引き起こします。適切なマージは、DVCSだけでなく、すべてのVCSの機能である必要があります。


this によれば、--record-only問題の解決に使用できる--reintegrateスイッチがあり、 明らかに v1.8はいつ実行するかを選択します自動的に再統合し、その後ブランチが停止することはありません

5
Paul S

Subversion 1.5より前(私が間違っていなかった場合)、Subversionにはマージ履歴が記憶されないという重大な欠点がありました。

VonCで概説されているケースを見てみましょう。

- x - x - x (v2) - x - x - x (v2.1)
          |\
          |  x - A - x (v2-only)
           \
             x - B - x (wss)

リビジョンAとBに注意してください。「wss」ブランチのリビジョンAからリビジョンBの「v2専用」ブランチに(何らかの理由で)変更をマージし、両方のブランチを引き続き使用するとします。 Mercurialを使用して2つのブランチを再度マージしようとすると、リビジョンAとBの後にのみ変更がマージされます。Subversionでは、以前にマージを行わなかったかのように、すべてをマージする必要があります。

これは私自身の経験からの例であり、BからAへのマージはコードの量が原因で数時間かかっていました。これはagain、これはSubversion 1.5より前の場合に当てはまります。

Hginit:Subversion Re-education :からのマージ動作における、おそらくより関連性の高い別の違い

あなたと私がいくつかのコードに取り組んでおり、そのコードを分岐し、それぞれ別のワークスペースに移動して、そのコードに個別に多くの変更を加えると想像してください。

マージする必要があるとき、Subversionは両方のリビジョン(変更されたコードと変更されたコード)を調べようとし、それらを1つの大きな不浄な混乱の中で破壊する方法を推測しようとします。通常は失敗し、ページと実際には競合ではない「マージ競合」のページが生成されます。Subversionが実行した内容を把握できなかった場所です。

対照的に、Mercurialで個別に作業している間、Mercurialは一連の変更セットの保持に忙しかった。したがって、コードをマージしたい場合、Mercurialには実際にさらに多くの情報があります。最終製品を見てそれをどのように配置するかを推測するのではなく、各自が何を変更したかを把握し、それらの変更を再適用できます一緒に。

つまり、違いを分析するMercurialの方法は、Subversionの方法よりも優れていました(だった?)。