バージョン管理に Subversion を使っているプロジェクトを手伝うことになりました。
最近はすっかり Git に慣れてしまい、今更 svn コマンドを思い出してっていうのは億劫だったので、git-svn を使うことにしました。
しかしこれがなかなかくせ者で、ハマったのでメモしておきます。
とはいっても、結局原因究明に至る前にタイムアップして、解決策はバージョンアップというひどい内容なのですが。
dcommit しようとして出てきたエラーは次のような感じです。
Committing to https://192.168.0.10/repos/site/001.Hoge/001.Fuga & Piyo System/001.MyProject/trunk ...
ファイルシステムに項目がありません: パス '/repos/site/!svn/bc/471/001.Hoge/001.Fuga' が見つかりません at /usr/libexec/git-core/git-svn line 573
ぱっと見、パスに含まれるスペースが悪いような気がするのですが……
Git のバージョンは、1.7.1 です。
エラーが出ている箇所を追ってみると、
559 my %ed_opts = ( r => $last_rev, 560 log => get_commit_entry($d)->{log}, 561 ra => Git::SVN::Ra->new($url), 562 config => SVN::Core::config_get_config( 563 $Git::SVN::Ra::config_dir 564 ), 565 tree_a => "$d~1", 566 tree_b => $d, 567 editor_cb => sub { 568 print "Committed r$_[0]\n"; 569 $cmt_rev = $_[0]; 570 }, 571 svn_path => ''); 572 if (!SVN::Git::Editor->new(\%ed_opts)->apply_diff) { 573 print "No changes\n$d~1 == $d\n"; 574 } elsif ($parents->{$d} && @{$parents->{$d}}) { 575 $gs->{inject_parents_dcommit}->{$cmt_rev} = 576 $parents->{$d}; 577 }
SVN::Git::Editor の &apply_diff を呼び出してるところでエラーが出てるみたい。
SVN::Git::Editor というのは、git-svn の 4367 行目から定義されています。
4367 package SVN::Git::Editor;
その中の、apply_diff を見てみると、
4787 # this drives the editor 4788 sub apply_diff { 4789 my ($self) = @_; 4790 my $mods = $self->{mods}; 4791 my %o = ( D => 1, R => 0, C => -1, A => 3, M => 3, T => 3 ); 4792 foreach my $m (sort { $o{$a->{chg}} <=> $o{$b->{chg}} } @$mods) { 4793 my $f = $m->{chg}; 4794 if (defined $o{$f}) { 4795 $self->$f($m); 4796 } else { 4797 fatal("Invalid change type: $f"); 4798 } 4799 } 4800 $self->rmdirs if $_rmdir; 4801 if (@$mods == 0) { 4802 $self->abort_edit; 4803 } else { 4804 $self->close_edit; 4805 } 4806 return scalar @$mods; 4807 }
$mods の中の chg の中身が指す関数を呼んでるみたい。
ここで、warn を仕込んで、chg が何の時にエラーが出たのか見てみると、'R' でした。
ということで、R の中身を見てみると、
4673 sub R { 4674 my ($self, $m) = @_; 4675 my ($dir, $file) = split_path($m->{file_b}); 4676 my $pbat = $self->ensure_path($dir); 4677 my $fbat = $self->add_file($self->repo_path($m->{file_b}), $pbat, 4678 $self->url_path($m->{file_a}), $self->{r}); 4679 print "\tR\t$m->{file_a} => $m->{file_b}\n" unless $::_q; 4680 $self->apply_autoprops($file, $fbat); 4681 $self->chg_file($fbat, $m); 4682 $self->close_file($fbat,undef,$self->{pool}); 4683 4684 ($dir, $file) = split_path($m->{file_a}); 4685 $pbat = $self->ensure_path($dir); 4686 $self->delete_entry($m->{file_a}, $pbat); 4687 }
なんか、file とか path とか出てきてるので、この辺の処理で半角スペースを間違って処理しちゃったのかな?
しかし読みにくいコードだ……。
これを書いてる人が頭で理解できてるというのがすごい。
今回エラーの起きた Subversion のサーバーは共用しているサーバーで、そこで何度も試すわけにも行かないし、仕事も進めないといけなかったので、今回は追うのはここまでにして、Git のバージョンを上げちゃいました。
CentOS 6.4 に入ってた Git が v1.7.1 で、新規に $HOME 以下に v1.9.0 を入れたらこの現象は起きなくなりました。
どの辺が修正されたのかなと思ってソースちらっと見てみましたけど、中身全然違いました。読みにくいのは相変わらずですけど。
*
ところで、どこでエラーメッセージを出しているのかというと、
50 push @SVN::Git::Editor::ISA, 'SVN::Delta::Editor';
SVN::Git::Editor の関数が呼ばれるときに、SVN::Delta::Editor の関数が参照されていて、じゃあ、その中を見てみると、AUTOLOAD というのがあります。
151 our $AUTOLOAD; 152 153 sub AUTOLOAD { 154 no warnings 'uninitialized'; 155 return unless $_[0]->{_editor}; 156 my $class = ref($_[0]); 157 my $func = $AUTOLOAD; 158 $func =~ s/.*:://; 159 warn "$func: ".join(',',@_)."\n" if $_[0]->{_debug}; 160 return unless $func =~ m/[^A-Z]/; 161 162 my %ebaton = ( set_target_revision => 1, 163 open_root => 1, 164 close_edit => 1, 165 abort_edit => 1, 166 ); 167 168 my $self = shift; 169 no strict 'refs'; 170 171 my @ret = UNIVERSAL::isa ($self->{_editor}, __PACKAGE__) ? 172 $self->{_editor}->$func (@_) : 173 eval { &{"invoke_$func"}($self->{_editor}, 174 $ebaton{$func} ? $self->{_baton} : (), @_) }; 175 176 die $@ if $@; 177 178 return @ret ? $#ret == 0 ? $ret[0] : [@ret] : undef; 179 }
このの中に eval して $@ 取って die してる箇所が見付かりました。