名前付きブランチと複数のリポジトリ


130

現在、比較的大きなコードベースでSubversionを使用しています。各リリースには独自のブランチがあり、修正はトランクに対して実行され、次を使用してリリースブランチに移行されますsvnmerge.py

より良いソース管理に移行する時が来たと私は信じており、私はしばらくの間Mercurialをいじっています。

Mercurialを使用してこのようなリリース構造を管理する方法は2つあります。各リリースは独自のリポジトリを取得し、リリースブランチに対して修正が行われ、メインブランチ(およびその他の新しいリリースブランチ)にプッシュされます。または、単一リポジトリ内の名前付きブランチ(または複数の一致するコピー)を使用します。

どちらの場合も、リリースブランチに含めるために、移植のようなものをチェリーピックの変更に使用しているようです。

私はあなたに尋ねます。各アプローチの相対的なメリットは何ですか?

回答:


129

最大の違いは、ブランチ名が履歴に記録される方法です。名前付きブランチでは、ブランチ名が各チェンジセットに埋め込まれているため、履歴の不変部分になります。クローンでは、特定のチェンジセットがどこから来たのかについての永続的な記録はありません

つまり、クローンはブランチ名を記録したくないクイック実験に最適であり、名前付きブランチは長期ブランチ(「1.x」、「2.x」など)に適しています。

また、1つのリポジトリーでMercurialの複数の軽量ブランチを簡単に収容できることにも注意してください。このようなリポジトリ内ブランチをブックマークして、簡単に見つけられるようにすることができます。次のような会社のリポジトリを複製したとします。

[a] --- [b]

あなたはハックして作り[x]、そして[y]

[a] --- [b] --- [x] --- [y]

誰かプットしながら、平均[c]して[d]、あなたが引くときに、あなたがこのような歴史のグラフを取得し、リポジトリに:

            [x] --- [y]
           /
[あいうえお]

ここでは、1つのリポジトリに2つのヘッドがあります。作業コピーは常に単一のチェンジセット、いわゆる作業コピーの親チェンジセットを反映します。これを確認してください:

% hg parents

それが報告するとしましょう[y]。あなたは頭を見ることができます

% hg heads

そして、これは報告[y][d]ます。リポジトリをのクリーンチェックアウトに更新する場合は[d]、次のようにします([d]のリビジョン番号に置き換えます[d])。

% hg update --clean [d]

その後、そのhg parentsレポートが表示され[d]ます。あなたの次のコミットというこの手段があります[d]親として。したがって、メインブランチで気付いたバグを修正し、チェンジセットを作成できます[e]

            [x] --- [y]
           /
[a] --- [b] --- [c] --- [d] --- [e]

チェンジセット[e]のみをプッシュするには、以下を実行する必要があります

% hg push -r [e]

[e]チェンジセットハッシュはどこですか。デフォルトではhg push、単純にリポジトリを比較し、それを見るだろう[x][y]と、[e]不足しているが、あなたが共有したくない場合があります[x]し、[y]まだ。

バグ修正があなたにも影響を与える場合、あなたはそれをあなたの機能ブランチとマージしたいでしょう:

% hg update [y]
% hg merge

これにより、リポジトリグラフは次のようになります。

            [x] --- [y] ----------- [z]
           / /
[a] --- [b] --- [c] --- [d] --- [e]

どこ[z]の間にマージされ[y]とは[e]。ブランチを破棄することもできます。

% hg strip [x]

このストーリーの主なポイントは次のとおりです。1つのクローンで複数の開発トラックを簡単に表現できます。これは、拡張機能を使用しない「プレーンhg」にも常に当てはまります。ブックマークの拡張子はいえ、大きな助けです。チェンジセットに名前(ブックマーク)を割り当てることができます。上記の場合、開発ヘッドとアップストリームヘッドにブックマークが必要です。ブックマークはMercurial 1.6 でプッシュおよびプルでき、Mercurial 1.8の組み込み機能になっています。

2つのクローンを作成することを選択した場合、作成後の開発クローンは次のように[x]なり[y]ます。

[a] --- [b] --- [x] --- [y]

そして、上流のクローンには以下が含まれます:

[a] --- [b] --- [c] --- [d]

これでバグに気づき、修正しました。ここでhg updateは、上流のクローンを使用する準備ができているので、必要はありません。あなたはコミットして作成します[e]

[a] --- [b] --- [c] --- [d] --- [e]

バグ修正を開発クローンに含めるには、そこにプルします。

[a] --- [b] --- [x] --- [y]
           \
            [c] --- [d] --- [e]

そしてマージ:

[a] --- [b] --- [x] --- [y] --- [z]
           \ /
            [c] --- [d] --- [e]

グラフは異なって見える場合がありますが、構造は同じであり、最終結果は同じです。クローンを使用すると、精神的な簿記を少し少なくする必要がありました。

名前付きブランチは非常にオプションであるため、ここでは実際には表示されません。Mercurial自体は、名前付きブランチの使用に切り替える前の数年間、2つのクローンを使用して開発されました。「デフォルト」ブランチに加えて「安定」というブランチを維持し、「安定」ブランチに基づいてリリースを作成します。参照標準の分岐推奨ワークフローの説明については、ウィキでページを。


