The Hackerlab at regexps.com

開発ブランチ -- star-merge を使った共同作業

up: arch Meets hello-world
next: シンボリックタグ
prev: パッチログとプロジェクトツリーの履歴

前章では、hello-world プロジェクトからの例題を拡張しました.

このプロジェクトの主要なプログラマであるアリスとボブは、一つのアーカイブから始めて, いくつかのリビジョンを作成しました。

キャンディスは、プロジェクトのユーザであり, 自身のアーカイブを作って, hello-worldプロジェクトのブランチを開始し, 自身のローカルな変更を保守し始めました.

この章では、自由ソフトウェアプロジェクトの, 現実世界での, より典型的な状況を考え始めます. ここでは、アリスとボブが公的なプロジェクトのメンテナであり, キャンディスはプロジェクト外の重要な貢献者であるとします. このような設定から出てくる, 新しいリビジョン制御の要求を, ここでは明らかにします. そして, arch のコマンド群がそれらの要求を満たすことを助けることを確認します.

基本ブランチを開発ブランチにする

ここまでの例では、キャンディスは基本ブランチを持っています。 彼女はメインラインからブランチを作り、いくつかのローカルの変更を行い、アリスおよびボブのメインラインの最新版に追随してました.

ここでは, アリスとボブが, キャンディスの変更をメインラインにマージしたい場合を考えます.

