subversionでのブランチマネジメント


2010年 12月 08日

分散バージョン管理システムつかってますか?

世の中ではgitやhgなどの分散型のバージョン管理システムが流行していて、「もうsvnなんて、、、」「まだsvnつかっているの、、、」という風潮になっています。
弊社内でもgitのレポジトリが立ったり、svnのプロジェクトでも自分の環境だけはgit-svnで分散バージョン管理を使う人が増えています。

「自分の環境だけはgit-svnで」。そう、社内ではまだまだsvnを使っているプロジェクトが多いのです。「日本語のファイル名が使えない」「デザイナーさんに使ってもらうためのわかりやすいクライアントが無い」「svnからなかなか移行するコストが、、」などの理由でsvnを使い続けているプロジェクトも多いと思います。

というわけで、分散バーション管理システムではなく社内で運用されているsvnでのブランチマネジメントについて、備忘録もかねて説明します。

前提

次の様な前提で話をします。

  • trunk 開発、不安定版
  • testingブランチ 次期リリース用開発ブランチ
  • releaseブランチ リリース済み、メンテナンスブランチ

説明する構成はこちら。

  • trunk 一本
  • trunk + release ブランチ
  • trunk + testing + release ブランチ

trunk 一本

小規模なプロジェクトや、保守の少ないプロジェクトでよく見られます。
trunkで開発ないし保守を行い、リリース時にタグを打ちます。

              o (tags/release_2010_12_10)
              |
              |         o (tags/release_2010_12_13)
              |         |
              v         v
    o----o----o----o----o----o----o----o (trunk)

trunk + release ブランチ

  • リリース後にすぐに次の改修がスタートする。が、releaseしたアプリケーションもメンテナンスしなければならない

という状況で利用します。

            o (tags/release_2010_12_10)
            |
            |              o (tags/release_2010_12_13)
            |              |
            v              v
            o----o---------o (branches/release_2010_12_10)
           /
          /
    o----o----o----o----o (trunk)

releaseブランチのメンテナンス

ブランチマネージャというブランチを管理する役割をたてます。
releaseしたアプリケーションの修正はreleaseブランチに対して行います。
releaseの修正とtrunkで取りこぼさないために、ブランチマネージャはrelaseブランチに発生した修正をtestingにマージします。

    cd trunk
    svn merge -r A:C svn+ssh://hostname/path/to/svn/repos/branches/release_2010_12_10

            o (tags/release_2010_12_10)
            |
            |              o (tags/release_2010_12_13)
            |              |
            v              v
            A----B---------C (branches/release_2010_12_10)
           /                \
          /                  \ B-Cをマージ
    o----o----o----o----o-----o (trunk)

最近のsvnではmergeinfoというマージの情報が記録されます。マージ漏れを防ぐためにB-Cのように、常に-でつなぐ形になるようにしましょう。

    svn proplist -v .
    '.' の属性:
      svn:mergeinfo
        /branches/release_2010_12_10:B-C

マージバック

  • releaseへのコミットをコントロールしたい
  • svn switchのコストがあまりに高い場合

など、何らかの理由releaseで作業ができない場合はtrunkに修正を入れ、その修正をreleaseブランチにマージバックする場合もあります。
この場合、修正の数に比例して(releaseブランチの品質が低いほど)ブランチマネージャの負担が増加します。

trunk + testing + release ブランチ

  • releaseした後、開発と保守が続く。しかも、複数のマイルストーン設定されている
    という状況で利用します。
    testingブランチには2010_12_20がマイルストーンの機能のみコミットしていきます。
    trunkにはそれ以降リリースの機能をコミットしていきます。
                 o (tags/release_2010_12_10)
                 |
                 |                  o (tags/release_2010_12_13)
                 |                  |
                 v                  v
                 o------------------o (branches/release_2010_12_10)
                /
               /
              /
             /         o--------o-------o----o-----------o--------o (branches/testing_2010_12_20)
            /         /
           /         /
          /         /
    o----o----o----o----o------------o-----------------o------------o (trunk)

この構成の場合のブランチのメンテナンスは二通りあります。

「release -> testing」、「testing -> trunk」を行う

「trunk + release ブランチ」の場合のメンテナンス方法をそのまま適応したパターンです。
releaseの修正はreleaseにコミット。testingの修正はtestingにコミットし、ブランチマネージャは
「release -> testing」のマージ、「testing -> trunk」のマージを順番に行う必要があります。

「testing -> relase」、「testing -> trunk」を行う

