]>
Git — Sourcephile - git-remote-gpg.git/blob - git-remote-gpg
2 our $VERSION = '2014.01.28';
4 # This file is a git-remote-helpers(1) to use a gpg(1)
5 # as a cryptographic layer below git(1)'s objects.
6 # Copyright (C) 2014 Julien Moutinho
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published
10 # by the Free Software Foundation, either version 3 of the License,
11 # or any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty
15 # of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 # See the GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
22 use warnings FATAL
=> qw(all);
27 use File
::Spec
::Functions
qw(:ALL);
31 # NOTE: to debug: IPCRUNDEBUG=basic|data|details|gory
34 use POSIX
qw(WNOHANG);
41 foreach my $msg (@_) {
47 my $call = (caller(1))[3];
51 , "\e[30m\e[1m.", join('.', $call."\e[m")
55 : Data
::Dumper
::Dumper
($_)
62 my $call = (caller(1))[3];
65 , "\e[30m\e[1m.", join('.', $call."\e[m")
66 , " ", (ref $_ eq 'CODE'?(join("\n ", $_->()), "\n"):(@_, "\n"))
70 local $Carp::CarpLevel
= 1;
71 carp
("\e[33mWARNING\e[m ", @_, "\n\t");
74 local $Carp::CarpLevel
= 1;
75 croak
("\e[31mERROR\e[m ", @_, "\n\t");
80 debug
(sub{"file="},$file);
86 my ($ctx, $size) = @_;
88 IPC
::Run
::run
([@{$ctx->{config
}->{gpg
}}
89 , '--armor', '--gen-rand', '1', $size]
91 or error
("failed to get random bits");
96 my ($ctx, $algo, $run) = @_;
97 $run = sub {return @_} unless defined $run;
99 IPC
::Run
::run
($run->([@{$ctx->{config
}->{gpg
}}
100 , '--with-colons', '--print-md', $algo]
102 or error
("failed to hash data");
103 return ((split(':', $hash))[2]);
105 sub gpg_fingerprint
($$$) {
106 my ($ctx, $id, $caps_needed) = @_;
109 if (IPC
::Run
::run
([@{$ctx->{config
}->{gpg
}}
110 , '--fixed-list-mode', '--with-colons', '--with-fingerprint', '--list-keys', $id]
112 my @lines = split(/\n/,$output);
113 while (my $line = shift @lines) {
114 if (my ($longkeyid, $caps) = $line =~ m/^pub:[^:]*:[^:]*:[^:]*:([^:]*):[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:([^:]+):.*$/) {
116 foreach my $cap (@$caps_needed) {
117 if (not ($caps =~ m/$cap/)) {
118 warning
("skipping key 0x$longkeyid which has not usable capability: $cap, but matches: `$id'");
125 while ((not defined $fpr or not defined $uid)
126 and $line = shift @lines) {
127 (not defined $fpr and (($fpr) = $line =~ m/^fpr:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:([0-9A-F]+):.*$/)) or
128 (not defined $uid and (($uid) = $line =~ m/^uid:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:([^:]+):.*$/)) or
131 error
("unable to extract fingerprint and user ID")
139 error
("unable to find any OpenPGP key with usable capability: ".join('', @$caps_needed)." for: `$id'")
140 unless scalar(%h) gt 0;
141 debug
(sub{"$id -> "}, \
%h);
144 sub grg_encrypt_symmetric
($$$;$) {
145 my ($ctx, $clear, $key, $run) = @_;
146 $run = sub {return @_} unless defined $run;
147 IPC
::Run
::run
($run->([@{$ctx->{config
}->{gpg
}}
149 , '--compress-algo', 'none'
151 , '--passphrase-fd', '3'
153 , '--trust-model', 'always'
155 , '<', \
$clear, '3<', \
$key))
156 or error
("failed to encrypt symmetrically data");
158 sub grg_decrypt_symmetric
($$$;$) {
159 my ($ctx, $key, $run) = @_;
160 $run = sub {return @_} unless defined $run;
161 IPC
::Run
::run
($run->([@{$ctx->{config
}->{gpg
}}
162 , '--batch', '--no-default-keyring', '--keyring', '/dev/null', '--secret-keyring', '/dev/null'
163 , '--passphrase-fd', '3', '--quiet', '--decrypt']
165 or error
("failed to decrypt symmetrically data");
167 sub grg_encrypt_asymmetric
($$;$) {
168 my ($ctx, $clear, $run) = @_;
169 $run = sub {return @_} unless defined $run;
171 ( (map { ('--recipient', '0x'.$_) } (keys %{$ctx->{config
}->{keys}}))
172 , (map { ('--hidden-recipient', '0x'.$_) } (keys %{$ctx->{config
}->{'hidden-keys'}})) );
173 @recipients = ('--default-recipient-self')
175 IPC
::Run
::run
($run->([@{$ctx->{config
}->{gpg
}}
177 , '--compress-algo', 'none'
178 , '--trust-model', 'always'
179 , '--sign', '--encrypt'
180 , ($ctx->{config
}->{signingkey
}->{fpr
} ? ('--local-user', $ctx->{config
}->{signingkey
}->{fpr
}) : ())
183 or error
("failed to encrypt asymmetrically data");
185 sub grg_decrypt_asymmetric
($$;$) {
186 my ($ctx, $run) = @_;
187 my ($clear, $status);
188 $run = sub {return @_} unless defined $run;
189 IPC
::Run
::run
($run->([@{$ctx->{config
}->{gpg
}}
190 , '--batch', '--no-default-keyring',
191 , '--status-fd', '3', '--quiet', '--decrypt']
192 , '>', \
$clear, '3>', \
$status))
193 or error
("failed to decrypt asymmetrically data");
194 debug
(sub{"status=\n$status"});
195 my @lines = split(/\n/,$status);
196 my ($enc_to, $goodsig, $validsig, $validpub, $goodmdc);
197 foreach my $line (@lines) {
198 (not defined $enc_to and (($enc_to) = $line =~ m/^\[GNUPG:\] ENC_TO ([0-9A-F]+).*$/)) or
199 (not defined $goodsig and (($goodsig) = $line =~ m/^\[GNUPG:\] GOODSIG ([0-9A-F]+).*$/)) or
200 (not defined $goodmdc and (($goodmdc) = $line =~ m/^\[GNUPG:\] (GOODMDC)$/)) or
201 (not defined $validsig and not defined $validpub and (($validsig, $validpub)
202 = $line =~ m/^\[GNUPG:\] VALIDSIG ([0-9A-F]+) [^ ]+ [^ ]+ [^ ]+ [^ ]+ [^ ]+ [^ ]+ [^ ]+ [^ ]+ ([0-9A-F]+).*$/)) or
205 error
("data expected to be encrypted")
207 debug
(sub{"enc_to=$enc_to\n"});
208 error
("data expected to be signed")
210 debug
(sub{"goodsig=$goodsig\n"});
211 error
("modification detection code incorrect")
213 debug
(sub{"good_mdc=$goodmdc\n"});
214 error
("data signature invalid")
215 unless $validsig and $validpub;
216 debug
(sub{"validsig=$validsig\n"});
217 debug
(sub{"validpub=$validpub\n"});
218 error
("data signature refused")
219 unless exists $ctx->{config
}->{keys}->{$validpub}
220 or exists $ctx->{config
}->{'hidden-keys'}->{$validpub};
221 debug
(sub{"accepted:$validpub\n"});
225 sub grg_remote_fetch_file
($$$) {
226 my ($ctx, $files, $fetch_files) = @_;
227 # NOTE: avoid File::Copy::copy().
228 @$fetch_files = map { File
::Spec-
>catfile($ctx->{remote
}->{uri
}->file, $_) } @$files;
229 foreach my $file (@$fetch_files) {
230 -r
$file or return ();
234 sub grg_remote_fetch_rsync
($$$) {
235 my ($ctx, $files, $fetch_files) = @_;
236 my $uri = $ctx->{remote
}->{uri
}->clone;
238 $uri->fragment(undef);
240 $uri = $uri->as_string;
241 IPC
::Run
::run
([@{$ctx->{config
}->{rsync
}}
242 , '--verbose', '--ignore-times', '--inplace', '--progress'
243 , (map { File
::Spec-
>catfile($uri, $_) } @$files)
244 , $ctx->{'dir-cache'}.'/']
247 sub grg_remote_fetch_sftp
($$$) {
248 my ($ctx, $files, $fetch_files) = @_;
249 IPC
::Run
::run
([@{$ctx->{config
}->{curl
}}
251 , '--output', File
::Spec-
>catfile($ctx->{'dir-cache'}, '#1')
252 , File
::Spec-
>catfile($ctx->{remote
}->{uri
}, '{'.join(',',@$files).'}') ])
254 sub grg_remote_fetch
($$) {
255 my ($ctx, $files) = @_;
256 debug
(sub{'files='}, $files);
257 my $scheme = $ctx->{remote
}->{uri
}->scheme;
258 my $fetch_files = [map { File
::Spec-
>catfile($ctx->{'dir-cache'}, $_) } @$files];
260 { file
=> \
&grg_remote_fetch_file
261 , rsync
=> \
&grg_remote_fetch_rsync
262 , sftp
=> \
&grg_remote_fetch_sftp
264 error
("URL scheme not supported: `$scheme'")
266 $fct->($ctx, $files, $fetch_files)
268 return @$fetch_files;
270 sub grg_remote_init_file
($) {
272 my $dst = $ctx->{remote
}->{uri
}->file;
273 debug
(sub{"File::Path::make_path('$dst')\n"});
274 defined File
::Path
::make_path
($dst, {verbose
=> 1})
276 sub grg_remote_init_rsync
($) {
278 my $tmp = File
::Temp-
>tempdir(CLEANUP
=> 1);
279 my $path = $ctx->{remote
}->{uri
}->path;
280 my $uri = $ctx->{remote
}->{uri
}->clone;
281 $uri->fragment(undef);
284 File
::Path
::make_path
(File
::Spec-
>catdir($tmp, $path), {verbose
=> 0}) and
285 IPC
::Run
::run
([@{$ctx->{config
}->{rsync
}}
286 , '--verbose', '--recursive', '--relative'
288 , File
::Spec-
>catfile($uri->as_string)]
289 , init
=> sub { chdir $tmp or die $!; })
291 sub grg_remote_init_sftp
($) {
293 my $path = $ctx->{remote
}->{uri
}->path;
294 my $uri = $ctx->{remote
}->{uri
}->clone;
295 $uri->fragment(undef);
298 IPC
::Run
::run
([@{$ctx->{config
}->{curl
}}
299 , '--show-error', '--ftp-create-dirs'
300 , '-Q', "+mkdir ".$path
303 sub grg_remote_init
($) {
305 my $scheme = $ctx->{remote
}->{uri
}->scheme;
307 { file
=> \
&grg_remote_init_file
308 , rsync
=> \
&grg_remote_init_rsync
309 , sftp
=> \
&grg_remote_init_sftp
311 error
("URL scheme not supported: `$scheme'")
314 or error
("remote init failed");
317 sub grg_remote_push_file
($$) {
318 my ($ctx, $files) = @_;
319 foreach my $file (@$files) {
320 my $src = File
::Spec-
>catfile($ctx->{'dir-cache'}, $file);
321 my $dst = File
::Spec-
>catfile($ctx->{remote
}->{uri
}->file, $file);
322 debug
(sub{"File::Copy::move('$src', '$dst')\n"});
323 File
::Copy
::move
($src, $dst);
327 sub grg_remote_push_rsync
($$) {
328 my ($ctx, $files) = @_;
329 my $uri = $ctx->{remote
}->{uri
}->clone;
332 IPC
::Run
::run
([@{$ctx->{config
}->{rsync
}}
333 , '--verbose', '--relative'
337 sub grg_remote_push_sftp
($$) {
338 my ($ctx, $files) = @_;
339 my $uri = $ctx->{remote
}->{uri
}->clone;
342 IPC
::Run
::run
([@{$ctx->{config
}->{curl
}}
343 , '--show-error', '--ftp-create-dirs', '--upload-file'
344 , '{'.join(',', @$files).'}'
345 , $uri->as_string.'/'])
347 sub grg_remote_push
($) {
349 my $scheme = $ctx->{remote
}->{uri
}->scheme;
350 grg_remote_init
($ctx)
351 unless $ctx->{remote
}->{checked
};
353 if @{$ctx->{remote
}->{push}} == 0;
355 { file
=> \
&grg_remote_push_file
356 , rsync
=> \
&grg_remote_push_rsync
357 , sftp
=> \
&grg_remote_push_sftp
359 error
("URL scheme not supported: `$scheme'")
361 $fct->($ctx, $ctx->{remote
}->{push})
362 or error
("remote push failed");
365 sub grg_remote_remove
($) {
367 #my $scheme = $ctx->{remote}->{uri}->scheme;
370 # File::Copy::remove_tree
371 # ( map { File::Spec->catfile($ctx->{remote}->{uri}->path, $_) } @$files
375 # IPC::Run::run([@{$ctx->{config}->{rsync}}
376 # , '--verbose', '--ignore-times', '--recursive', '--delete'
378 # , $ctx->{remote}->{uri}])
381 # IPC::Run::run([@{$ctx->{config}->{curl}}
383 # , map { ('-Q', 'rm '.$_) } @$files
384 # , $ctx->{remote}->{uri}])
387 #error("URL scheme not supported: `$scheme'")
389 #$fct->($ctx, $ctx->{remote}->{remove})
390 # or error("remote remove failed");
394 sub grg_pack_fetch
($$) {
395 my ($ctx, $fetch_objects) = @_;
398 my %remote_objects = ();
399 while (my ($pack_id, $pack) = each %{$ctx->{manifest
}->{packs
}}) {
400 foreach my $obj (@{$pack->{objects
}}) {
401 $remote_objects{$obj} = $pack_id;
405 my %packs_to_fetch = ();
406 foreach my $obj (@$fetch_objects) {
407 my @packs = ($remote_objects{$obj});
408 while (my $pack_id = shift @packs) {
409 if (not exists $packs_to_fetch{$pack_id}) {
410 $packs_to_fetch{$pack_id} = 1;
411 my $manifest_pack = $ctx->{manifest
}->{packs
}->{$pack_id};
412 error
("manifest is missing a dependency pack: $pack_id")
413 unless defined $manifest_pack;
414 @packs = (@packs, @{$manifest_pack->{deps
}});
418 my @packs_to_fetch = keys %packs_to_fetch;
419 grg_remote_fetch
($ctx, [@packs_to_fetch]);
420 foreach my $pack_id (@packs_to_fetch) {
421 my $pack_file = File
::Spec-
>catfile($ctx->{'dir-cache'}, $pack_id);
422 my $manifest_pack = $ctx->{manifest
}->{packs
}->{$pack_id};
423 my $pack_key = $manifest_pack->{key
};
425 grg_decrypt_symmetric
($ctx, $pack_key, sub {
426 push @{$_[0]}, ($pack_file);
427 return (@_, '>', \
$pack_data);
429 my $pack_hash_algo = $manifest_pack->{hash_algo
};
430 my $pack_hash = grg_hash
($ctx
432 , sub { return (@_, '<', \
$pack_data); });
433 error
("pack data hash differs from pack manifest hash")
434 unless $pack_hash eq $manifest_pack->{hash
};
436 IPC
::Run
::run
(['git', 'index-pack', '-v', '--stdin']
441 sub grg_pack_push
($$) {
442 my ($ctx, $push_objects) = @_;
444 debug
(sub{"push_objects=\n"}, $push_objects);
446 my %remote_objects = ();
447 while (my ($pack_id, $pack) = each %{$ctx->{manifest
}->{packs
}}) {
448 foreach my $obj (@{$pack->{objects
}}) {
449 $remote_objects{$obj} = $pack_id;
453 IPC
::Run
::run
(['git', 'cat-file', '--batch-check']
454 , '<', \
join("\n", keys %remote_objects)
456 or error
("failed to query local git objects");
459 if ($_ =~ m/ missing$/) { () }
462 # @pack_objects, @pack_deps_objects
463 IPC
::Run
::run
(['git', 'rev-list', '--objects-edge', '--stdin', '--']
464 , '<', \
join("\n", ((map {'^'.$_} @common_objects), @$push_objects))
466 or error
("failed to query objects to pack");
467 my @pack_objects_edge = split(/\n/, $_);
468 foreach (@pack_objects_edge) {s/ .*//}
469 my @pack_objects = grep {m/^[^-]/} @pack_objects_edge;
470 my @pack_deps_objects = grep {s/^-//} @pack_objects_edge;
473 foreach my $obj (@pack_deps_objects) {
474 my $pack = $remote_objects{$obj};
475 error
("manifest is missing object dependencies")
476 unless defined $pack;
477 $pack_deps{$pack} = 1;
479 if (@pack_objects > 0) {
483 while (not defined $pack_id
484 or exists $ctx->{manifest
}->{packs
}->{$pack_id}) {
485 $pack_id = grg_rand
($ctx, $ctx->{config
}->{'pack-filename-size'});
486 $pack_id =~ s{/}{-}g;
487 error
("failed to pick an unused random pack filename after 512 tries; retry or increase grg.pack-filename-size")
488 if $pack_id_try++ >= 512;
490 my $pack_key = grg_rand
($ctx, $ctx->{config
}->{'pack-key-size'});
492 IPC
::Run
::run
(['git', 'pack-objects', '--stdout']
493 , '<', \
join("\n", @pack_objects)
495 or error
("failed to pack objects to push");
496 my $pack_hash = grg_hash
($ctx
497 , $ctx->{config
}->{'pack-hash-algo'}
498 , sub { return (@_, '<', \
$pack_data); });
499 grg_encrypt_symmetric
($ctx, $pack_data, $pack_key, sub {
500 push @{$_[0]}, ('--output', File
::Spec-
>catfile($ctx->{'dir-cache'}, $pack_id));
503 push @{$ctx->{remote
}->{push}}, $pack_id;
504 $ctx->{manifest
}->{packs
}->{$pack_id} =
505 { deps
=> [keys %pack_deps]
507 , hash_algo
=> $ctx->{config
}->{'pack-hash-algo'}
509 , objects
=> \
@pack_objects
514 sub grg_manifest_fetch
($) {
517 { 'hidden-keys' => {}
523 my ($crypt) = grg_remote_fetch
($ctx, [$ctx->{'manifest-file'}]);
524 if (defined $crypt) {
525 $ctx->{remote
}->{checked
} = 1;
527 grg_decrypt_asymmetric
($ctx, sub {
528 push @{$_[0]}, $crypt;
529 return (@_, '>', \
$json); });
530 # TODO: remove cached manifest?
532 ($manifest = JSON
::decode_json
($json) and ref $manifest eq 'HASH')
533 or error
("failed to decode JSON manifest");
534 $ctx->{manifest
} = {%{$ctx->{manifest
}}, %$manifest};
535 foreach my $slot (qw(keys hidden-keys)) {
536 while (my ($fpr, $uid) = each %{$ctx->{manifest
}->{$slot}}) {
537 my %keys = gpg_fingerprint
($ctx, '0x'.$fpr, ['E']);
538 my ($fpr, $uid) = each %keys;
539 $ctx->{config
}->{$slot}->{$fpr} = $uid;
544 debug
(sub{'ctx='}, $ctx);
545 if ($ctx->{command
} eq 'push') {
546 $ctx->{remote
}->{checked
} = 0;
548 elsif ($ctx->{remote
}->{checking
}) {
552 error
("remote checking failed");
556 sub grg_manifest_push
($) {
558 foreach my $slot (qw(keys hidden-keys)) {
559 $ctx->{manifest
}->{$slot} = {};
560 while (my ($fpr, $uid) = each %{$ctx->{config
}->{$slot}}) {
561 $ctx->{manifest
}->{$slot}->{$fpr} = $uid;
564 my $json = JSON
::encode_json
($ctx->{manifest
})
565 or error
("failed to encode JSON manifest");
566 grg_encrypt_asymmetric
($ctx, $json, sub {
568 , ('--output', File
::Spec-
>catfile($ctx->{'dir-cache'}, $ctx->{'manifest-file'}));
570 push @{$ctx->{remote
}->{push}}, $ctx->{'manifest-file'};
573 sub grg_config_read
($) {
575 my $cfg = $ctx->{config
};
578 foreach my $name (qw(gpg signingkey keys)
579 , grep { !m/^(gpg|signingkey|keys)$/ } (keys %$cfg)) {
581 IPC
::Run
::run
(['git', 'config', '--get', 'remote.'.$ctx->{remote
}->{name
}.'.'.$name, '.+'], '>', \
$value) or
582 IPC
::Run
::run
(['git', 'config', '--get', 'grg.'.$name, '.+'], '>', \
$value) or 1;
583 if ($name eq 'signingkey') {
584 IPC
::Run
::run
(['git', 'config', '--get', 'user.'.$name, '.+'], '>', \
$value)
587 my %keys = gpg_fingerprint
($ctx, $value, ['S']);
588 warning
("signing key ID is not matching a unique key: taking only one")
589 unless scalar(keys %keys) == 1;
590 my ($fpr, $uid) = each %keys;
591 $cfg->{$name} = {fpr
=> $fpr, uid
=> $uid};
593 elsif ($name eq 'keys' or $name eq 'hidden-keys') {
594 IPC
::Run
::run
(['git', 'config', '--get', 'user.'.$name, '.+'], '>', \
$value)
597 my @ids = split(/,/, $value);
599 foreach my $key (@ids) {
600 my %keys = gpg_fingerprint
($ctx, $key, ['E']);
601 while (my ($fpr, $uid) = each %keys) {
602 $cfg->{$name}->{$fpr} = $uid;
607 elsif (grep(/^$name$/, qw(curl gpg rsync))) {
608 IPC
::Run
::run
(['git', 'config', '--get', $name.'.program', '.+'], '>', \
$value)
610 $cfg->{$name} = [split(' ', $value)]
615 $cfg->{$name} = $value
619 error
("no signingkey configured; to do so you may use one of following commands:\n"
620 , "\t\$ git config remote.'$ctx->{remote}->{name}'.signingkey \$your_openpgp_id\n"
621 , "\t\$ git config grg.signingkey \$your_openpgp_id\n"
622 , "\t\$ git config user.signingkey \$your_openpgp_id"
623 ) unless defined $cfg->{signingkey
};
624 if ( (scalar (keys %{$cfg->{keys}}) == 0)
625 and (scalar (keys %{$cfg->{'hidden-keys'}}) == 0) ) {
626 $cfg->{keys} = { $cfg->{signingkey
}->{fpr
} => $cfg->{signingkey
}->{uid
} };
629 debug
(sub{'config='},$cfg);
632 sub grg_connect
($) {
634 grg_config_read
($ctx);
635 grg_manifest_fetch
($ctx);
637 sub grg_disconnect
($) {
639 grg_remote_push
($ctx);
642 sub gpg_command_answer
($) {
644 debug
(sub{join('', @cmd)."\n"});
645 print STDOUT
(@cmd, "\n");
647 sub grg_command_capabilities
($) {
649 $ctx->{command
} = 'capabilities';
650 gpg_command_answer
("fetch");
651 gpg_command_answer
("push");
652 gpg_command_answer
("");
655 sub grg_command_fetch
($$) {
656 my ($ctx, $fetch_refs) = @_;
657 $ctx->{command
} = 'fetch';
658 debug
(sub{"fetch_refs="}, $fetch_refs);
661 my @fetch_objects= ();
662 foreach my $ref (@$fetch_refs) {
663 push @fetch_objects, $ref->{sha1
};
665 grg_pack_fetch
($ctx, \
@fetch_objects);
667 sub grg_command_list
($) {
669 $ctx->{command
} = 'list';
671 while (my ($ref, $obj) = each %{$ctx->{manifest
}->{refs
}}) {
672 gpg_command_answer
("$obj $ref");
674 gpg_command_answer
("");
676 sub grg_command_push
($$) {
677 my ($ctx, $push_refs) = @_;
679 $ctx->{command
} = 'push';
680 debug
(sub{"push_refs="}, $push_refs);
683 my @push_objects= ();
684 foreach my $ref (@$push_refs) {
685 IPC
::Run
::run
(['git', 'rev-list', '--ignore-missing', '--max-count=1', $ref->{src
}, '--']
687 or error
("failed to dereference ref to push: ".$ref->{src
});
689 $ref->{src_obj
} = $_;
690 push @push_objects, $_;
692 grg_pack_push
($ctx, \
@push_objects);
693 my $manifest_refs = $ctx->{manifest
}->{refs
};
694 foreach my $ref (@$push_refs) {
695 $manifest_refs->{$ref->{dst
}} = $ref->{src_obj
};
697 $manifest_refs->{HEAD
}
698 = $push_refs->[-1]->{src_obj
}
699 unless exists $manifest_refs->{HEAD
}
701 grg_manifest_push
($ctx);
702 grg_disconnect
($ctx);
704 sub grg_commands
(@) {
708 #STDOUT->autoflush(1);
709 while (defined $line or (not eof(*STDIN
) and
710 (defined($line = readline(*STDIN
)))
712 : error
("readline failed: $!")
714 debug
(sub{"line=\"",$line,"\"\n"});
715 $ctx->{command
} = undef;
716 if ($line eq 'capabilities') {
717 grg_command_capabilities
($ctx);
720 elsif ($line =~ m/^fetch .*$/) {
723 while ((defined $line or (not eof(*STDIN
) and
724 ((defined($line = readline(*STDIN
)))
726 : error
("readline failed: $!")))) and
727 (($sha1, $name) = ($line =~ m/^fetch ([0-9a-f]{40}) (.+)$/))
729 debug
(sub{"fetch line=\"",$line,"\"\n"});
730 push @refs, {sha1
=>$sha1, name
=>$name};
733 error
("failed to parse command: $line")
735 grg_command_fetch
($ctx, \
@refs);
737 elsif ($line eq 'list' or $line eq 'list for-push') {
738 grg_command_list
($ctx);
741 elsif ($line =~ m/^push .*$/) {
743 my ($force, $src, $dst);
744 while ((defined $line or (not eof(*STDIN
) and
745 ((defined($line = readline(*STDIN
)))
747 : error
("readline failed: $!")))) and
748 (($force, $src, $dst) = ($line =~ m/^push (\+)?([^:]+):(.+)$/))
750 debug
(sub{"push line=\"",$line,"\"\n"});
751 push @refs, {force
=>(defined $force), src
=>$src, dst
=>$dst};
754 error
("failed to parse command: $line")
756 grg_command_push
($ctx, \
@refs);
758 elsif ($line =~ m/^$/) {
760 gpg_command_answer
("");
764 warning
("unsupported command supplied: `$line'");
770 $ENV{GIT_DIR
} = $ENV{GIT_DIR
} || '.git';
771 $ENV{GITCEPTION
} = ($ENV{GITCEPTION
} || '') . '+';
778 , 'hidden-keys' => {}
779 , 'manifest-hash-algo' => 'SHA224' # NOTE: SHA512, SHA384, SHA256, SHA224 supported.
780 , 'pack-filename-size' => 42
781 , 'pack-hash-algo' => 'SHA224' # NOTE: SHA512, SHA384, SHA256, SHA224 supported.
782 , 'pack-key-size' => 64
783 , signingkey
=> undef
786 , 'dir-cache' => undef
788 , 'manifest-file' => undef
797 Getopt
::Long
::Configure
802 Getopt
::Long
::GetOptions
803 ( help
=> sub { Pod
::Usage
::pod2usage
805 , -sections
=> ['SYNOPSIS', 'OPTIONS', 'REMOTES', 'CONFIG']
806 , -verbose
=> 99 ); }
807 , man
=> sub { Pod
::Usage
::pod2usage
(-verbose
=> 2); }
809 $ctx->{remote
}->{checking
} = 1;
812 if (not $ctx->{remote
}->{checking
}) {
813 my $name = shift @ARGV;
814 Pod
::Usage
::pod2usage
(-verbose
=> 1)
815 unless defined $name;
816 ($ctx->{remote
}->{name
}) = ($name =~ m/^((\w|-)+)$/);
817 error
("valid name of remote Git required, got: `$name'")
818 unless $ctx->{remote
}->{name
};
820 my $uri = shift @ARGV;
821 Pod
::Usage
::pod2usage
(-verbose
=> 1)
823 $ctx->{remote
}->{uri
} = URI-
>new($uri);
824 error
("valid URL of remote Git required, got: `$uri'")
825 unless $ctx->{remote
}->{uri
};
826 my $fragment = $ctx->{remote
}->{uri
}->fragment;
828 unless defined $fragment;
829 $ctx->{'manifest-file'} = grg_hash
($ctx
830 , $ctx->{config
}->{'manifest-hash-algo'}
831 , sub { return (@_, '<', \
$fragment); });
832 if (-d
$ENV{GIT_DIR
}) {
833 $ctx->{'dir-cache'} = File
::Spec-
>catdir
834 ( $ENV{GIT_DIR
}, 'cache', 'remotes'
835 , $ctx->{remote
}->{name
}, 'gpg');
836 File
::Path
::make_path
($ctx->{'dir-cache'}, {verbose
=> 1});
839 $ctx->{'dir-cache'} = File
::Temp-
>tempdir(CLEANUP
=> 1);
841 debug
(sub{"ctx="},$ctx);
852 git-remote-gpg - git-remote-helpers(1) to encrypt remote repository through gpg(1)
856 =item git-remote-gpg $gpg_remote $gpg_url
858 =item git-remote-gpg --check $gpg_url
864 =item B<-h>, B<--help>
874 =item git remote add $remote gpg::rsync://${user:+$user@}$host/$path
878 =item git remote add $remote gpg::sftp://${user:+$user@}$host/$path
880 =head2 Via File::Copy(3pm)
882 =item git remote add $remote gpg::file://$path
896 =item B<grg.hidden-keys>
898 =item B<grg.manifest-hash-algo>
900 =item B<grg.pack-filename-size>
902 =item B<grg.pack-hash-algo>
904 =item B<grg.pack-key-size>
906 =item B<grg.signingkey>