ソフトウェア設計及び演習2017

subversionの使い方2017

目的

ソフトウェアをグループで開発する際,バージョン管理を行うことは必須でしょう. バージョンを管理してくれるソフトウェアはいくつも知られています. CVS,Git など,いろいろあります. この講義では「subversion (svn)」を使ってもらっています. 前回,各班の subversion の設定を行ってもらいました. 今日の講義では,subversion の解説を行い, 効果的に subversion を使ってもらうことです. ただし,ごく基本的な使い方を説明するのみなので,各自,調べながらスキルアップを心がけてください.

本ページは山中が作成しました.
Thanks
このページは svn::簡単な使い方 を基に作成しました.

Subversionの利用したソフトウェア開発の概要

Subversionを使う際の概要を図示すると以下のようになります.

図において,班員達は,共有ディレクトリを更新(チェックアウト,コミット)しながらソフトウェア開発を行っています. この共有ディレクトリのことをリポジトリといいます. 正確にはバージョン管理されたディレクトリです. リポジトリの中には,グループで開発したプログラムソースやドキュメントなどを格納します.

本講義では,このリポジトリに格納されているファイルをどのように更新していくのかを解説することで, subversionを利用したソフトウェア開発の流れの一例を解説します.

今日の解説の仮定

グループ開発を想定して解説したほうが分かりやすいので, g99班という架空の班があるとして解説していきます. g99班は,サブ君とバージョン君の2人から構成されているとします. この班では,好きな果物を表示するというフルーツプロジェクトを開発しています.

基本的な subversion の使い方

ここでは,subversionを使ってソフトウェア開発を行う際, 必要になるであろうコマンド達を紹介します.

Subversionを利用する際の基本的な流れ

ソフトウェアを開発する際,リポジトリに格納されているファイルを直接に編集することは行いません. では,どうするのかというと,

  • リポジトリの中身を,いったん,ローカルディレクトリにコピー(チェックアウト)して,
  • コピーしたファイル達を編集します.
  • 編集が一段落したら,編集したファイル達をリポジトリに保存(コミット(=チェックイン))します.

ここで出てくるローカルコピーのことをワーキングコピーとか,作業コピーと呼びます. 「ワーキングコピーを編集してリポジトリに保存する」ということを 繰り返し行ってソフトウェア開発を少しずつ進めていきます.

ワーキングコピーの取得: svn co(svn checkout)

まずはリポジトリからコピーを取得する操作を行ってみましょう. コピーしたいディレクトリに移動してリポジトリの内容をコピーします. これをチェックアウトと言います.

例えば,g99班の班員が,自分のディレクトリ ~/csd/ に, g99 という名前のワーキングコピーを展開したいとしたら, 以下のコマンドを打ちます.

% cd ~/csd
% svn co https://svn.cis.iwate-u.ac.jp/svn/csd/17/g99

リポジトリ上のファイル,ディレクトリの情報を見るためのコマンド

通常のunixコマンドの頭に svn をつけると,リポジトリ上のディレクトリやファイルに対する操作になります.

コマンド      動作
svn ls リポジトリ上のディレクトリエントリーを一覧表示
svn cat リポジトリ上のファイルの中身を表示
svn diff 自分が変更したファイルと,リポジトリに登録されている最新版とを比較する
svn log 開発履歴を閲覧する(ここで表示されるメッセージは,commit時に -m オプションで書きこまれたもの) オプションで「-v」を加えると,より詳細な情報も表示される.
svn info ファイル,ディレクトリに関する情報を表示

使い始めのうちは,作業コピー(自分のローカルディレクトリ)上の状況と, リポジトリ上の状況を混同してしまう場合が多々あります. 通常のunixコマンドは作業コピー(自分のローカルディレクトリ)に対する操作, 頭に「svn」をつけるとリポジトリ上の操作になる,と覚えておきましょう.

ファイルを新規に追加する

ファイル「svn-test.txt」を新たに追加したいときは, ファイルを作った後に,リポジトリに反映させるために

% emacs svn-test.txt
(emacs を 使って svn-test.txt の中身を作成)
% svn add svn-test.txt

のようにsvn addとします.ただし,これらが真にリポジトリに登録追加されるのはコミットした時点なので注意が必要です. svn add を行った時点では,リポジトリは以前の状態のままであり,Svn-test や svn-text.txt を含みません. svn ci でコミットして初めてリポジトリに Svn-test と svn-text.txt が登録されます.

ワーキングコピーに対する修正変更をリポジトリに登録: svn ci(svn commit)