releaseの修正をtestingにコミットし、特定のリビジョンをブランチマネージャがreleaseにマージバックするパターンです。ブランチマネージャは「testing -> trunk」のマージだけを気にすればよく、順番に依存する事は無くなります。
マージの回数は増えますが、releaseには数コミットずつのマージしか発生しないため、衝突の解決が簡単になります。

testingのリリース

testingをリリースする場合は、testing_2010_12_20をrelease_2010_12_20にリネームします。
その瞬間前リリースブランチのrelease_2010_12_10は役目を終え、メンテナンスを終了します。

    svn mv svn+ssh://hostname/path/to/svn/repos/branches/testing_2010_12_20 \
           svn+ssh://hostname/path/to/svn/repos/branches/release_2010_12_20

                 o (tags/release_2010_12_10)
                 |
                 |                  o (tags/release_2010_12_13)       o (tags/release_2010_12_20)
                 |                  |                                 |
                 v                  v                                 v
                 o------------------o (branches/release_2010_12_10)   o (branches/release_2010_12_20)
                /                                                    /
               /                                                    /
              /                                                    /
             /         o--------o-------o----o-----------o--------o (branches/testing_2010_12_20)
            /         /
           /         /
          /         /
    o----o----o----o----o------------o-----------------o------------o (trunk)

testingブランチの延命

期日までに開発が終わらなくなってしまった場合にtestingブランチを延命する事があります。

    svn cp svn+ssh://hostname/path/to/svn/repos/branches/testing_2010_12_20 \
           svn+ssh://hostname/path/to/svn/repos/branches/testing_2010_12_30
    svn mv svn+ssh://hostname/path/to/svn/repos/branches/testing_2010_12_20 \
           svn+ssh://hostname/path/to/svn/repos/branches/release_2010_12_20

                 o (tags/release_2010_12_10)
                 |
                 |                  o (tags/release_2010_12_13)       o (tags/release_2010_12_20)
                 |                  |                                 |
                 v                  v                                 v
                 o------------------o (branches/release_2010_12_10)   o (branches/release_2010_12_20)
                /                                                    /
               /                                                    /
              /                                                    /
             /         o--------o-------o----o-----------o--------o (branches/testing_2010_12_20)
            /         /                                            \
           /         /                                              o (branches/testing_2010_12_30)
          /         /
    o----o----o----o----o------------o-----------------o------------o (trunk)

その他のブランチ

個人用の作業ブランチ

「ある機能を実装するが、他の人のコミットに左右されたくない」という場合に利用します。
ある機能を入れたいブランチ(たとえばtrunk)から自分用のブランチを作成し、作業が完了したら分岐元のブランチにマージします。

    svn cp svn+ssh://hostname/path/to/svn/repos/trunk \
           svn+ssh://hostname/path/to/svn/repos/branches/troter/working \
           -m "troter用の作業ブランチ"
    # 作業ブランチにスイッチ
    svn sw svn+ssh://hostname/path/to/svn/repos/branches/troter/working
    # 何か作業してコミット
    emacs xxx; svn ci -m "hoge";
    # 分岐元にスイッチ
    svn sw svn+ssh://hostname/path/to/svn/repos/trunk
    # 作業ブランチでの変更をマージ
    svn merge -r n:m svn+ssh://hostname/path/to/svn/repos/branches/troter/working
    # コミット
    svn ci -m "○○を実装"
    # 用が済んだら消しましょう。
    svn rm svn+ssh://hostname/path/to/svn/repos/branches/troter/working

トピックブランチ

個人用の作業ブランチは人を主軸に置いた考えですが、人ではなく機能に主軸を置くとトピックブランチになります。ある機能を実装するためだけにブランチを作成します。
イシュートラッカー(trac、redmine)などのイシューの番号をブランチ名にするとわかりやすいです。

    svn cp svn+ssh://hostname/path/to/svn/repos/trunk \
           svn+ssh://hostname/path/to/svn/repos/branches/issue/2010 \
           -m "#2010 ○○機能の実装"

実装できたら個人用の作業ブランチと同じように分岐元に変更をマージします。

その他の話題

大規模な変更、リファクタリングを行うタイミング

ブランチマネージャの立場を考えると、trunk + releaseブランチしか存在しない瞬間に行ってほしいです。
それ以外のタイミングで行うとコンフリクトの解決が大変です。

testingからtrunkへのマージを行う間隔

testingとtrunkが乖離しないように頻繁にマージを行った方がよいです。
bugfixがメインの状態であれば40リビジョンくらいのマージでもそれほど苦労はありません。
開発がメインでどんどん変更される場合は15位を目安にマージした方がよいと思います。

ブランチの運用に困っている人に参考になれば幸いです。