!!! 目的 ソフトウェアをグループで開発する際,バージョン管理を行うことは必須でしょう. バージョンを管理してくれるソフトウェアはいくつも知られています. CVS,Git など,いろいろあります. この講義では「subversion (svn)」を使ってもらっています. 今まで,subversion の説明が何度か出てきましたが, 今日の講義の目的は,subversion の復習を行い, ((color red より効果的))に subversion を使ってもらうことです. : Thanks :: このページは [[svn::簡単な使い方]] を基に作成しました. !!! Subversionの利用したソフトウェア開発の概要 Subversionを使う際の概要を図示すると以下のようになります. ((img outline03.png 500)) 図において,班員達は,共有ディレクトリを更新(チェックアウト,コミット)しながらソフトウェア開発を行っています. この共有ディレクトリのことを((color red リポジトリ))といいます. 正確には,バージョン管理されたディレクトリです. リポジトリの中には,グループで開発したプログラムソースやドキュメントなどを格納します. 本講義では,このリポジトリに格納されているファイルをどのように更新していくのかを解説することで, subversionを利用したソフトウェア開発の流れの一例を解説します. //* [[サブバージョンの使い方]]を参照のこと. //* リポジトリ,ワーキングコピー,リビジョン,チェックアウト,コミット,などの単語の意味を理解すること. //** 普段の作業は,リポジトリからチェックアウトしたワーキングコピーの方を使う.'''リポジトリそのものには手を触れない'''. //** ある程度の完成度まで到達したら,ワーキングコピー(中の修正したファイル群)の内容をリポジトリに反映させる.これがコミット. //** コミット時には,どのファイルを,どのように修正したか,といった内容をログに残しておく. //** 別の人間が,同じファイルを別々に修正して,それぞれでコミットしようとすると衝突(コンフリクト)する //*** コンフリクトは,手動で対処する以外に方法はない. !!! 基本的な subversion の使い方 ここでは,subversionを使ってソフトウェア開発を行う際, 必要になるであろうコマンド達を紹介します. ほとんど復習の内容になりますが,まだ subversion を使うことに慣れていない人はしっかり復習してください. !! Subversionを利用する際の基本的な流れ ソフトウェアを開発する際,リポジトリに格納されているファイルを直接に編集することは行いません. では,どうするのかというと, * リポジトリの中身を,いったん,ローカルディレクトリにコピー(チェックアウト)して, * コピーしたファイル達を編集します. * 編集が一段落したら,編集したファイル達をリポジトリに保存(コミット(=チェックイン))します. ここで出てくるローカルコピーのことを((color red ワーキングコピー))とか,((color red 作業コピー))と呼びます. 「ワーキングコピーを編集してリポジトリに保存する」ということを 繰り返し行ってソフトウェア開発を少しずつ進めていきます. !! ワーキングコピーの取得: svn co(svn checkout) まずはリポジトリからコピーを取得する操作を行ってみましょう. コピーしたいディレクトリに移動してリポジトリの内容をコピーします. これを((color red チェックアウト))と言います. 例えば,g99班の班員が,自分のディレクトリ ~/csd/g99/ に, proj という名前のワーキングコピーを展開したいとしたら, 以下のコマンドを打ちます. 8< % cd ~/csd/g99/ % svn co https://svn.cis.iwate-u.ac.jp/svn/csd/14/g99/proj >8 : Tips :: チェックアウトという用語を初めて聞くと「なぜチェックアウトと言うんだろう???」と思う方のいるかもしれません. 私なりの解釈はこうです.リポジトリを「ホテル」だと思いましょう. そして,プログラムファイルやドキュメントといった成果物をホテルの「客」だと思いましょう. リポジトリというホテルに,お客さんとして成果物が泊まっていると考えたとします. 成果物は,ユーザからのコマンドによってホテルを「チェックアウト」してくるのです. 逆に,成果物をリポジトリに保存する操作はホテルへの「チェックイン」となります. しかしながら,本講義では,subversion のコマンド名を見習って 「コミット(= チェックイン)」という用語を使いますのでご了承ください. !! ワーキングコピーの更新: svn update 複数の人間で開発を行なう場合,他の人が開発を進めているかもしれません. つまり,自分のワーキングコピーは,最新のプログラムファイルでないかもしれません. そこで,編集作業を行う前に((color red 必ず))ワーキングコピーに (リポジトリの)最新情報を反映させる必要があります. 8< % cd ~/csd/proj % svn update >8 : Tips :: 自分が編集作業中でも,他の班員が作業しているかもしれません. こまめに svn update を実行するようにしましょう. !! ワーキングコピーの状態確認: svn st(svn status) 「ワーキングコピーのファイル xxx.c は,リポジトリに登録されていたっけ?」 などといった疑問が生じたときには,ワーキングコピー上のファイルの状態を 調べるということを行います. ワーキングコピー上のファイルの状態を確かめるには, (ワーキングコピーが存在するディレクトリ以下で) 8< % svn st >8 というコマンドを実行します. ファイル名の先頭にマークがつけられており, そのマークで状態を判断します. 6< :? ::どんな状態か分からない.(例えば,ワーキングコピーディレクトリ下に新たにファイルを作成したが,svn addしていない場合,とか) :A ::追加(このファイルは,次のコミットでリポジトリに保存される) :D ::削除(このファイルは,次のコミットでリポジトリから削除される) :C ::競合(=衝突)(このファイルを,他の開発者が既に変更している --> あとで詳しく解説) :M ::変更(このファイルは,リポジトリから変更されている) :! ::バージョン管理下で,ファイル,ディレクトリが不完全,または失われている.svn のコマンド以外でファイルを消去したりするとよく起こる.svn revertなどを使って回復できるが,revertコマンドは「変更を破棄してしまう」という意味で本質的に危険なので,多用しないこと.(!フラグが出ないように気をつけてファイル管理すること) >9 オプションで「-u」を入れると,リビジョン番号の情報が, 「-v」 を入れると,各ファイルのリビジョン番号の情報が表示されます. !! ワーキングコピーに対する修正変更をリポジトリに登録: svn ci(svn commit) 自分が加えた修正をリポジトリに反映させることを ((color red コミット))または((color red チェックイン))と言います. 次の svn ci というコマンドでコミットすることができます. 8< (proj内のxxx.cを修正して登録する場合の例) % cd ~/csd/proj % emacs xxx.c % svn ci -m '... Messages ...' >8 「... Messages ...」の部分には,何を変更したかを書いておきます. このメッセージは必ず書かないといけません. ((color blue commitする際には必ずメッセージが必要))と覚えておきましょう. メッセージには,他の共同開発者に修正内容を伝える役割があります. まったく無意味なメッセージを書くことは極力避けるべきです. !! ファイルやディレクトリを新規に追加する ディレクトリ(dir1)を新たに追加したい場合は, 8< % svn mkdir dir1 >8 のように,'''svn mkdir'''とすること.また,ファイル(a.txt)を新たに追加したいときは, ファイルを作った後に,リポジトリに反映させるために 8< % svn add a.txt >8 のように'''svn add'''とします.ただし,'''これらが真にリポジトリに登録追加されるのはコミットした時点'''なので注意が必要です. svn add を行った時点では,リポジトリは以前の状態のままであり,a.txt を含みません. svn ci でコミットして初めてリポジトリに a.txt が登録されます. : Tips :: リポジトリに登録するファイルは「プロジェクトの成果物」のみにしましょう.プログラム(.cや.hなど)や,ドキュメント(READMEなど)はリポジトリに登録しますが,実行ファイル(a.outなど),オブジェクトファイル(.o),作業用ファイル(#,~のついたもの),コアファイル(.core)はリポジトリには登録しません.膨大な量の記憶領域を無駄遣いすることになってしまいます. !! その他,ファイルやディレクトリに対する操作コマンド svn add 以外にも,ファイルやディレクトリを操作するコマンドがいくつかあります. || コマンド | 動作 || svn rm(svn remove)| ファイル,ディレクトリを削除 || svn cp(svn copy)| ファイル,ディレクトリコピー || svn mv(svn move)| ファイル,ディレクトリを移動(別名にする) 上記はすべて,svn add と同様に,コミット時に操作内容がリポジトリに反映されるので注意しましょう. !! ファイル,ディレクトリの情報を見るためのコマンド || コマンド      | 動作 || svn diff | 自分が変更したファイルと,リポジトリに登録されている最新版とを比較する || svn log | 開発履歴を閲覧する(ここで表示されるメッセージは,commit時に -m オプションで書きこまれたもの) オプションで「-v」を加えると,より詳細な情報も表示される. || svn info | ファイル,ディレクトリに関する情報を表示 || svn ls | ((color red リポジトリ上))のディレクトリエントリーを一覧表示 !!! 競合とその解決 同じファイルを複数人が編集した場合,ファイルに不整合が生じてしまいます. これを((color red 競合)) または ((color red コンフリクト)),((color red 衝突))と呼びます. ここでは競合を解決するためのsubversionの機能を解説します. ただし,本質的な解決は,((color blue 競合を引き起こした開発者同士で話し合う必要がある))ことに注意しましょう. (きちんとした作業分担が行われていれば, 実際の開発において,競合はほとんど起こらないと言われていますが, グループ開発に慣れていない開発者達は競合に苦しめられることでしょう.) 次のような状況を考えましょう.サブ君とバージョン君は同じ班に属しています. この班では,好きな果物を表示するというフルーツプロジェクトを担当しています. 開発中に,サブ君とバージョン君の2人が同じプログラムファイル fruits.c を編集してしまったとします. サブ君,バージョン君,それぞれがリポジトリへのコミットを行ったのですが, * はじめに,サブ君が fruits.c のコミットを行って, * 次に,バージョン君が fruits.c のコミットを行った. となったとして下さい. ! 編集箇所が重ならない競合 Subversionは,サブ君,バージョン君,それぞれの変更を ((color blue 両方とも採用したプログラムファイルを自動で作ってくれます)). バージョン君がコミットしようとすると, 8< (サブ君がコミットした後,下のようにバージョン君がコミットしているとします) % svn ci -m "... Massages ..." 送信しています fruits.c svn: E160042: コミットに失敗しました (詳しい理由は以下のとおりです): svn: E160042: ファイルまたはディレクトリ 'fruits.c' はリポジトリ側と比べて古くなっています。update を実行してみてください svn: E160024: resource out of date; try updating >8 と怒られます.Subversionの指示通り「svn update」を実行してみましょう.すると,以下のようになります. 8< % svn update Updating '.': G fruits.c リビジョン xxxxx に更新しました。 >8 ここで「G」は((color blue ファイルが問題なくマージされたこと))を意味します. fruits.c の中身を見てみると, サブ君の修正内容とバージョン君の修正内容とがどちらも反映されているはずです. ! 編集箇所が重なってしまった競合 このような衝突は起きてしまった場合は,衝突を引き起こした開発者同士で話し合いをして解決策を見つける必要があります. 解決策を決めてから,またプログラムを書き直すことはとても手間のかかる作業になるでしょう. しかしながら,Subversion は,この作業を賢くサポートしてくれます. 例を見ながら解説します. サブ君とバージョン君は,fruits.c の同じ箇所を修正してしまったとします. はじめにコミットを行ったサブ君は,衝突のことを気にせずコミットを完了させるでしょう. しかし,続いてコミットを行ったバージョン君は,エラーが起きて困ってしまいます. 8< (バージョン君が実行している状況を想定して下さい) % svn ci -m "... Massages ..." 送信しています fruits.c svn: E160042: コミットに失敗しました (詳しい理由は以下のとおりです): svn: E160042: ファイルまたはディレクトリ 'fruits.c' はリポジトリ側と比べて古くなっています。update を実行してみてください svn: E160024: resource out of date; try updating >8 うーん,怒られてしまいました.とりあえず,Subversionの指示通り「svn update」を実行してみます. 8< % svn update Updating '.': '***(ローカルディレクトリのパス名)***/fruits.c' で競合が見つかりました。 選択: 延期 (p), 全差分 (df), 編集 (e), 衝突自分 (mc), 衝突他人 (tc), , すべてのオプションを表示 (s): >8 すると,いくつかの選択肢が表示されました. 競合が起きているファイルに対してどのような処置を施すのかを聞かれています. まずは,どのような競合が起きているかを確認するために「df」を選択してみましょう. 8< (競合が起きている場所の内容が表示されます.) ..... ..... ..... 選択: 延期 (p), 全差分 (df), 編集 (e), 解決版 (r), 衝突自分 (mc), 衝突他人 (tc), , すべてのオプションを表示 (s): >8 さぁ,ここからが本番です.どうにかして競合を解決しなければなりません. 単なるケアレスミスで,すぐに解決策が分かるならば問題ありませんが, ほとんどの場合,開発者同士の話合いが必要になると思います. バージョン君は,サブ君と話し合いをするため, いったん「延期(p)」を選択したとします. すると,いくつかのファイルが自動で生成されます. 「fruits.c.mine」「fruits.c.rxxxxx」です. それぞれ「ワーキングコピーのfruits.c」「リビジョンxxxxxのfruits.c」に対応しています. ここで注意して欲しいのは,((color blue fruits.c の中身が自動で変更される))ということです, 競合が起きている箇所が以下のように変更されています. 8< +<<<<<<< .mine + + (自分のワーキングコピー上の内容) + +======= + + (リポジトリ内の内容) + +>>>>>>> .rxxxxx >8 上の「<<<<<<<」と「>>>>>>>」を((color red 競合マーカー))と呼びます. 競合マーカーで囲まれている部分には「自分自身が作った修正内容」と 「リポジトリ内の内容」とが両方とも記載されています. 続いて,バージョン君は,サブ君の席へ行き,競合を解決するための話し合いを行います. 話し合いの結果,バージョン君の修正が正しいことが判明したとしましょう. その場合,次のようなコマンドを打ちます. 8< % cp fruits.c.mine fruits.c(バージョン君が作った fruits.c を採用) % svn resolved fruits.c(競合を解決したことをsubversionに伝える) >8 これで,競合が解消されました. Subversionが自動で生成したファイルは消されているはずです. ここで「svn resolved」は,競合が解決されたことを subversion に通知するコマンドです. 競合を解消する方法はいくつもありますが,最も代表的と思われるシチュエーションを解説しました. !!! 参考文献 * Subversion実践入門 第2版 Mike Mason著/でびあんぐる監訳