マージ:Hg / GitとSVN


144

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

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

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


2
私はあなたが正確な重複に注意を払うべきだと思う:stackoverflow.com/questions/43995/...の stackoverflow.com/questions/459891/...
P Shved

11
私はすでに最初のものを読んでおり、もう1つは新しいものでした。しかし、それらはすでに1〜2年前のものであり、ほとんどがsvn-1.5より前の問題(svnにはまだマージ追跡がありませんでした)のようです。
stmax

2
以下の問題を正しく処理する別のDVCSとしてgit / hgを使用してBazaarをまとめることもできるというコメントだけです。そして、あなたが利点を見つけることを試みることを述べたので:git / hg / bzrの1つの単純なロジスティクスの利点は、ブランチがsvnのようにグローバルではないことです。あなたに適用されるカップルが2つだけの場合、67のブランチを表示する必要はありません。誰もが「プライベート」ブランチで作業を行い、優れたマージ機能を使用して、ケースの99%でマージが機能するかどうかを気にせずにマージします。
wadesworld 2010

5
@wade:「プライベート」ブランチを企業環境の利点として見ていますか?バックアップが心配です。私は、多くの場合、社会復帰の前に1〜2ヶ月のためにそのライブ機能ブランチを持っている...
stmax

9
@stmax:有効な懸念事項。ただし、subversionを使用している多くの企業環境では、コードが完全になるまでチェックインを延期し、そこで同じように危険にさらされます。
wadesworld 2010

回答:


91

私自身はSubversionを使用していませんが、Subversion 1.5のリリースノートから:マージ追跡(基礎)は、GitやMercurialのような完全なDAGバージョン管理システムでのマージ追跡の動作に次のような違いがあるようです。

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

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

  • あなたは、新しい提供する必要があります-g--use-merge-historyに)オプションをsvn logしてsvn blame、アカウントにマージ追跡を取ります。

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

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

  • Subversionのマージ追跡の「既知の問題」のサブセクションは、繰り返し/循環/反射マージが正しく機能しない可能性があることを示唆しています。これは、次の履歴では、2番目のマージが正しく機能しない可能性があることを意味します(「A」はそれぞれトランクまたはブランチ、「B」はブランチまたはトランクになる可能性があります)。

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

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

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

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

  • Subversionは、クロスクロスマージの高度なケースをサポートしない場合があります。

    * --- b ----- B1--M1-* --- M3
         \ \ / /
          \ バツ /
           \ / \ /
            \-B2--M2-*
    

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

  • では、「既知の問題」 1辺が(古い名前で)名前を変更せずにファイル(そしておそらく修正も)、及び第二の側の修正ファイルの名前を変更するときに、ファイルの名前の変更、例えばとmighない作業を追跡すること、マージが警告しています。

    GitとMercurialはどちらもこのようなケースを実際に問題なく処理します。Gitは名前変更検出を使用し、Mercurialは名前変更追跡を使用ます。

HTH


どういうわけか(<pre>...</pre>
マーク

1
多くの詳細な例の+1。最初のascii-artの例で問題が発生する理由はまだわかりません。これは、機能ブランチを処理する標準的な方法のように見えます。Aがトランクで、Bが機能ブランチであると想定します。毎週AからBにマージし、機能が完了したら、BからAにすべてをマージしてから、Bを削除します。図を誤解していますか?
stmax 2010年

1
上記の例が実際にSubversionで問題を起こすことは知りません(チェックしていません)。名前の変更とクロスクロスマージは、SVNの本当の問題だと思います。
JakubNarębski、2010年

2
再統合マージは、マージ時の最も一般的なケースであなたを助ける特別なオプションです-svnのブランチとトランクの間に技術的な違いはありません。私はそれを決して使用しない傾向があり、標準のマージオプションを使用します。それでも、svn mergeの単一の問題は、移動/名前変更が削除+追加として扱われることです。
gbjbaanb

--reintegrate廃止予定です。
naught101 2015年

120

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

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

cd / tmp
RM - 高周波のsvn - レポはsvn - チェックアウト
svnadminのは、SVNの作成- レポ
SVNチェックアウトファイル:/// tmpに/ SVN - レポはsvn - チェックアウト
CDのsvn - チェックアウト
mkdirトランクブランチ
echo 'さようなら、世界!' > トランク/ こんにちはtxt 
svnトランクブランチを追加
svn commit - m 「初期インポート」。
svn copy '^ / trunk' '^ / branches / rename' - m 'Create branch。' 
svnスイッチ'^ / trunk' 
エコー'こんにちは、世界!' > こんにちはtxt    
svn commit - m 「トランクで更新」。
svnスイッチ'^ / branches / rename' 
svn rename hello txtこんにちはen txt 
svn commit - m 「ブランチで名前を変更」。
svnスイッチ'^ / trunk' 
svn merge - '^ / branches / rename'を再統合 

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

---リポジトリURL間の違いを「。」にマージ:
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):