自分が加えた修正をリポジトリに反映させることを コミットまたはチェックインと言います. 次の svn ci というコマンドでコミットすることができます.

% svn ci -m '... Messages ...' 

「... Messages ...」の部分には,何を変更したかを書いておきます. このメッセージは必ず書かないといけません. commitする際には必ずメッセージが必要と覚えておきましょう. メッセージには,他の共同開発者に修正内容を伝える役割があります. まったく無意味なメッセージを書くことは極力避けるべきです.

ディレクトリをリポジトリに新規で追加

ディレクトリ「Svn-test」を新たに追加したい場合は,

% svn mkdir Svn-test

のように,svn mkdirとします.

その他,ファイルやディレクトリに対する操作コマンド

上記の svn add 以外にも,ファイルやディレクトリを操作するコマンドがいくつかあります.

コマンド 動作
svn rm(svn remove) ファイル,ディレクトリを削除
svn cp(svn copy) ファイル,ディレクトリコピー
svn mv(svn move) ファイル,ディレクトリを移動(別名にする)

上記はすべて,svn add と同様に,コミット時に操作内容がリポジトリに反映されるので注意しましょう.

ワーキングコピーの更新: svn up(svn update)

複数の人間で開発を行なう場合,他の人が開発を進めているかもしれません. つまり,自分のワーキングコピーは,最新のプログラムファイルでないかもしれません. そこで,編集作業を行う前に必ずワーキングコピーに (リポジトリの)最新情報を反映させる必要があります.

% svn up
Tips
自分が編集作業中でも,他の班員が作業しているかもしれません. こまめに svn up を実行するようにしましょう.

ワーキングコピーの状態確認: svn st(svn status)

「ワーキングコピーのファイル xxx.c は,リポジトリに登録されていたっけ?」 などといった疑問が生じたときには,ワーキングコピー上のファイルの状態を 調べるということを行います. ワーキングコピー上のファイルの状態を確かめるには, (ワーキングコピーが存在するディレクトリ以下で)

% svn st

というコマンドを実行します. ファイル名の先頭にマークがつけられており, そのマークで状態を判断します.

?
どんな状態か分からない.(例えば,ワーキングコピーディレクトリ下に新たにファイルを作成したが,svn addしていない場合,とか)
A
追加(このファイルは,次のコミットでリポジトリに保存される)
D
削除(このファイルは,次のコミットでリポジトリから削除される)
C
競合(=衝突)(このファイルを,他の開発者が既に変更している --> あとで詳しく解説)
M
変更(このファイルは,リポジトリから変更されている)
!
バージョン管理下で,ファイル,ディレクトリが不完全,または失われている.svn のコマンド以外でファイルを消去したりするとよく起こる.svn revertなどを使って回復できるが,revertコマンドは「変更を破棄してしまう」という意味で本質的に危険なので,多用しないこと.(!フラグが出ないように気をつけてファイル管理すること)

オプションで「-u」を入れると,リビジョン番号の情報が, 「-v」 を入れると,各ファイルのリビジョン番号の情報が表示されます.

競合とその解決

同じファイルを複数人が編集した場合,ファイルに不整合が生じてしまいます. これを競合 または コンフリクト衝突と呼びます. ここでは競合を解決するためのsubversionの機能を解説します. ただし,本質的な解決は,競合を引き起こした開発者同士で話し合う必要があることに注意しましょう. (きちんとした作業分担が行われていれば, 実際の開発において,競合はほとんど起こらないと言われていますが, グループ開発に慣れていない開発者達は競合に苦しめられることでしょう.)

次のような状況を考えましょう. 開発中に,サブ君とバージョン君の2人が同じプログラムファイル fruits.c を編集してしまったとします. サブ君,バージョン君,それぞれがリポジトリへのコミットを行ったのですが,

  • はじめに,サブ君が fruits.c のコミットを行って,
  • 次に,バージョン君が fruits.c のコミットを行った.

となったとして下さい.

編集箇所が重ならない競合

Subversionは,サブ君,バージョン君,それぞれの変更を 両方とも採用したプログラムファイルを自動で作ってくれます. バージョン君がコミットしようとすると,

(サブ君がコミットした後,下のようにバージョン君がコミットしているとします)

% svn ci -m "... Massages ..."

送信しています              fruits.c
svn: E160042: コミットに失敗しました (詳しい理由は以下のとおりです):
svn: E160042: ファイルまたはディレクトリ 'fruits.c' はリポジトリ側と比べて古くなっています。update を実行してみてください
svn: E160024: resource out of date; try updating

