git-svnの使い方を覚えた

分散SCMを使いたい!と思う今日この頃。

仕事ではSVNSubversion)を使っているのだが、ちょっとしたお試し編集をするためにブランチを作ることに抵抗がある。ブランチは欲しい、大きめな変更をコミット無しで行いたくない、やはり少しずつコミットして進めていきたい。しかし、変更が全て記録されてしまうのがいただけない。ログが残るのは良いことなのだが、本当に使うかどうか未知数な実験的プログラミングのログまで残したくない。使うと決まってから初めて残すようにしたいのだ。

すまん、これまで一緒に仕事をしてきた人々よ。俺はこれまで「ログが残って困ることがなんかある?いらなきゃ無視すればいいだけなんだから、気にするな。ブランチでもなんでもバンバン作ってしまえ!」とうそぶいてきているわけだが…ハッタリかましてました!本当は俺も抵抗があるのだ。

そこで、分散SCMだ。さらにいうと、SVKがいまひとつ気に入らなかったのと、Git勉強中なので、この際git-svnだ。そんなわけで、使い方を覚えてみた。

Gitには、git-svnというSVNと連携するための機能というかコマンドというかがある。

インストール

GitはMacPortsでインストールしたのだが、普通にgit-coreをインストールしてもgit-svnは有効になっていない。有効にするためには、次のようにする必要があった。

$ sudo port install git-core +svn

または、バイナリをダウンロードして自分でインストールすればOK.

チェックアウト

SVNリポジトリのクローンとなるローカルgitリポジトリを生成する。

$ git svn clone -s http://server:port/repo/project

オプション-sは、--stdlayout。つまり、SVNリポジトリが、一般的な構造であるtrunk/, branches/, tags/を採用していることを示している。これにより、gitのmasterブランチにtrunk/が割り当てられ、リモートブランチにはtrunk/と、branches/とtags/直下のディレクトリが設定されるようになる。また、-T, -t, -bオプションを利用することで、トランク、タグ、ブランチのパスを指定することも可能。詳しくはman git-svn

コミット

ローカルリポジトリの更新内容をSVNリポジトリに反映。git pushに当たる。

$ git svn dcommit

アップデート

SVNリポジトリの更新内容をローカルリポジトリに取り込む。git rebaseに当たる。git pullしてもアップデートされないので注意。

$ git svn rebase

より安全に実行しようと思えば、fetchして更新内容を確認してからmergeなりなんなりしたほうがよいかも?

$ git svn fetch
$ git checkout trunk
... 更新内容確認 ...
... 問題無ければmerge ...
$ git checkout master
$ git merge trunk

無視ファイル設定取り込み

$ git svn show-ignore >> .git/info/exclude

または

$ git svn create-ignore
$ git commit
$ git svn dcommit

後者の場合、SVNリポジトリに.gitignoreが書き込まれるが、SVN利用者にいやがられなければそれでも。

タグとブランチ

タグもブランチも、リモートブランチとして扱われている。
SVNリポジトリ上でこうなっているものが…

repo/
  project/
    trunk/
    branches/
      v1_0
    tags/
      v1_0_1

gitからはこう見える(git svn clone -sの場合)。

$ git branch -r
  tags/v1_0_1
  trunk
  v1_0

トランクとブランチは同じ扱い、タグは「tags/タグ名」という名前に変更される。

タグ作成
$ git svn tag v1_0 -m "v1.0タグ。"

SVNリポジトリにタグを作成する。タグの作成先は、git svn clone時に指定したディレクトリ、例えば-sを使ったならtags/。

オプション-mを省略した場合、"Create tag v1_0"というコメントが自動的に設定される。

ブランチ作成
$ git svn branch v1_0 -m "v1.0ブランチ。"

SVNリポジトリにブランチを作成する。ブランチの作成先は、git svn clone時に指定したディレクトリ、例えば-sを使ったならbranches/。

オプション-mを省略した場合、"Create branch v1_0"というコメントが自動的に設定される。

タグまたはブランチをチェックアウト

初回のみ、こう(ローカルブランチを作成し、そのブランチにスイッチ)。

$ git checkout -b my_v1.0 v1_0

二回目以降は、こう(スイッチするだけ)。

$ git checkout my_v1.0

この操作の後でdcommitしたら、当然ながらSVNリポジトリのブランチ(この場合branches/v1_0)に変更が反映される。

ちなみに、複数のブランチへの更新を一気にdcommitすることはできない。

ブランチからトランクにマージ

最後にgit svn dcommitすることを除けば、普通にgitを使う場合と同じ。

$ git checkout my_v1.0
...編集...
$ git commit
$ git checkout master
$ git merge my_v1.0
$ git svn dcommit

基本的な作業手順

俺がやりたいことは、たぶん次のような手順で実施するんだろうなぁ。

trunkをチェックアウトして、そいつから自分の検証用のローカルブランチをつくって、好きにプログラミングする。

$ git svn clone -s http://server:port/repo/project
$ cd project
$ git checkout -b mywork
... なんやかんや編集およびcommitする ...

作ったプログラムが使い物にならないと判断して廃棄する場合、単にローカルブランチを削除する。

$ git checkout master
$ git branch -D mywork

気に入って、trunkに取り入れたい場合は、マージしてdcommitし、用済みになったローカルブランチを削除する。

$ git checkout master
$ git merge mywork
$ git svn dcommit
$ git branch -d mywork

これで、自分が好きに使えるブランチを自由に作成でき、気に入らなければなかったことにできるわけだ。

Gitは学習コストこそ高いが、慣れると柔軟性が高いうえに高速で使いやすいSCMである。自分一人で使う(俺が扱えればいい)ので、とりあえず学習コストは考慮しなくていいから、SVNの代わりに使うことに問題は無い。

SVNリポジトリに対してより細かい操作が必要になったら、素直にSVNクライアントを使えばいい。

SVN側のブランチを反映させたローカルブランチを作成した場合、そいつとgit側で自分が作ったローカルブランチとを簡単に見分ける方法が無いのが問題。ネーミングルールを作ればいいか。

認証

追記SVNとの認証は、普通にSVNを使う場合と同様の手順になる。つまり、SVNの認証キャッシュ(~/.subversion/auth/以下のファイル)があればそれを使うし、無ければパスワードを聞いてくる。

ただし、ここでちょっとした問題がある。MacSVNバージョン1.4以降は認証キャッシュを~/.subversion/auth/svn.simpleに書くのではなく、Keychainを使うようになっている。前者はパスワードが平文で書かれるため、セキュリティ的に危なかったのだが、それが改善されたわけだ。

しかし、git-svnを使用して認証すると、これが従来通りの平文キャッシュになってしまう。普通のSVNクライアントを使ってKeychain使用版のキャッシュを作っておいても、それを平文版キャッシュで上書きしてしまう。

まぁ、~/.subversion/auth/svn.simpleディレクトリのパーミッションに気をつけていれば(700にしておく)あまり問題はないと思うのだが、気持ち悪いなぁ。