@チェンジセット:2:6502899164cc
| タグ:ヒント
| 親:0:d08bcebadd9e
| ユーザー:Martin Geisler
| 日付:2010年4月1日12:29:19 +0200
| 概要:名前を変更します。
|
| oチェンジセット:1:9d06fa155634
| /ユーザー:Martin Geisler 
| 日付:2010年4月1日12:29:18 +0200
| 概要:更新。
|
oチェンジセット:0:d08bcebadd9e
   ユーザー:Martin Geisler 
   日付:2010年4月1日12:29:18 +0200
   概要:最初のインポート。

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

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

つまり、Mercurialはリビジョン1からの変更を採用し、リビジョン2からの新しいファイル名にマージしました(hello.en.txt)。このような場合を処理するサポートリファクタリングするために、もちろん不可欠であり、リファクタリングがあり、正確にあなたがブランチ上で行うことになるでしょうものの一種。


詳細な例の+1は、キーボードをタップして何が起こるかを自分で確認できます。Mercurial noobとして、この例のhgバージョンが明白な方法で行ごとに続いているのだろうか?
DarenW 2010年

4
@DarenW:私は対応するMercurialコマンドを追加しました。
Martin Geisler、2010

17

通常の利点(オフラインコミット、公開プロセスなど)については触れずに、ここに「マージ」の例を示します。

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

mainブランチの2つの機能のうち1つだけをマージするにはどうすればよいですか?
または、2つの機能をそれぞれのブランチにどのように分離しますか?

ある種のパッチを生成しようとすることができますが、その問題は、次の間に存在する可能性のある機能の依存関係がわからなくなったことです。

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

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

Jefromiの投稿から

- 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)

、次のことができます。

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

私が気に入っているもう1つの機能(マージに影響を与える)は、(まだ別のリポジトリにプッシュされていないブランチで)コミットスカッシュして表示する機能です。

  • よりきれいな歴史
  • より一貫性のあるコミット(function1のcommit1の代わりに、function2のcommit2、function1のcommit3 ...)

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


svnにはオフラインコミットはありませんか?rofl?もしそうなら、リモートでそれを使用することを誰でも検討することができますか?
o0 '。

@Lohoris SVNが登場したとき、広く使われているオープンソースのDVCSはありませんでした。現時点では、人々がまだそれを使用しているのは、ほとんどが慣性だと思います。
Max Nanasy

@MaxNanasyは非常に悪い慣性です...それでも、それを選択することは単に愚かでしょう。
o0 '。

@Lohoris Online(より正確には、一元化された)コミットは、リポジトリが単に共有ローカルサーバー上にある小さなチームではそれほど重要ではありません。DVCSは主に、地理的に分散した大規模なチーム(gitとmercurialの両方がLinuxカーネルコードを管理することを目的とした)とオープンソースプロジェクト(したがってGitHubの人気)のために発明されました。慣性は、チームのワークフローの中心となるツールを変更することのリスクとメリットの評価と見なすこともできます。
IMSoP 2014

1
@Lohoris私はDB、ファイアウォールなどに関する私の見解を誤解していると思います。実際に最初にそのコードを実行できない場合、自分のホームマシンでコミットできることはほとんどありません。私盲目で働くことはできましたが、どこかに物事をコミットできないという事実は、私を後押しする主なものではありません。
IMSoP 2014

8

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

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

ここに私たちが見つけたもののいくつかがあります:

  • SVNは、本来そうではないと思われる状況で、多くのツリーの競合を引き起こしていました。この問題の根底には達していませんが、GITでは発生しません。
  • GITの方が優れていますが、かなり複雑です。トレーニングに少し時間をかけてください。
  • 私たちは気に入ったTortoise SVNに慣れました。Tortoise GITはそれほど良くなく、これはあなたを先送りにするかもしれません。ただし、今ではTortoise SVNまたはGIT GUIのどれよりも好みのGITコマンドラインを使用しています。

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

