our @rpushprotovsn_support = qw(4 3 2); # 4 is new tag format
our $protovsn;
-our $isuite = 'unstable';
+our $isuite;
our $idistro;
our $package;
our @ropts;
our $dodep14tag_re = 'want|no|always';
our $split_brain_save;
our $we_are_responder;
+our $we_are_initiator;
our $initiator_tempdir;
our $patches_applied_dirtily = 00;
our $tagformat_want;
our $tagformat;
our $tagformatfn;
+our $chase_dsc_distro=1;
our %forceopts = map { $_=>0 }
qw(unrepresentable unsupported-source-format
our $td = $ENV{DGIT_TEST_DUMMY_DIR} || "DGIT_TEST_DUMMY_DIR-unset";
our %defcfg = ('dgit.default.distro' => 'debian',
+ 'dgit.default.default-suite' => 'unstable',
+ 'dgit.default.old-dsc-distro' => 'debian',
'dgit-suite.*-security.distro' => 'debian-security',
'dgit.default.username' => '',
'dgit.default.archive-query-default-component' => 'main',
'dgit.default.sshpsql-dbname' => 'service=projectb',
'dgit.default.aptget-components' => 'main',
'dgit.default.dgit-tag-format' => 'new,old,maint',
+ 'dgit.dsc-url-proto-ok.http' => 'true',
+ 'dgit.dsc-url-proto-ok.https' => 'true',
+ 'dgit.dsc-url-proto-ok.git' => 'true',
+ 'dgit.default.dsc-url-proto-ok' => 'false',
# old means "repo server accepts pushes with old dgit tags"
# new means "repo server accepts pushes with new dgit tags"
# maint means "repo server accepts split brain pushes"
my ($c) = @_;
foreach my $src (@gitcfgsources) {
my $l = $gitcfgs{$src}{$c};
+ croak "$l $c" if $l && !ref $l;
printdebug"C $c ".(defined $l ?
join " ", map { messagequote "'$_'" } @$l :
"undef")."\n"
sub cfg {
foreach my $c (@_) {
return undef if $c =~ /RETURN-UNDEF/;
+ printdebug "C? $c\n" if $debuglevel >= 5;
my $v = git_get_config($c);
return $v if defined $v;
my $dv = $defcfg{$c};
"$us: distro or suite appears not to be (properly) supported";
}
-sub access_basedistro () {
+sub access_basedistro__noalias () {
if (defined $idistro) {
return $idistro;
} else {
}
}
+sub access_basedistro () {
+ my $noalias = access_basedistro__noalias();
+ my $canon = cfg("dgit-distro.$noalias.alias-canon",'RETURN-UNDEF');
+ return $canon // $noalias;
+}
+
sub access_nomdistro () {
my $base = access_basedistro();
my $r = cfg("dgit-distro.$base.nominal-distro",'RETURN-UNDEF') // $base;
foreach my $fi (@dfi) {
my $f = $fi->{Filename};
die "$f ?" if $f =~ m#/|^\.|\.dsc$|\.tmp$#;
+ my $upper_f = "../../../../$f";
printdebug "considering linking $f: ";
- link_ltarget "../../../../$f", $f
+ link_ltarget $upper_f, $f
or ((printdebug "($!) "), 0)
or $!==&ENOENT
or die "$f $!";
complete_file_from_dsc('.', $fi)
or next;
- if (is_orig_file_in_dsc($f, \@dfi)) {
- link $f, "../../../../$f"
- or $!==&EEXIST
- or die "$f $!";
- }
+ link $f, $upper_f
+ or $!==&EEXIST
+ or die "$f $!";
}
# We unpack and record the orig tarballs first, so that we only
my $tf = "$dstdir/$f";
my $downloaded = 0;
+ my $checkhash = sub {
+ open F, "<", "$tf" or die "$tf: $!";
+ $fi->{Digester}->reset();
+ $fi->{Digester}->addfile(*F);
+ F->error and die $!;
+ my $got = $fi->{Digester}->hexdigest();
+ $got eq $fi->{Hash} or
+ fail "file $f has hash $got but .dsc".
+ " demands hash $fi->{Hash} ".
+ ($downloaded ? "(got wrong file from archive!)"
+ : "(perhaps you should delete this file?)");
+ };
+
if (stat_exists $tf) {
progress "using existing $f";
} else {
$downloaded = 1;
}
- open F, "<", "$tf" or die "$tf: $!";
- $fi->{Digester}->reset();
- $fi->{Digester}->addfile(*F);
- F->error and die $!;
- my $got = $fi->{Digester}->hexdigest();
- $got eq $fi->{Hash} or
- fail "file $f has hash $got but .dsc".
- " demands hash $fi->{Hash} ".
- ($downloaded ? "(got wrong file from archive!)"
- : "(perhaps you should delete this file?)");
+ $checkhash->();
return 1;
}
}
sub git_lrfetch_sane {
- my (@specs) = @_;
+ my ($supplementary, @specs) = @_;
+ # Make a 'refs/'.lrfetchrefs.'/*' be just like on server,
+ # at least as regards @specs. Also leave the results in
+ # %lrfetchrefs_f, and arrange for lrfetchref_used to be
+ # able to clean these up.
+ #
+ # With $supplementary==1, @specs must not contain wildcards
+ # and we add to our previous fetches (non-atomically).
# This is rather miserable:
# When git fetch --prune is passed a fetchspec ending with a *,
my $url = access_giturl();
- printdebug "git_lrfetch_sane specs @specs\n";
+ printdebug "git_lrfetch_sane suppl=$supplementary specs @specs\n";
my $specre = join '|', map {
my $x = $_;
$x =~ s/\W/\\$&/g;
- $x =~ s/\\\*$/.*/;
+ my $wildcard = $x =~ s/\\\*$/.*/;
+ die if $wildcard && $supplementary;
"(?:refs/$x)";
} @specs;
printdebug "git_lrfetch_sane specre=$specre\n";
my $wanted_rref = sub {
local ($_) = @_;
- return m/^(?:$specre)$/o;
+ return m/^(?:$specre)$/;
};
my $fetch_iteration = 0;
my @fcmd = (@git, qw(fetch -p -n -q), $url, @fspecs);
runcmd_ordryrun_local @fcmd if @fspecs;
- %lrfetchrefs_f = ();
+ if (!$supplementary) {
+ %lrfetchrefs_f = ();
+ }
my %objgot;
git_for_each_ref(lrfetchrefs, sub {
$objgot{$objid} = 1;
});
+ if ($supplementary) {
+ last;
+ }
+
foreach my $lrefname (sort keys %lrfetchrefs_f) {
my $rrefname = 'refs'.substr($lrefname, length lrfetchrefs);
if (!exists $wantr{$rrefname}) {
}
last;
}
+
+ if (defined $csuite) {
+ printdebug "git_lrfetch_sane: tidying any old suite lrfetchrefs\n";
+ git_for_each_ref("refs/dgit-fetch/$csuite", sub {
+ my ($objid,$objtype,$lrefname,$reftail) = @_;
+ next if $lrfetchrefs_f{$lrefname}; # $csuite eq $distro ?
+ runcmd_ordryrun_local @git, qw(update-ref -d), $lrefname;
+ });
+ }
+
printdebug "git_lrfetch_sane: git fetch --no-insane emulation complete\n",
Dumper(\%lrfetchrefs_f);
}
push @specs, $rewritemap;
push @specs, qw(heads/*) if deliberately_not_fast_forward;
- git_lrfetch_sane @specs;
+ git_lrfetch_sane 0, @specs;
my %here;
my @tagpats = debiantags('*',access_nomdistro);
if (!defined $f) {
progress "$what: NO git hash";
} elsif (($dsc_hash, $dsc_distro, $dsc_hint_tag, $dsc_hint_url)
- = $f =~ m/^(\w+) ($distro_re) ($versiontag_re) (\S+)(?:\s|$)/) {
+ = $f =~ m/^(\w+)\s+($distro_re)\s+($versiontag_re)\s+(\S+)(?:\s|$)/) {
progress "$what: specified git info ($dsc_distro)";
$dsc_hint_tag = [ $dsc_hint_tag ];
} elsif ($f =~ m/^\w+\s*$/) {
$dsc_hash = $&;
- $dsc_distro //= 'debian';
+ $dsc_distro //= cfg qw(dgit.default.old-dsc-distro
+ dgit.default.distro);
$dsc_hint_tag = [ debiantags +(getfield $dsc, 'Version'),
$dsc_distro ];
progress "$what: specified git hash";
return unless defined $dsc_hash;
- my $rewritemapdata = git_cat_file $already_mapref.':map';
- if (defined $rewritemapdata
- && $rewritemapdata =~ m/^$dsc_hash(?:[ \t](\w+))/m) {
- progress "server's git history rewrite map contains a relevant entry!";
+ my $mapref =
+ defined $already_mapref &&
+ ($already_distro eq $dsc_distro || !$chase_dsc_distro)
+ ? $already_mapref : undef;
- $dsc_hash = $1;
- if (defined $dsc_hash) {
- progress "using rewritten git hash in place of .dsc value";
- } else {
- progress "server data says .dsc hash is to be disregarded";
+ my $do_fetch;
+ $do_fetch = sub {
+ my ($what, @fetch) = @_;
+
+ local $idistro = $dsc_distro;
+ my $lrf = lrfetchrefs;
+
+ if (!$chase_dsc_distro) {
+ progress
+ "not chasing .dsc distro $dsc_distro: not fetching $what";
+ return 0;
+ }
+
+ progress
+ ".dsc names distro $dsc_distro: fetching $what";
+
+ my $url = access_giturl();
+ if (!defined $url) {
+ defined $dsc_hint_url or fail <<END;
+.dsc Dgit metadata is in context of distro $dsc_distro
+for which we have no configured url and .dsc provides no hint
+END
+ my $proto =
+ $dsc_hint_url =~ m#^([-+0-9a-zA-Z]+):# ? $1 :
+ $dsc_hint_url =~ m#^/# ? 'file' : 'bad-syntax';
+ parse_cfg_bool "dsc-url-proto-ok", 'false',
+ cfg("dgit.dsc-url-proto-ok.$proto",
+ "dgit.default.dsc-url-proto-ok")
+ or fail <<END;
+.dsc Dgit metadata is in context of distro $dsc_distro
+for which we have no configured url;
+.dsc provices hinted url with protocol $proto which is unsafe.
+(can be overridden by config - consult documentation)
+END
+ $url = $dsc_hint_url;
+ }
+
+ git_lrfetch_sane 1, @fetch;
+
+ return $lrf;
+ };
+
+ my $rewrite_enable = do {
+ local $idistro = $dsc_distro;
+ access_cfg('rewrite-map-enable', 'RETURN-UNDEF');
+ };
+
+ if (parse_cfg_bool 'rewrite-map-enable', 'true', $rewrite_enable) {
+ if (!defined $mapref) {
+ my $lrf = $do_fetch->("rewrite map", $rewritemap) or return;
+ $mapref = $lrf.'/'.$rewritemap;
+ }
+ my $rewritemapdata = git_cat_file $mapref.':map';
+ if (defined $rewritemapdata
+ && $rewritemapdata =~ m/^$dsc_hash(?:[ \t](\w+))/m) {
+ progress
+ "server's git history rewrite map contains a relevant entry!";
+
+ $dsc_hash = $1;
+ if (defined $dsc_hash) {
+ progress "using rewritten git hash in place of .dsc value";
+ } else {
+ progress "server data says .dsc hash is to be disregarded";
+ }
+ }
+ }
+
+ if (!defined git_cat_file $dsc_hash) {
+ my @tags = map { "tags/".$_ } @$dsc_hint_tag;
+ my $lrf = $do_fetch->("additional commits", @tags) &&
+ defined git_cat_file $dsc_hash
+ or fail <<END;
+.dsc Dgit metadata requires commit $dsc_hash
+but we could not obtain that object anywhere.
+END
+ foreach my $t (@tags) {
+ my $fullrefname = $lrf.'/'.$t;
+ print STDERR "CHK $t $fullrefname ".Dumper(\%lrfetchrefs_f);
+ next unless $lrfetchrefs_f{$fullrefname};
+ next unless is_fast_fwd "$fullrefname~0", $dsc_hash;
+ lrfetchref_used $fullrefname;
}
}
}
fail "-p specified $package but changelog specified $clogpackage"
unless $package eq $clogpackage;
my $cversion = getfield $clogp, 'Version';
- my $tag = debiantag($cversion, access_nomdistro);
- runcmd @git, qw(check-ref-format), $tag;
+
+ if (!$we_are_initiator) {
+ # rpush initiator can't do this because it doesn't have $isuite yet
+ my $tag = debiantag($cversion, access_nomdistro);
+ runcmd @git, qw(check-ref-format), $tag;
+ }
my $dscfn = dscfn($cversion);
prep_ud();
access_giturl(); # check that success is vaguely likely
+ rpush_handle_protovsn_bothends() if $we_are_initiator;
select_tagformat();
my $clogpfn = ".git/dgit/changelog.822.tmp";
responder_send_file('changes',$changesfile);
responder_send_command("param head $dgithead");
responder_send_command("param csuite $csuite");
+ responder_send_command("param isuite $isuite");
responder_send_command("param tagformat $tagformat");
if (defined $maintviewhead) {
die unless ($protovsn//4) >= 4;
$isuite = branchsuite();
if (!$isuite) {
my $clogp = parsechangelog();
- $isuite = getfield $clogp, 'Distribution';
+ my $clogsuite = getfield $clogp, 'Distribution';
+ $isuite= $clogsuite if $clogsuite ne 'UNRELEASED';
}
} elsif (@ARGV==1) {
($isuite) = @ARGV;
sub cmd_push {
parseopts();
- pushing();
badusage "-p is not allowed with dgit push" if defined $package;
check_not_dirty();
my $clogp = parsechangelog();
badusage "incorrect arguments to dgit push";
}
$isuite = getfield $clogp, 'Distribution';
+ pushing();
if ($new_package) {
local ($package) = $existing_package; # this is a hack
canonicalise_suite();
$we_are_responder = 1;
$us .= " (build host)";
- pushing();
-
open PI, "<&STDIN" or die $!;
open STDIN, "/dev/null" or die $!;
open PO, ">&STDOUT" or die $!;
unless defined $protovsn;
responder_send_command("dgit-remote-push-ready $protovsn");
- rpush_handle_protovsn_bothends();
changedir $dir;
&cmd_push;
}
}
sub cmd_rpush {
- pushing();
my $host = nextarg;
my $dir;
if ($host =~ m/^((?:[^][]|\[[^][]*\])*)\:/) {
my @cmd = (@ssh, $host, shellquote @rdgit);
debugcmd "+",@cmd;
+ $we_are_initiator=1;
+
if (defined $initiator_tempdir) {
rmtree $initiator_tempdir;
mkdir $initiator_tempdir, 0700 or die "$initiator_tempdir: $!";
die "$protovsn ?" unless grep { $_ eq $protovsn } @rpushprotovsn_support;
$supplementary_message = '' unless $protovsn >= 3;
- fail "rpush negotiated protocol version $protovsn".
- " which does not support quilt mode $quilt_mode"
- if quiltmode_splitbrain;
-
- rpush_handle_protovsn_bothends();
for (;;) {
my ($icmd,$iargs) = initiator_expect {
m/^(\S+)(?: (.*))?$/;
sub i_resp_want ($) {
my ($keyword) = @_;
die "$keyword ?" if $i_wanted{$keyword}++;
+
+ defined $i_param{'csuite'} or badproto \*RO, "premature desire, no csuite";
+ $isuite = $i_param{'isuite'} // $i_param{'csuite'};
+ die unless $isuite =~ m/^$suite_re$/;
+
+ pushing();
+ rpush_handle_protovsn_bothends();
+
+ fail "rpush negotiated protocol version $protovsn".
+ " which does not support quilt mode $quilt_mode"
+ if quiltmode_splitbrain;
+
my @localpaths = i_method "i_want", $keyword;
printdebug "[[ $keyword @localpaths\n";
foreach my $localpath (@localpaths) {
rmtree '.pc';
+ runcmd @git, qw(checkout -f), $headref, qw(-- debian);
my $unapplied=git_add_write_tree();
printdebug "fake orig tree object $unapplied\n";
parse_dscdata();
- parse_dsc_field($dsc, "Dgit metadata in .dsc");
+ $package = getfield $dsc, 'Source';
+
+ parse_dsc_field($dsc, "Dgit metadata in .dsc")
+ unless forceing [qw(import-dsc-with-dgit-field)];
- if (defined $dsc_hash
- && !forceing [qw(import-dsc-with-dgit-field)]) {
+ if (defined $dsc_hash) {
progress "dgit: import-dsc of .dsc with Dgit field, using git hash";
+ resolve_dsc_field_commit undef, undef;
+ }
+ if (defined $dsc_hash) {
my @cmd = (qw(sh -ec),
"echo $dsc_hash | git cat-file --batch-check");
my $objgot = cmdoutput @cmd;
END
if $oldhash && !$force;
- $package = getfield $dsc, 'Source';
+ notpushing();
+
my @dfi = dsc_files_info();
foreach my $fi (@dfi) {
my $f = $fi->{Filename};
badusage "need destination argument" unless @ARGV==1;
my ($destdir) = @ARGV;
$package = '_dgit-repos-server';
+ local $access_forpush = 0;
my @cmd = (@git, qw(clone), access_giturl(), $destdir);
debugcmd ">",@cmd;
exec @cmd or fail "exec git clone: $!\n";
}
+sub cmd_print_dgit_repos_server_source_url {
+ badusage "no arguments allowed to dgit print-dgit-repos-server-source-url"
+ if @ARGV;
+ $package = '_dgit-repos-server';
+ local $access_forpush = 0;
+ my $url = access_giturl();
+ print $url, "\n" or die $!;
+}
+
sub cmd_setup_mergechangelogs {
badusage "no arguments allowed to dgit setup-mergechangelogs" if @ARGV;
setup_mergechangelogs(1);
}
sub cmd_setup_useremail {
- badusage "no arguments allowed to dgit setup-mergechangelogs" if @ARGV;
+ badusage "no arguments allowed to dgit setup-useremail" if @ARGV;
setup_useremail(1);
}
} elsif (m/^--no-rm-on-error$/s) {
push @ropts, $_;
$rmonerror = 0;
+ } elsif (m/^--no-chase-dsc-distro$/s) {
+ push @ropts, $_;
+ $chase_dsc_distro = 0;
} elsif (m/^--overwrite$/s) {
push @ropts, $_;
$overwrite_version = '';
# undocumented, for testing
push @ropts, $_;
$need_split_build_invocation = 1;
+ } elsif (m/^--config-lookup-explode=(.+)$/s) {
+ # undocumented, for testing
+ push @ropts, $_;
+ $gitcfgs{cmdline}{$1} = 'CONFIG-LOOKUP-EXPLODE';
+ # ^ it's supposed to be an array ref
} elsif (m/^(--[-0-9a-z]+)(=|$)/ && ($oi = $valopts_long{$1})) {
$val = $2 ? $' : undef; #';
$valopt->($oi->{Long});
sub parseopts_late_defaults () {
+ $isuite //= cfg("dgit-distro.$idistro.default-suite", 'RETURN-UNDEF')
+ if defined $idistro;
+ $isuite //= cfg('dgit.default.default-suite');
+
foreach my $k (keys %opts_opt_map) {
my $om = $opts_opt_map{$k};