1
変更セットが別のユーザーからのものであった場合、それは記録されているはずなので、クローンを使用しても問題はありません。新しい機能をプッシュするとき、別のリポジトリからそれを実行したことを知るのは、多くの場合面白みがありません。localbranch拡張もあり、ローカルのみのブランチを提供します。リポジトリのクローン作成が高コスト(時間/スペース)に関連する場合に役立ちます。
ヨハネスルドルフ

2
参照:「クローンは素早い実験に最適」-いいえ、そうではありません!リポジトリに数十万のファイルがある場合はどうなりますか?クローンの切り替えには時間がかかり(1分を超える場合はいつでも)、ブランチの切り替えはほんの少し(1秒未満)です。名前付きブランチを引き続き使用すると、変更ログが汚染されます。行き止まりじゃないですか?または、何か不足していますか?
seler

オーケーセラー; 彼の元の議論に対する修正のように聞こえます。クローンは、複数の完全なコピーのオーバーヘッドが重要でない場合、またはhgのシンボリックリンク/ハードリンクを使用して、ブランチごとに個別のローカル作業コピーのコストを軽減できる場合に適しています。
ウォレンP

@seler:コードが大きければクローンは実用的ではないというのはあなたの言う通りです。ブックマークがその解決策です。
Martin Geisler、2011

29

1つのリポジトリにすべての履歴が必要だと思います。短期的なレポからの生成は、短期的な実験用であり、リリースなどの主要なイベントではありません。

Mercurialの失望の1つは、短命のブランチを作成し、それを使って遊んで、それを放棄し、ゴミを収集する簡単な方法がないように見えることです。枝は永遠です。私は歴史を決して捨てたくないと思いますが、非常に安価で使い捨てのブランチは、git私が本当に見たい機能ですhg


20
このような機能のブランチは非常に簡単に作成できます。ブランチポイントへの "hg update"、編集、そして "hg commit"です。分岐した開発ラインを新しく作成しました-新しいコミットはこのブランチを拡張します。「hg clone -r」を使用して削除するか、「hg strip」を使用してインラインで削除します。ですから、がっかりしたり、機能のリクエストをMercurialメーリングリストに送ったりしないでください。
マーティンガイスラー、

8
以下のように見えますhg strip私が欲しいものです。オンラインドキュメントでブランチを削除できないと主張されているのはなぜですか?
ノーマンラムジー、

11
Mercurialがどのようにしてgitよりも安価なブランチを持っているかについての説明は、このブログ投稿も参照してください。stevelosh.com/blog/ entry
Martin Geisler

9
名前付きブランチはで閉じることができhg ci --close-branchます。
Andrey Vlasovskikh、2009年

3
@ノーマンラムジー:ブランチを削除できないと人々が言うとき、それはチェンジセットに埋め込まれたブランチ名を変更できないことを意味します。チェンジたちなしブランチは、それが定義されてブランチを。別のブランチに「移動」する場合は、変更セットを削除して、別のブランチ名で再作成する必要があります。
Martin Geisler、2011

14

両方を実行する必要があります。

@Normanからの承認済みの回答から始めます。リリースごとに1つの名前付きブランチを持つ1つのリポジトリを使用します。

次に、ビルドとテストのためにリリースブランチごとに1つのクローンを用意します。

重要な注意点の1つは、複数のリポジトリを使用している場合でも、transplant1)ハッシュを変更し、2)変更セット間に競合する変更があると、検出が非常に困難なバグが発生する可能性があるため、それらの間で変更セットを移動することは避けてください。移植とターゲットブランチ。代わりに通常のマージを行いたい(そして事前マージを行わない:常にマージを視覚的に検査する)ことで、@ mgが回答の最後に言ったことになります。

グラフは異なって見える場合がありますが、構造は同じであり、最終結果は同じです。

より詳細には、複数のリポジトリを使用する場合、「トランク」リポジトリ(またはデフォルト、メイン、開発など)には、すべてのリポジトリのすべての変更セットが含まれます。各リリース/ブランチリポジトリは、トランク内の1つのブランチであり、古いリリースを残したいまで、すべて一方向にマージされるか、他の方法でトランクに戻されます。したがって、名前付きブランチスキームのメインレポと単一レポの唯一の実際の違いは、ブランチに名前が付けられているかどうかだけです。

これで、「1つのリポジトリから始める」と言った理由が明らかになります。その単一のリポジトリは、すべてのリリースで変更セットを探す必要がある唯一の場所です。バージョン管理のために、リリースブランチのチェンジセットにさらにタグを付けることができます。概念的に明確でシンプルであり、システム管理者をシンプルにします。これは、常に利用可能であり、回復可能でなければならない唯一のことだからです。

ただし、ビルドおよびテストする必要があるブランチ/リリースごとに1つのクローンを維持する必要があります。できることhg clone <main repo>#<branch> <branch repo>はささいなことです。hg pullブランチレポでは、そのブランチの新しいチェンジセット(および、マージされた以前のブランチの祖先チェンジセット)のみをプルします。