GITとSVNのマージ評価


5

他の人たちはこれのより理論的な側面をカバーしています。たぶん、もっと実用的な見方をすることができます。

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

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

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

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

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

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

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

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

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

...だが...

  • 他のブランチと同じブランチにあるため、実行できない場合は、トランクが動作することがあります。
  • 人々はすべてのマージ(簡単なものでも)を回避するため、自分の小さな泡で作業することがよくあります
  • 大きなマージが発生する傾向があり、限られた量の混乱を引き起こします。

ありがたいことに、チームは対応するのに十分なほど小さいですが、規模は大きくありません。事は、これはCVCSの問題ではありませんが、マージはDVCSほど重要ではないため、滑らかではありません。その「マージフリクション」は動作を引き起こします。つまり、「機能ブランチ」モデルが壊れ始めます。適切なマージは、DVCSだけでなく、すべてのVCSの機能である必要があります。


よると、このあり、今だ--record-only解決するために使用することができたスイッチ--reintegrateの問題は、と明らか V1.8選択し、自動的に再統合を行うことをするとき、それはブランチがその後死んではありません。


私が理解しているように、-reintegrateオプションは、機能ブランチにマージするときに競合する変更をすでに解決したことをsvnに伝えます。パッチとして扱うのではなく、すべてのトランクリビジョンがブランチにマージされたことをマージ履歴ですでにチェックしているので、パッチとして扱うのではなく、ファイル全体をブランチバージョンで上書きします。
IMSoP 2014

@IMSoP:たぶん、それは理にかなっています。それはなぜそれが必要だったのか、なぜそれがそのブランチからのさらなるマージを不可能にしたのかを説明していません。オプションがほとんど文書化されていなかったことも助けにはならなかった。
ポールS

私はこれまでTortoiseSVN経由でしか使用していませんでしたが、マージUIで常に目立つように説明されていました。私はSVN 1.8が自動的に正しい戦略を選択し、別のオプションを必要としないと信じていますが、この方法でリセットされたブランチを正しく処理するように通常のマージアルゴリズムを修正したかどうかはわかりません。
IMSoP 2014

3

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にマージするのに数時間かかった私の経験の例です。これは、Subversion 1.5より前の場合と同じように、やり直すのが本当に大変でした。

Hginit:Subversion Re-educationからのマージ動作のもう1つの、おそらくより関連性の高い違い:

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

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

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

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


5
私はhginitを読みました。残念ながら、hgがsvnよりも優れているという実際的な例は表示されません。彼が示した簡単な例は、おそらくsvnでも実行できます。実際、それが私がこの質問を開いた理由です。
stmax

1
これがどのように言われるかに基づいて、単純な質問が思い浮かびます:MercurialのマージアルゴリズムがSubversionに組み込まれた場合はどうなりますか?svnはhgと同じくらい良いでしょうか?いいえ、hgの利点はファイルの行をマージする低レベルのテキスト計算ではなく、高レベルの編成にあるためです。これは、svnユーザーが理解する必要のある斬新なアイデアです。
DarenW 2010年

@stmax:私はあなたが何を意味するかを見ることができます。ただし、Joelの意見や他の誰かの意見はそれほど重要ではありません。1つのテクノロジーが他のテクノロジーよりも優れている(一連のユースケースの場合)かどうかは関係ありません。@DarenWと@stmax:私自身の個人的な経験から、Hgは分散操作(常に接続されているわけではない)、パフォーマンス(多くのローカル操作)、優れたマージアルゴリズムによって強化された非常に直感的な分岐により、勝つことができます。 hgロールバック、テンプレート化されたログ出力、hg glog、単一の.hgフォルダー...私はただ続けていくことができます...おそらくgitとbazaar以外のものはストレートジャケットのように感じます。
Tomislav Nakic-Alfirevic

「チェンジセット」について引用されたhgコメントは、私にはかなり不正確なようです。SVNはマージする変更を完全に把握しており(変更セットは基本的に2つのスナップショットの違いであり、逆も同様です)、必要に応じてそれぞれを順番に適用できます。確かに何かを「推測」する必要はありません。それが「1つの大きな不浄な混乱」を引き起こす場合、それは実装の問題であり、設計の基本的なものではありません。現在のアーキテクチャ設計の上で解決するのが難しい主な問題は、ファイルの移動/コピー/名前の変更です。
IMSoP 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.