fix remote sftp://
[git-remote-gpg.git] / git-remote-gpg
index 8e4e4e1902d2778a6eebf231d9b641dea684d90b..8cdc0cc04bdb1d0e3bd0de1b111f7526776fed7c 100755 (executable)
@@ -1,5 +1,5 @@
 #!/usr/bin/perl
-our $VERSION = '2014.01.28';
+our $VERSION = '2014.04.29';
 # License
        # This file is a git-remote-helpers(1) to use a gpg(1)
        # as a cryptographic layer below git(1)'s objects.
@@ -76,24 +76,23 @@ our $VERSION = '2014.01.28';
                croak("\e[31mERROR\e[m ", @_, "\n\t");
         }
 # System utilities
-       sub rm ($) {
-               my ($file) = @_;
-               debug(sub{"file="},$file);
-               unlink($file)
-                       or error("rm $file");
-        }
-       sub mkdir ($) {
-               my ($dir) = @_;
-               debug(sub{"dir=\"$dir\"\n"});
-               File::Path::make_path($dir, {verbose=>0, error => \my $error});
-               if (@$error) {
-                       for my $diag (@$error) {
-                               my ($dir, $message) = %$diag;
-                               if ($dir eq '') {
-                                       print "general error: $message\n";
-                                }
-                               else {
-                                       print "problem mkdir $dir: $message\n";
+       sub rm (@) {
+               foreach my $file (@_) {
+                       debug(sub{"file=$file\n"});
+                       if (-e $file) {
+                               unlink($file)
+                                       or error("rm $file");
+                        }
+                }
+        }
+       sub mkdir (@) {
+               foreach my $dir (@_) {
+                       debug(sub{"dir=$dir\n"});
+                       File::Path::make_path($dir, {verbose=>0, error => \my $error});
+                       if (@$error) {
+                               for my $diag (@$error) {
+                                       my ($dir, $message) = %$diag;
+                                       error("dir=$dir: $message");
                                 }
                         }
                 }
@@ -174,8 +173,6 @@ our $VERSION = '2014.01.28';
         }
        sub grg_decrypt_symmetric ($$$;$) {
                my ($ctx, $key, $run) = @_;
-               debug(sub{'ctx='}, $ctx);
-               debug(sub{'key='}, $key);
                $run = sub {return @_} unless defined $run;
                IPC::Run::run($run->([@{$ctx->{config}->{gpg}}
                 , '--batch', '--no-default-keyring', '--keyring', '/dev/null', '--secret-keyring', '/dev/null'
@@ -241,14 +238,13 @@ our $VERSION = '2014.01.28';
                return $clear;
         }
 # grg remote I/O
-       sub grg_remote_fetch_file ($$$) {
-               my ($ctx, $files, $fetch_files) = @_;
+       sub grg_remote_fetch_file ($) {
+               my ($ctx) = @_;
                # NOTE: avoid File::Copy::copy().
-               while (my ($file, undef) = each %$fetch_files) {
+               while (my ($file, undef) = each %{$ctx->{remote}->{fetch}}) {
                        my $path = File::Spec->catfile($ctx->{remote}->{uri}->file, $file);
-                       debug(sub{'test path='}, $path);
                        if (-r $path) {
-                               my $h = $fetch_files->{$file};
+                               my $h = $ctx->{remote}->{fetch}->{$file};
                                $h->{path}     = $path;
                                $h->{preserve} = 1;
                         }
@@ -256,31 +252,45 @@ our $VERSION = '2014.01.28';
                 }
                return 1;
         }
-       sub grg_remote_fetch_rsync ($$$) {
-               my ($ctx, $files, $fetch_files) = @_;
+       sub grg_remote_fetch_rsync ($) {
+               my ($ctx) = @_;
                my $uri  = $ctx->{remote}->{uri}->clone;
-               $uri->scheme(undef);
-               $uri->fragment(undef);
-               $uri->query(undef);
-               $uri = $uri->as_string;
+               my @src;
+               if ($uri->opaque =~ m{^//}) {
+                       $uri->fragment(undef);
+                       $uri->query(undef);
+                       @src = map { $uri->path($_); $uri->as_string; }
+                        (keys %{$ctx->{remote}->{fetch}});
+                }
+               else {
+                       my ($authority, $path, $fragment)
+                        = $uri->as_string =~ m|^rsync:(?:([^/#:]+):)?([^?#]*)(?:#(.*))?$|;
+                       @src = map { "$authority:$path/$_" }
+                        (keys %{$ctx->{remote}->{fetch}});
+                }
                IPC::Run::run([@{$ctx->{config}->{rsync}}
-                , '--verbose', '--ignore-times', '--inplace', '--progress'
-                , (map { File::Spec->catfile($uri, $_) } @$files)
+                , '-i', '--ignore-times', '--inplace', '--progress'
+                , @src
                 , $ctx->{'dir-cache'}.'/']
                 , '>&2')
         }
-       sub grg_remote_fetch_sftp ($$$) {
-               my ($ctx, $files, $fetch_files) = @_;
+       sub grg_remote_fetch_sftp ($) {
+               my ($ctx) = @_;
                IPC::Run::run([@{$ctx->{config}->{curl}}
                 , '--show-error'
                 , '--output', File::Spec->catfile($ctx->{'dir-cache'}, '#1')
-                , File::Spec->catfile($ctx->{remote}->{uri}, '{'.join(',',@$files).'}') ])
+                , $ctx->{remote}->{uri}->as_string.'/'.'{'.join(',', (keys %{$ctx->{remote}->{fetch}})).'}' ]
+                , '>&2')
         }
        sub grg_remote_fetch ($$) {
                my ($ctx, $files) = @_;
                debug(sub{'files='}, $files);
                my $scheme = $ctx->{remote}->{uri}->scheme;
-               my $fetch_files = {map { $_ => { path => File::Spec->catfile($ctx->{'dir-cache'}, $_), preserve => 0 } } @$files};
+               $ctx->{remote}->{fetch}
+                = {map { $_ =>
+                        { path => File::Spec->catfile($ctx->{'dir-cache'}, $_)
+                        , preserve => 0 }
+                } @$files};
                my $fct =
                 { file  => \&grg_remote_fetch_file
                 , rsync => \&grg_remote_fetch_rsync
@@ -288,11 +298,9 @@ our $VERSION = '2014.01.28';
                 }->{$scheme};
                error("URL scheme not supported: `$scheme'")
                        unless $fct;
-               debug(sub{'fetch_files='}, $fetch_files);
-               $fct->($ctx, $files, $fetch_files)
-                       or $fetch_files = {};
-               debug(sub{'fetch_files='}, $fetch_files);
-               return $fetch_files;
+               $fct->($ctx)
+                       or $ctx->{remote}->{fetch} = {};
+               return $ctx->{remote}->{fetch};
         }
        sub grg_remote_init_file ($) {
                my ($ctx) = @_;
@@ -302,30 +310,41 @@ our $VERSION = '2014.01.28';
         }
        sub grg_remote_init_rsync ($) {
                my ($ctx) = @_;
-               my $tmp = File::Temp->tempdir(CLEANUP => 1);
-               my $path = $ctx->{remote}->{uri}->path;
-               my $uri  = $ctx->{remote}->{uri}->clone;
-               $uri->fragment(undef);
-               $uri->path(undef);
-               $uri->query(undef);
+               my $tmp = File::Temp->tempdir('grg_rsync_XXXXXXXX', CLEANUP => 1);
+               my $uri = $ctx->{remote}->{uri}->clone;
+               my ($path, $dst);
+               if ($uri->opaque =~ m{^//}) {
+                       $uri->fragment(undef);
+                       $uri->query(undef);
+                       $path = $uri->path;
+                       $dst = $uri->as_string;
+                }
+               else {
+                       my ($authority, $fragment);
+                       ($authority, $path, $fragment)
+                        = $uri->as_string =~ m|^rsync:(?:([^/#:]+):)?([^?#]*)(?:#(.*))?$|;
+                       $dst = "$authority:";
+                }
                &mkdir(File::Spec->catdir($tmp, $path));
                IPC::Run::run([@{$ctx->{config}->{rsync}}
-                , '--verbose', '--recursive', '--relative'
+                , '-i', '--recursive', '--relative'
                 , '--exclude=*', '.'
-                , File::Spec->catfile($uri->as_string)]
+                , $dst]
+                , '>&2'
                 , init => sub { chdir $tmp or die $!; })
         }
        sub grg_remote_init_sftp ($) {
                my ($ctx) = @_;
-               my $path = $ctx->{remote}->{uri}->path;
                my $uri  = $ctx->{remote}->{uri}->clone;
+               my ($path) = $uri->path =~ m|^/?(.*)$|;
                $uri->fragment(undef);
                $uri->path(undef);
                $uri->query(undef);
                IPC::Run::run([@{$ctx->{config}->{curl}}
                 , '--show-error', '--ftp-create-dirs'
                 , '-Q', "+mkdir ".$path
-                , $uri->as_string])
+                , $uri->as_string]
+                , '>&2')
         }
        sub grg_remote_init ($) {
                my ($ctx) = @_;
@@ -341,10 +360,10 @@ our $VERSION = '2014.01.28';
                        or error("remote init failed");
                return;
         }
-       sub grg_remote_push_file ($$) {
-               my ($ctx, $files) = @_;
+       sub grg_remote_push_file ($) {
+               my ($ctx) = @_;
                my $ok = 1;
-               foreach my $file (@$files) {
+               foreach my $file (@{$ctx->{remote}->{push}}) {
                        my $src = File::Spec->catfile($ctx->{'dir-cache'}, $file);
                        my $dst = File::Spec->catfile($ctx->{remote}->{uri}->file, $file);
                        debug(sub{"File::Copy::move('$src', '$dst')\n"});
@@ -355,25 +374,39 @@ our $VERSION = '2014.01.28';
                 }
                return $ok;
         }
-       sub grg_remote_push_rsync ($$) {
-               my ($ctx, $files) = @_;
-               my $uri  = $ctx->{remote}->{uri}->clone;
-               $uri->fragment('');
-               $uri->query('');
+       sub grg_remote_push_rsync ($) {
+               my ($ctx) = @_;
+               my $uri = $ctx->{remote}->{uri}->clone;
+               $uri->fragment(undef);
+               $uri->query(undef);
+               my ($path, $dst);
+               if ($uri->opaque =~ m{^//}) {
+                       $uri->fragment(undef);
+                       $uri->query(undef);
+                       $dst = $uri->as_string;
+                }
+               else {
+                       my ($authority, $path, $fragment)
+                        = $uri->as_string =~ m|^rsync:(?:([^/#:]+):)?([^?#]*)(?:#(.*))?$|;
+                       $dst = "$authority:$path/";
+                }
                IPC::Run::run([@{$ctx->{config}->{rsync}}
-                , '--verbose', '--relative'
-                , @$files
-                , $uri->as_string])
+                , '-i', '--relative'
+                , (@{$ctx->{remote}->{push}})
+                , $dst]
+                , '>&2'
+                , init => sub { chdir $ctx->{'dir-cache'} or die $!; });
         }
-       sub grg_remote_push_sftp ($$) {
-               my ($ctx, $files) = @_;
-               my $uri  = $ctx->{remote}->{uri}->clone;
-               $uri->fragment('');
-               $uri->query('');
+       sub grg_remote_push_sftp ($) {
+               my ($ctx) = @_;
+               my $uri = $ctx->{remote}->{uri}->clone;
+               $uri->fragment(undef);
+               $uri->query(undef);
                IPC::Run::run([@{$ctx->{config}->{curl}}
                 , '--show-error', '--ftp-create-dirs', '--upload-file'
-                , '{'.join(',', @$files).'}'
-                , $uri->as_string.'/'])
+                , File::Spec->catfile($ctx->{'dir-cache'},'{'.join(',', @{$ctx->{remote}->{push}}).'}')
+                , $uri->as_string.'/']
+                , '>&2')
         }
        sub grg_remote_push ($) {
                my ($ctx) = @_;
@@ -389,8 +422,9 @@ our $VERSION = '2014.01.28';
                 }->{$scheme};
                error("URL scheme not supported: `$scheme'")
                        unless $fct;
-               $fct->($ctx, $ctx->{remote}->{push})
+               $fct->($ctx)
                        or error("remote push failed");
+               rm(map {File::Spec->catfile($ctx->{'dir-cache'}, $_)} @{$ctx->{remote}->{push}});
                return 1;
         }
        sub grg_remote_remove ($) {
@@ -466,7 +500,7 @@ our $VERSION = '2014.01.28';
                         , sub { return (@_, '<', \$pack_data); });
                        error("pack data hash differs from pack manifest hash")
                                unless $pack_hash eq $manifest_pack->{hash};
-                       rm($pack_fetched)
+                       rm($pack_fetched->{path})
                                unless $pack_fetched->{preserve};
                        IPC::Run::run(['git', 'index-pack', '-v', '--stdin']
                         , '<', \$pack_data
@@ -553,7 +587,7 @@ our $VERSION = '2014.01.28';
                 , keys => {}
                 , packs => {}
                 , refs => {}
-                , version => undef
+                , version => $VERSION
                 };
                my $fetched = grg_remote_fetch($ctx, [$ctx->{'manifest-file'}]);
                my $crypt = $fetched->{$ctx->{'manifest-file'}}->{path};
@@ -578,7 +612,6 @@ our $VERSION = '2014.01.28';
                         }
                 }
                else {
-                       debug(sub{'ctx='}, $ctx);
                        if ($ctx->{command} eq 'push' or $ctx->{command} eq 'list for-push') {
                                $ctx->{remote}->{checked} = 0;
                         }
@@ -705,8 +738,13 @@ our $VERSION = '2014.01.28';
                my ($ctx, $command) = @_;
                $ctx->{command} = $command;
                grg_connect($ctx);
-               while (my ($ref, $obj) = each %{$ctx->{manifest}->{refs}}) {
-                       gpg_command_answer("$obj $ref");
+               my $manifest_refs = $ctx->{manifest}->{refs};
+               while (my ($ref, $obj) = each %$manifest_refs) {
+                       if ($obj =~ m|^ref: *(.*) *$|) {
+                               $obj = $manifest_refs->{$1};
+                        }
+                       gpg_command_answer("$obj $ref")
+                               if defined $obj;
                 };
                gpg_command_answer("");
         }
@@ -731,10 +769,8 @@ our $VERSION = '2014.01.28';
                foreach my $ref (@$push_refs) {
                        $manifest_refs->{$ref->{dst}} = $ref->{src_obj};
                 }
-               $manifest_refs->{HEAD}
-                = $push_refs->[-1]->{src_obj}
-                       unless exists $manifest_refs->{HEAD}
-                       or @$push_refs == 0;
+               $manifest_refs->{HEAD} = 'ref: refs/heads/master'
+                unless defined $manifest_refs->{HEAD};
                grg_manifest_push($ctx);
                grg_disconnect($ctx);
         }
@@ -876,7 +912,7 @@ sub main {
                &mkdir($ctx->{'dir-cache'});
         }
        else {
-               $ctx->{'dir-cache'} = File::Temp->tempdir(CLEANUP => 1);
+               $ctx->{'dir-cache'} = File::Temp->tempdir('grg_cache_XXXXXXXX', CLEANUP => 1);
         }
        debug(sub{"ctx="},$ctx);
        grg_commands($ctx);
@@ -911,11 +947,13 @@ git-remote-gpg - git-remote-helpers(1) to encrypt remote repository through gpg(
 
 =head2 Via rsync(1)
 
-=item git remote add $remote gpg::rsync://${user:+$user@}$host/$path
+=item git remote add $remote gpg::rsync:${user:+$user@}$host:$path
+
+=item git remote add $remote gpg::rsync://${user:+$user@}$host${port:+:$port}/$path
 
 =head2 Via curl(1)
 
-=item git remote add $remote gpg::sftp://${user:+$user@}$host/$path
+=item git remote add $remote gpg::sftp://${user:+$user@}$host${port:+:$port}/$path
 
 =head2 Via File::Copy(3pm)
 
@@ -927,25 +965,25 @@ git-remote-gpg - git-remote-helpers(1) to encrypt remote repository through gpg(
 
 =over 8
 
-=item B<grg.curl>
+=item B<grg.curl>, B<remote.$remote.curl>
 
-=item B<grg.gpg>
+=item B<grg.gpg>, B<remote.$remote.gpg>
 
-=item B<grg.keys>
+=item B<grg.keys>, B<remote.$remote.keys>
 
-=item B<grg.hidden-keys>
+=item B<grg.hidden-keys>, B<remote.$remote.hidden-keys>
 
-=item B<grg.manifest-hash-algo>
+=item B<grg.manifest-hash-algo>, B<remote.$remote.manifest-hash-algo>
 
-=item B<grg.pack-filename-size>
+=item B<grg.pack-filename-size>, B<remote.$remote.pack-filename-size>
 
-=item B<grg.pack-hash-algo>
+=item B<grg.pack-hash-algo>, B<remote.$remote.pack-hash-algo>
 
-=item B<grg.pack-key-size>
+=item B<grg.pack-key-size>, B<remote.$remote.pack-key-size>
 
-=item B<grg.signingkey>
+=item B<grg.signingkey>, B<remote.$remote.signingkey>
 
-=item B<grg.rsync>
+=item B<grg.rsync>, B<remote.$remote.rsync>
 
 =back