use URI;
use PublicInbox::Config;
use PublicInbox::Inbox;
+use PublicInbox::Git;
use PublicInbox::LeiCurl;
use PublicInbox::OnDestroy;
-use Digest::SHA qw(sha256_hex sha1_hex);
+use PublicInbox::SHA qw(sha256_hex sha1_hex);
use POSIX qw(strftime);
our $LIVE; # pid => callback
$_[0]->{lei}->{opt}->{'keep-going'});
}
-sub _wq_done_wait { # dwaitpid callback (via wq_eof)
- my ($arg, $pid) = @_;
- my ($mrr, $lei) = @$arg;
+sub _wq_done_wait { # awaitpid cb (via wq_eof)
+ my ($pid, $mrr, $lei) = @_;
if ($?) {
$lei->child_error($?);
} elsif (!$lei->{child_error}) {
my ($k) = ($sw =~ /\A([\w-]+)/);
$opt->{$k} = $lei->{opt}->{$k};
}
- # force synchronous dwaitpid for v2:
+ # force synchronous awaitpid for v2:
local $PublicInbox::DS::in_loop = 0;
my $cfg = PublicInbox::Config->new(undef, $lei->{2});
my $env = PublicInbox::Admin::index_prepare($opt, $cfg);
sub start_cmd {
my ($self, $cmd, $opt, $fini) = @_;
do_reap($self);
- $self->{lei}->qerr("# @$cmd");
+ utf8::decode(my $msg = "# @$cmd");
+ $self->{lei}->qerr($msg);
return if $self->{dry_run};
$LIVE->{spawn($cmd, undef, $opt)} = [ \&reap_cmd, $self, $cmd, $fini ]
}
($lei->{opt}->{jobs} // 1) > 1;
push @cmd, '-v' if $lei->{opt}->{verbose};
push(@cmd, '-p') if $lei->{opt}->{prune};
+ PublicInbox::Git::version() >= ((2 << 24) | (29 << 16)) and
+ push(@cmd, '--no-write-fetch-head');
@cmd;
}
print $w "$op ", join("\0", @rest, '') or die "print(w): $!";
}
+sub start_update_ref {
+ my ($fgrp) = @_;
+ pipe(my ($r, $w)) or die "pipe: $!";
+ my $cmd = [ 'git', "--git-dir=$fgrp->{cur_dst}",
+ qw(update-ref --stdin -z) ];
+ my $pack = PublicInbox::OnDestroy->new($$, \&satellite_done, $fgrp);
+ start_cmd($fgrp, $cmd, { 0 => $r, 2 => $fgrp->{lei}->{2} }, $pack);
+ close $r or die "close(r): $!";
+ $fgrp->{dry_run} ? undef : $w;
+}
+
+sub upref_warn { warn "E: close(update-ref --stdin): $! (need git 1.8.5+)\n" }
+
sub fgrp_update {
my ($fgrp) = @_;
return if !keep_going($fgrp);
close $srcfh;
my %dst = map { chomp; split(/\0/) } (<$dstfh>);
close $dstfh;
- pipe(my ($r, $w)) or die "pipe: $!";
- my $cmd = [ 'git', "--git-dir=$fgrp->{cur_dst}",
- qw(update-ref --stdin -z) ];
+ my $w = start_update_ref($fgrp) or return;
my $lei = $fgrp->{lei};
- my $pack = PublicInbox::OnDestroy->new($$, \&satellite_done, $fgrp);
- start_cmd($fgrp, $cmd, { 0 => $r, 2 => $lei->{2} }, $pack);
- close $r or die "close(r): $!";
- return if $fgrp->{dry_run};
+ my $ndel;
for my $ref (keys %dst) {
my $new = delete $src{$ref};
my $old = $dst{$ref};
upr($lei, $w, 'update', $ref, $new, $old);
} else {
upr($lei, $w, 'delete', $ref, $old);
+ ++$ndel;
}
}
- while (my ($ref, $oid) = each %src) {
- upr($lei, $w, 'create', $ref, $oid);
+ # git's ref files backend doesn't allow directory/file conflicts
+ # between `delete' and `create' ops:
+ if ($ndel && scalar(keys %src)) {
+ $fgrp->{-create_refs} = \%src;
+ } else {
+ while (my ($ref, $oid) = each %src) {
+ upr($lei, $w, 'create', $ref, $oid);
+ }
}
- close($w) or warn "E: close(update-ref --stdin): $! (need git 1.8.5+)\n";
+ close($w) or upref_warn();
}
sub satellite_done {
my ($fgrp) = @_;
- pack_refs($fgrp, $fgrp->{cur_dst});
- run_puh($fgrp);
+ if (my $create = delete $fgrp->{-create_refs}) {
+ my $w = start_update_ref($fgrp) or return;
+ while (my ($ref, $oid) = each %$create) {
+ upr($fgrp->{lei}, $w, 'create', $ref, $oid);
+ }
+ close($w) or upref_warn();
+ } else {
+ pack_refs($fgrp, $fgrp->{cur_dst});
+ run_puh($fgrp);
+ }
}
sub pack_refs {
my $dst = $self->{cur_dst} // $self->{dst};
my @git = ('git', "--git-dir=$dst");
my $opt = { 2 => $self->{lei}->{2} };
- my $rn = 'origin'; # configurable?
+ my $rn = 'random'.int(rand(1 << 30));
for ("url=$uri", "fetch=+refs/*:refs/*", 'mirror=true') {
- my @kv = split(/=/, $_, 2);
- $kv[0] = "remote.$rn.$kv[0]";
- next if $self->{dry_run};
- run_die([@git, 'config', @kv], undef, $opt);
+ push @git, '-c', "remote.$rn.$_";
}
my $cmd = [ @{$self->{-torsocks}}, @git,
fetch_args($self->{lei}, $opt), $rn ];