この設定は、単一のプーラーのLinuxカーネルコミットモデルに最適です(Linus卿のように振る舞うのは良い感じではありません。当社ではロールインテグレーターと呼んでいます)。メインのリポジトリは、開発者が複製する必要がある唯一のものであり、プーラーは引き込む必要があります。ブランチリポジトリのメンテナンスは純粋にリリース管理のためであり、完全に自動化できます。開発者はブランチレポジトリからプルしたり、ブランチレポにプッシュしたりする必要はありません。


これは、このセットアップ用にリキャストされた@mgの例です。出発点:

[a] - [b]

アルファリリースに到達したら、リリースバージョンの名前付きブランチを作成します(「1.0」など)。バグ修正をコミットします:

[a] - [b] ------------------ [m1]
         \                 /
          (1.0) - [x] - [y]

(1.0)名前付きブランチはコミットするまで存在しないため、実際のチェンジセットではありません。(タグを追加するなどの簡単なコミットを行って、名前付きブランチが適切に作成されていることを確認できます。)

マージ[m1]がこのセットアップの鍵です。ヘッドの数に制限がない開発者リポジトリとは異なり、メインリポジトリに複数のヘッドを含めることは望ましくありません(前述の古いデッドリリースブランチを除く)。そのため、リリースブランチに新しいチェンジセットがある場合は、すぐにそれらをデフォルトブランチ(またはそれ以降のリリースブランチ)にマージして戻す必要があります。これにより、1つのリリースのバグ修正がすべての以降のリリースに含まれることが保証されます。

その間、デフォルトブランチの開発は次のリリースに向けて継続されます。

          ------- [c] - [d]
         /
[a] - [b] ------------------ [m1]
         \                 /
          (1.0) - [x] - [y]

そしていつものように、あなたはデフォルトのブランチで2つのヘッドをマージする必要があります:

          ------- [c] - [d] -------
         /                         \
[a] - [b] ------------------ [m1] - [m2]
         \                 /
          (1.0) - [x] - [y]

そして、これは1.0ブランチクローンです。

[a] - [b] - (1.0) - [x] - [y]

今度は、次のリリースブランチを追加するための練習です。2.0であれば、間違いなくデフォルトから分岐します。それが1.1の場合、1.0またはデフォルトから分岐することを選択できます。いずれにせよ、1.0の新しいチェンジセットは最初に次のブランチにマージされ、次にデフォルトにマージされます。これは、競合がない場合は自動的に行われ、結果として空のマージになります。


この例が私の以前のポイントを明確にすることを願っています。要約すると、このアプローチの利点は次のとおりです。

  1. 完全な変更セットとバージョン履歴を含む単一の信頼できるリポジトリ。
  2. 明確で簡素化されたリリース管理。
  3. 開発者とインテグレーターのための明確で簡素化されたワークフロー。
  4. ワークフローの反復(コードレビュー)と自動化(空の自動マージ)を促進します。

UPDATEは、自分自身をHG これを行いますメインのレポは、 デフォルトと安定した枝が含まれており、安定したレポは安定版ブランチクローンです。ただし、安定版ブランチのバージョンタグはリリース管理の目的に十分対応できるため、バージョン付きブランチは使用しません。


5

私の知る限り、主な違いはすでに述べたとおりです。名前付きブランチは単一のリポジトリにあります。名前付きブランチは、1つの場所ですべてが便利です。個別のリポジトリは小さく、移動が簡単です。これについて2つの考え方がある理由は、明確な勝者がいないためです。どちらの側の引数があなたにとって最も理にかなっているのかは、おそらくあなたが行くべきものであろう。なぜなら、彼らの環境はあなたのものに最も似ている可能性が高いからです。


2

機能や再設計のサイズなど、現在の状況に応じて、それは明らかに実用的な決定だと思います。フォークは、技術的なオーバーヘッドが無視できるほどの適性を証明することで、まだコミッターではない役割を持つ貢献者が開発者チームに参加するのに本当に良いと思います。


0

バージョンに名前付きブランチを使用しないことをお勧めします。これがタグの目的です。名前付きブランチは、stableブランチのように長く続く転換のためのものです。

では、なぜタグを使用しないのですか?基本的な例:

  • 開発は単一のブランチで行われます
  • リリースが作成されるたびに、それに応じてタグ付けします
  • そこから開発が続く
  • 特定のリリースで修正するバグ(またはその他)がある場合は、そのタグに更新し、変更を加えてコミットするだけです

これにより、名前のない新しいヘッドがdefaultブランチ上に作成されます。匿名ブランチ。これはhgでは完全に問題ありません。その後、いつでもバグ修正コミットをメインの開発トラックにマージできます。名前付きブランチは必要ありません。


これはプロセスに大きく依存します。たとえば、Webアプリは、stable / testing / develブランチ階層でうまく機能します。デスクトップソフトウェアを構築する場合、通常、開発ブランチ(デフォルト)と、1つから3つ(!)の異なるブランチがメンテナンスにあります。いつブランチを再検討する必要があるかを予測することは困難であり、ブランチにメジャーマイナーバージョンを追跡させることにはある程度の優雅さがあります。
James Emerton 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.