git-svnでファイルシステムに項目がありませんといわれる

バージョン管理に Subversion を使っているプロジェクトを手伝うことになりました。
最近はすっかり Git に慣れてしまい、今更 svn コマンドを思い出してっていうのは億劫だったので、git-svn を使うことにしました。

しかしこれがなかなかくせ者で、ハマったのでメモしておきます。
とはいっても、結局原因究明に至る前にタイムアップして、解決策はバージョンアップというひどい内容なのですが。

dcommit しようとして出てきたエラーは次のような感じです。

$ 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 です。

エラーが出ている箇所を追ってみると、

/usr/libexec/git-core/git-svn
 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 行目から定義されています。

/usr/libexec/git-core/git-svn
4367 package SVN::Git::Editor;

その中の、apply_diff を見てみると、

/usr/libexec/git-core/git-svn
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 の中身を見てみると、

/usr/libexec/git-core/git-svn
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 を入れたらこの現象は起きなくなりました。
どの辺が修正されたのかなと思ってソースちらっと見てみましたけど、中身全然違いました。読みにくいのは相変わらずですけど。

*

ところで、どこでエラーメッセージを出しているのかというと、

/usr/libexec/git-core/git-svn
  50 push @SVN::Git::Editor::ISA, 'SVN::Delta::Editor';

SVN::Git::Editor の関数が呼ばれるときに、SVN::Delta::Editor の関数が参照されていて、じゃあ、その中を見てみると、AUTOLOAD というのがあります。

/usr/lib64/perl5/vendor_perl/SVN/Delta.pm
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 してる箇所が見付かりました。

コメントを残す