C#からSubversionを使う
TortoiseSVNやAnkhSVNは優れたGUIツールですが、バッチ的な処理を行う場合にはスクリプト言語やプログラミングコードで処理できた方が便利な場合がありますよね。
ということで、C# Advent Calendat 2011 12/10分はそのようなお話です。
C#をはじめとする.NET Fxの言語からSubversionを使うためのライブラリがCOLLABNETから公開されていて、実際にAnkhSVNでも使用されているようです。
以下のCOLLABNETのサイトからダウンロードが可能です。
公開サイト:sharpsvn: Subversion Library for .Net 2.0+
また、Nugetを使ってインストールすることもできます。
では、実際に動く物を作ってみましょう。
適当なコンソールアプリケーションのプロジェクトを作って、Nugetを起動したら、”SVN”で検索してみましょう。sharpSVNは現状.NET Fx 4.0では動作しないので、プロジェクトのターゲットは.NET Fx 3.5としてください。
検索後以下のようにsharpsvnが見つかるはずです。
通常ではとりあえずx86版を選んでおけば良いと思います。
ただし、通常のダウンロード、NuGetで入手できるのはSVN 1.6に対応した安定版で、SVN1.6までとは互換性の無いSVN 1.7のワーキングディレクトリ形式に対応した物ではありません。
Tortoise SVNは既にSVN 1.7に対応していることもあり、今から何かを作るのであれば、1.7以降に対応したライブラリを使用した方が良いでしょう。
残念ながら1.7に対応したsharpSVNはNuGetでは入手できず、SVN 1.7に対応したsharpSVNは現状Daily Buildとしてのみ公開されているので、以下よりダウンロードして使用します。(最新版のダウンロードで問題ないはずです)
http://sharpsvn.open.collab.net/daily/1.7.x
ダウンロードしたら適当なフォルダに解凍し、プロジェクトにSharpSvn.dllへの参照を追加します。
では実際にコードを見ていきましょう。とは言ってもコードを見れば一目瞭然ですね。
まずはクライアントの作成。Subversionクライアントのインスタンスを作って、コマンド実行時に呼び出される通知用のイベントを追加します。
1 2 3 |
1 2 3 4 5 6 7 8 9 10 11 12 13 | private static void client_Notify(object sender, SvnNotifyEventArgs e) { switch (e.CommandType) { //チェックアウト/アップデート中のファイルを表示する case SvnCommandType.CheckOut: case SvnCommandType.Update: case SvnCommandType.Add: case SvnCommandType.Commit: Console.WriteLine(e.FullPath + ", Rev. " + e.Revision + "\t : " + e.Action); break; default: break; } } |
つぎはワーキングディレクトリにチェックアウトもしくはアップデートを実行する場合のコードです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | private static void CheckoutOrUpdate(string repo, string dir) { //チェックアウト string svnDir = @".svn"; if (Directory.Exists(dir + "\\" + svnDir)) { //.svnディレクトリが存在したらチェックアウト済みと見なし、アップデートする。 Console.WriteLine("++ Working Directory Update ++"); client.Update(dir); } else { //チェックアウトする。 Console.WriteLine("++ CheckOut ++"); client.CheckOut(new Uri(repo), dir); } } |
ファイルの追加。
1 2 3 4 5 6 | //add client.Add(filePath); //move(rename) client.Move(filePath, destPath); //delete client.Delete(filePath); |
ワーキングディレクトリのステータスの取得。
1 2 3 4 5 6 7 8 9 | private static void PrintStatus(string workingDir) { //ステータスを取得する Collection<SvnStatusEventArgs> stats; client.GetStatus(workingDir, out stats); foreach (var s in stats) { Console.WriteLine("File :{0}, Last change time :{1}, Staus :{2}", s.FullPath, s.LastChangeTime, s.LocalContentStatus); } } |
コミット。
1 2 3 4 5 6 7 8 9 | private static void Commit(string workingDir) { SvnCommitArgs ca = new SvnCommitArgs(); ca.LogMessage = "ファイルを追加"; SvnCommitResult results; client.Commit(workingDir, ca, out results); Console.WriteLine("Commit Time :{0}", results.Time); Console.WriteLine("Commit Author :{0}", results.Author); Console.WriteLine("Commit Revision :{0}", results.Revision); } |
全体コード。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Collections.ObjectModel; using SharpSvn; namespace SVNSample { /// <summary> /// SharpSVNのサンプル /// </summary> class Program { private static SvnClient client; //Subversion Client static void Main(string[] args) { if (args.Length <= 0) { Console.WriteLine("Usage: SVNSample [Repo URI] [Work Directory]"); } client = new SvnClient(); //イベント設定 client.Notify += new EventHandler<SvnNotifyEventArgs>(client_Notify); try { //資格情報を作成してクライアントに登録する。(ネットワーク越しで認証が必要なケース) //var cred = new System.Net.NetworkCredential("ishisaka", ""); //client.Authentication.DefaultCredentials = cred; //ワーキングディレクトリの状態によってチェックアウトもしくはアップデートを行う。 CheckoutOrUpdate(args[0], args[1]); //listコマンドの実行 Console.WriteLine("+++ Repo Directory / File List +++"); GetRepoList(new Uri(args[0])); //テスト用のファイルの作成 Console.WriteLine("+++ File Cleate \"hello[yyyymmddhhmmss].py\" +++"); var contents = "print 'Hello py'\n"; var dateStr = DateTime.Now.ToString("yyyyMMddhhmmss"); var filePath = args[1] + "\\trunk\\hello" + dateStr + ".py"; File.WriteAllText(filePath, contents); //ADD Console.WriteLine("+++ File Add +++"); client.Add(filePath); //ステータス Console.WriteLine("+++ Status +++"); PrintStatus(args[1]); //コミット Console.WriteLine("+++ Commit +++"); Commit(args[1]); Console.Write("何か入力で終了:"); Console.Read(); } finally { client.Dispose(); } } private static void Commit(string workingDir) { SvnCommitArgs ca = new SvnCommitArgs(); ca.LogMessage = "ファイルを追加"; SvnCommitResult results; client.Commit(workingDir, ca, out results); Console.WriteLine("Commit Time :{0}", results.Time); Console.WriteLine("Commit Author :{0}", results.Author); Console.WriteLine("Commit Revision :{0}", results.Revision); } private static void PrintStatus(string workingDir) { //ステータスを取得する Collection<SvnStatusEventArgs> stats; client.GetStatus(workingDir, out stats); foreach (var s in stats) { Console.WriteLine("File :{0}, Last change time :{1}, Staus :{2}", s.FullPath, s.LastChangeTime, s.LocalContentStatus); } } private static void CheckoutOrUpdate(string repo, string dir) { //チェックアウト string svnDir = @".svn"; if (Directory.Exists(dir + "\\" + svnDir)) { //.svnディレクトリが存在したらチェックアウト済みと見なし、アップデートする。 Console.WriteLine("++ Working Directory Update ++"); client.Update(dir); } else { //チェックアウトする。 Console.WriteLine("++ CheckOut ++"); client.CheckOut(new Uri(repo), dir); } } private static void GetRepoList(Uri url) { //Collectionの用意 Collection<SvnListEventArgs> list; string myPath = url.LocalPath; //ターゲットの作成 SvnUriTarget repo = new SvnUriTarget(url); //listコマンドの実行 client.GetList(repo, out list); foreach (var l in list) { //レポジトリのルートディレクトリは無視する。 if (l.Uri.LocalPath == l.RepositoryRoot.LocalPath) continue; switch (l.Entry.NodeKind) { case SvnNodeKind.Directory: if (l.Uri.LocalPath != myPath) { Console.WriteLine("Folder\t:{0}", l.Uri.LocalPath); GetRepoList(l.Uri); } break; case SvnNodeKind.File: Console.WriteLine("\tFile\t:{0}", Path.GetFileName(l.Uri.LocalPath)); break; } } list.Clear(); } private static void client_Notify(object sender, SvnNotifyEventArgs e) { switch (e.CommandType) { //チェックアウト/アップデート中のファイルを表示する case SvnCommandType.CheckOut: case SvnCommandType.Update: case SvnCommandType.Add: case SvnCommandType.Commit: Console.WriteLine(e.FullPath + ", Rev. " + e.Revision + "\t : " + e.Action); break; default: break; } } } } |
この記事のサンプルコード ダウンロード