実は, そのマージ作業は既に終ってます。 キャンディスの最新のリビジョンはアリスとボブが望むツリーそのものです。 それは非常に簡潔な方法でそれをメインラインへマージすることができます. そのためにキャンディスの最新のリビジョンを彼らのメインラインにコミットします:


        % tla get -A candice@candice.net--2003-candice \
                    hello-world--candice--0.1 \
                    hw-C
        [...]


        % cd hw-C

        % tla set-tree-version -A lord@emf.net--2003-example \
                    hello-world--mainline--0.1

        % tla make-log
        ++log.hello-world--mainline--0.1--lord@emf.net--2003-example

        [... ログファイルの編集 (`tla log-for-merge' が使えます) ... ]

        % cat ++log.hello-world--mainline--0.1--lord@emf.net--2003-example
        Summary: merge from Candice's Branch
        Keywords: 

        Patches applied:

          * candice@candice.net--2003-candice/hello-world--candice--0.1--patch-2
             merge from mainline sources

          * candice@candice.net--2003-candice/hello-world--candice--0.1--patch-1
             Punctuated the output correctly

          * candice@candice.net--2003-candice/hello-world--candice--0.1--base-0
             tag of 
              lord@emf.net--2003-example/hello-world--mainline--0.1--patch-1

        % tla commit
        [....]


慎重に読むべき注意: いま使った トリック によく注意してください. キャンディスの最新のリビジョンはアリスとボブが求めるものそのものでした. 彼らは getset-tree-version を組み合わせ, キャンディスのツリーを, 彼らのメインラインに簡単にコミットできるものにしました.

単純な開発ブランチ

両方のブランチで開発が進むにつれて何が起こるのか考えましょう。 このため, ちょっと新しいことを導入します: 複数のブランチと両者のマージを図示する方法です.

ここまでの事例の後では, 次の状況になってます;


      mainline--0.1                    candice--0.1
      -------------                    ------------
        base-0             -----------> base-0 (a tag)
        patch-1  ---------'             patch-1
        patch-2             ----------> patch-2
        patch-3  ----------'  --------'
        patch-4  <-----------'


これの意味することは candice ブランチはメインラインの patch-1 タグであることです; そして, candice ブランチの patch-2 で, mainlinepatch-3 までの全てのマージがありました; さらに, mainlineの patch-4が, candiceブランチの patch-2 までの全てをマージしました.

マージ行が全く交差しない図である場合はいつも 単純な開発ブランチ です.

単純な開発ブランチの意義は、いかにして 2つの開発作業が 1つのプロジェクトにおいて非同期的に働きうるかについての手本であることです. それぞれのブランチの, それぞれの作業において, プログラマは "update/commit" 様式で協力します (共同作業における更新/コミットの方式を参照). しかしながら, 一方のブランチの変更は二つのブランチがマージされるまでは影響を及ぼしません.

開発ブランチのマージの問題の導入

mainlinecandice ブランチの両方でさらに作業があったとしましょう. こんな具合にです:


      mainline--0.1                    candice--0.1
      -------------                    ------------
        base-0             -----------> base-0 (a tag)
        patch-1  ---------'             patch-1
        patch-2             ----------> patch-2
        patch-3  ----------'  --------' patch-3
        patch-4  <-----------'          patch-4
        patch-5
        patch-6


        % tla revisions --summary -A candice@candice.net--2003-candice \
                          hello-world--candice--0.1
        base-0
            tag of 
            lord@emf.net--2003-example/hello-world--mainline--0.1--patch-1
        patch-1
            Punctuated the output correctly
        patch-2
            merge from mainline sources
        patch-3
            added a period to output string
        patch-4
            capitalized the output string



        % tla revisions --summary -A lord@emf.net--2003-example \
                          hello-world--mainline--0.1
        base-0
            initial import
        patch-1
            Fix bugs in the "hello world" string
        patch-2
            commented return from main
        patch-3
            added copywrong statements
        patch-4
            merge from Candice's Branch
        patch-5
            fixed the copyrwrong for hw.c
        patch-6
            fixed the copyrwrong for main.c





mainline ブランチの新しい作業を candice ブランチにマージすることを目的とした筋書を考えましょう. つまり, 次のように話をもっていきたいです:


      mainline--0.1                    candice--0.1
      -------------                    ------------
        base-0             -----------> base-0 (a tag)
        patch-1  ---------'             patch-1
        patch-2             ----------> patch-2
        patch-3  ----------'  --------' patch-3
        patch-4  <-----------'          patch-4
        patch-5               --------> patch-5
        patch-6  ------------'


どうすれば, このようにマージできるでしょうか? 出発点はマージ直前の candice のリビジョン (patch-4) としましょう:


        % tla get -A candice@candice.net--2003-candice \
                       hello-world--candice--0.1--patch-4 \
                       hw-C-4
        [....]

        % cd hw-C-4


ここでは, うまくいかない 2つのやり方があります:

replay は開発ブランチのマージの問題を解決しない

replay は、mainlineからの "見当たらない" 変更すべてを candice ツリーへ適用を試みるでしょう. それが適用するチェンジセットのリストは次のものです:


        % tla missing --summary \
                      -A candice@candice.net--2003-example \
                      hello-world--mainline--0.1
        patch-4
            merge from Candice's Branch
        patch-5
            fixed the copyrwrong for hw.c
        patch-6
            fixed the copyrwrong for main.c


リスト中で問題なのは patch-4です。 それは candice ブランチからの patch-2 レベルまで 全ての変更を含むマージです. しかし、それらの変更は、candice ブランチの patch-4 リビジョンに既にあります. -- だから, replay はそれらを余分に適用するでしょう (そしてパッチの衝突 (conflict) を起こします).

警告の註: replay コマンドは、 さらなる replay を妨げません. たとえ, ソースツリーが 一貫した状態に無くてさえもです. 現在の実装の TLA は, リジェクトするファイルをマージしません. このため, 一回目の replay でのリジェクトを解決する前に 二回目の replay が実行された場合, パッチのリジェクトが失なわれる可能性があります. (いつの日にか, TLA が複数のリジェクトを一つの組み合わせたリジェクトに統合できるようになるかもしれません.)

上級者への注意: replay コマンドには、 mainline から patch-4 リビジョンを飛びこすことを許す オプションがあります. それはまあその問題を解決しますが, 欠点もあります. まず, これが意味するところは, patch-4candice ブランチの missing の 出力に現われつづけることです. 次に, patch-4 チェンジセットが candice ブランチからの マージのみを含むとは限りません. もし, アリスとボブが patch-4 で他の変更を加えて, かつ, そのチェンジセットを飛びこせば, それらの変更は失なわれることになります.

update は開発ブランチのマージの問題を解決しない

mainline ブランチから, updateを試みるとします. 次のことを思いだしてください: update は, プロジェクトツリーの最も若い mainline の先祖から, ツリー自身のチェンジセットを求め, これを, 最も最近の mainline のリビジョンに適用します.

このための記法があります. X から Y へのチェンジセットを次のように記します:

        delta(X, Y)

この場合、update が最初にするのは, mainlinepatch-3 リビジョンから私たちのプロジェクトツリーまでの チェンジセットを計算することです:

        delta(mainline--0.1--patch-3, hw-C-4) 

X から Y へのチェンジセットを ツリー Z に適用したためにできるツリーを以下のように記します:

        delta(X, Y) [ Z ]

言いかえれば、私たちのサンプルでの update の結果は 次のように書けます:

        delta(mainline--0.1--patch-3, hw-C-4) [mainline--0.1--patch-6]

しかし, ここが問題です. mainlinepatch-3リビジョンは, 以前は candice ブランチとはマージされませんでした。 したがって, チェンジセット

        delta(mainline--0.1--patch-3, hw-C-4)

は, 変更のなかでも特に, キャンディスブランチの patch-1patch-2 からの変更を含むでしょう.

不運にも、私たちがそのチェンジセットを適用するツリーの mainline--0.1--patch-6 は、 既に candice ブランチの base-0...patch-2 が マージされてます.

replay 同様, update は余分な変更を 起こすためにマージが衝突を起します.

開発ブランチのマージの問題の一例を解決する

delta 記法とマージの図だけを使って, このマージの問題を きれいに解決することを示します.

現状を思いだすと,

      mainline--0.1                    candice--0.1
      -------------                    ------------
        base-0             -----------> base-0 (a tag)
        patch-1  ---------'             patch-1
        patch-2             ----------> patch-2
        patch-3  ----------'  --------' patch-3
        patch-4  <-----------'          patch-4
        patch-5
        patch-6

で, 目的は新しいマージを, Candice のブランチの patch-5 に対して 作ることです:

                              --------> patch-5
        patch-6  ------------'

mainline ブランチから出発して, candice の変更点でまだ無い部分をマージしても良いですし, candice ツリーから出発して, mainline の 変更点でまだ無い部分をマージしても良いです. 後者で行きましょう (candice ツリーへマージ).

ここで, mainline-0.1 リビジョンの patch-6candice-0.1 リビジョン patch-2 まで "追随してます". そこからの変更を candice の最新版へ適用したいです:

        ここで:
                ancestor := candice--0.1--patch-2
                merge_in := mainline--0.1--patch-6
                target   := canidice--0.1--patch-4

        answer := delta(ancestor, merge_in)[target]

マージ図中の矢印は正解を探すために重要です。 例えば、キャンディスの patch-2から mainlineリビジョン patch-4への矢印が 無かったとします. このときは答は以下のとおりです:

        ここで:
                ancestor := mainline--0.1--patch-3
                merge_in := mainline--0.1--patch-6
                target   := canidice--0.1--patch-4

        answer := delta(ancestor, merge_in)[target]

マージに対応する矢印を追うのは退屈な作業です. これの自動化は, star-merge コマンドでなされます:

star-merge -- 開発ブランチのマージの問題を一般的に解決する

開発ブランチのマージ問題を, 一般的に, 完全に解決する方法を説明することは, 本書の範囲をちょっと越えます. 上で示した二つの解決策は二つの場合を説明します, しかし、少しだけ異なる解決策が必要なこともあります.

あなたが知るべきことは, 単純な開発ブランチ (単純な開発ブランチを参照)を持つ場合, star-merge コマンドは両者をマージする方法を知っていて, 偽のマージの衝突を起こさずに済むことです.

通常の使用では、情報をマージしたいツリーで star-merge を起動し, 引数としてマージしたいツリーを渡します:

        
        
        % tla get -A candice@candice.net--2003-candice \
                hello-world--candice--0.1--patch-4 \
                merge-temp

        % tla star-merge lord@emf.net--2003/hello-world-mainline--0.1


arch Meets hello-world: A Tutorial Introduction to The arch Revision Control System
The Hackerlab at regexps.com