と怒られます.Subversionの指示通り「svn up」を実行してみましょう.すると,以下のようになります.

% svn up

Updating '.':
G    fruits.c
リビジョン xxxxx に更新しました。

ここで「G」はファイルが問題なくマージされたことを意味します. fruits.c の中身を見てみると, サブ君の修正内容とバージョン君の修正内容とがどちらも反映されているはずです. その後,コミットしてリポジトリにも修正を反映させておきます.

% svn ci -m "... Messages ..."
編集箇所が重なってしまった競合

このような衝突は起きてしまった場合は,衝突を引き起こした開発者同士で話し合いをして解決策を見つける必要があります. 解決策を決めてから,またプログラムを書き直すことはとても手間のかかる作業になるでしょう. しかしながら,Subversion は,この作業を賢くサポートしてくれます. 例を見ながら解説します.

サブ君とバージョン君は,fruits.c の同じ箇所を修正してしまったとします. はじめにコミットを行ったサブ君は,衝突のことを気にせずコミットを完了させるでしょう. しかし,続いてコミットを行ったバージョン君は,エラーが起きて困ってしまいます.

(バージョン君が実行している状況を想定して下さい)

% svn ci -m "... Massages ..."

送信しています              fruits.c
svn: E160042: コミットに失敗しました (詳しい理由は以下のとおりです):
svn: E160042: ファイルまたはディレクトリ 'fruits.c' はリポジトリ側と比べて古くなっています。update を実行してみてください
svn: E160024: resource out of date; try updating

うーん,怒られてしまいました.とりあえず,Subversionの指示通り「svn up」を実行してみます.

% svn up

Updating '.':
'***(ローカルディレクトリのパス名)***/fruits.c' で競合が見つかりました。
選択: 延期 (p), 全差分 (df), 編集 (e),
        衝突自分 (mc), 衝突他人 (tc),
        , すべてのオプションを表示 (s): 

すると,いくつかの選択肢が表示されました. 競合が起きているファイルに対してどのような処置を施すのかを聞かれています. まずは,どのような競合が起きているかを確認するために「df」を選択してみましょう.

(競合が起きている場所の内容が表示されます.)

.....
.....
.....
 
選択: 延期 (p), 全差分 (df), 編集 (e), 解決版 (r),
        衝突自分 (mc), 衝突他人 (tc),
        , すべてのオプションを表示 (s): 

さぁ,ここからが本番です.どうにかして競合を解決しなければなりません. 単なるケアレスミスで,すぐに解決策が分かるならば問題ありませんが, ほとんどの場合,開発者同士の話合いが必要になると思います.

バージョン君は,サブ君と話し合いをするため, いったん「延期(p)」を選択したとします.

すると,いくつかのファイルが自動で生成されます. 「fruits.c.mine」「fruits.c.rxxxxx」です. それぞれ「ワーキングコピーのfruits.c」「リビジョンxxxxxのfruits.c」に対応しています. ここで注意して欲しいのは,fruits.c の中身が自動で変更されるということです, 競合が起きている箇所が以下のように変更されています.

+<<<<<<< .mine
+
+    (自分のワーキングコピー上の内容)
+
+=======
+
+    (リポジトリ内の内容)
+
+>>>>>>> .rxxxxx

上の「<<<<<<<」と「>>>>>>>」を競合マーカーと呼びます. 競合マーカーで囲まれている部分には「自分自身が作った修正内容」と 「リポジトリ内の内容」とが両方とも記載されています.

続いて,バージョン君は,サブ君の席へ行き,競合を解決するための話し合いを行います. 話し合いの結果,バージョン君の修正が正しいことが判明したとしましょう. その場合,次のようなコマンドを打ちます.

% cp fruits.c.mine fruits.c(バージョン君が作った fruits.c を採用)
% svn resolved fruits.c(競合を解決したことをsubversionに伝える)

これで,競合が解消されました. Subversionが自動で生成したファイルは消されているはずです. ここで「svn resolved」は,競合が解決されたことを subversion に通知するコマンドです. その後,コミットを行ってリポジトリにも反映しておきましょう.

% svn ci -m "... Messages ..."

競合を解消する方法はいくつもありますが,最も代表的と思われるシチュエーションを解説しました.

参考文献

  • Subversion実践入門 第2版 Mike Mason著/でびあんぐる監訳


最終更新日:2017/04/28 11:08:14