X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=dgit;h=56cee1140814185072007d6acf2f2e9336b9f6dd;hb=37e3be3b6a61e26f9836d3575531ba716818b134;hp=eea4dbc53fbbbd5e89022116ada26abfbec82a24;hpb=380552def4af93d4f299a706a9c01e8db2f725d0;p=dgit.git
diff --git a/dgit b/dgit
index eea4dbc5..56cee114 100755
--- a/dgit
+++ b/dgit
@@ -18,7 +18,9 @@
# along with this program. If not, see .
use strict;
-$SIG{__WARN__} = sub { die $_[0]; };
+
+use Debian::Dgit;
+setup_sigwarn();
use IO::Handle;
use Data::Dumper;
@@ -37,7 +39,8 @@ use Debian::Dgit;
our $our_version = 'UNRELEASED'; ###substituted###
-our $rpushprotovsn = 2;
+our @rpushprotovsn_support = qw(3 2);
+our $protovsn;
our $isuite = 'unstable';
our $idistro;
@@ -95,11 +98,24 @@ our %opts_opt_map = ('dget' => \@dget, # accept for compatibility
'mergechanges' => \@mergechanges);
our %opts_opt_cmdonly = ('gpg' => 1);
+our %opts_cfg_insertpos = map {
+ $_,
+ scalar @{ $opts_opt_map{$_} }
+} keys %opts_opt_map;
+
+sub finalise_opts_opts();
our $keyid;
autoflush STDOUT 1;
+our $supplementary_message = '';
+
+END {
+ local ($@, $?);
+ print STDERR "! $_\n" foreach $supplementary_message =~ m/^.+$/mg;
+}
+
our $remotename = 'dgit';
our @ourdscfield = qw(Dgit Vcs-Dgit-Master);
our $csuite;
@@ -111,7 +127,7 @@ sub lref () { return "refs/heads/".lbranch(); }
sub lrref () { return "refs/remotes/$remotename/".server_branch($csuite); }
sub rrref () { return server_ref($csuite); }
-sub lrfetchrefs () { return "refs/dgit-fetch/$isuite"; }
+sub lrfetchrefs () { return "refs/dgit-fetch/$csuite"; }
sub stripepoch ($) {
my ($vsn) = @_;
@@ -173,7 +189,9 @@ sub deliberately_not_fast_forward () {
#---------- remote protocol support, common ----------
# remote push initiator/responder protocol:
-# < dgit-remote-push-ready [optional extra info ignored by old initiators]
+# $ dgit remote-push-build-host ... ...
+# where is ,... ...
+# < dgit-remote-push-ready
#
# > file parsed-changelog
# [indicates that output of dpkg-parsechangelog follows]
@@ -268,7 +286,7 @@ sub protocol_send_file ($$) {
sub protocol_read_bytes ($$) {
my ($fh, $nbytes) = @_;
- $nbytes =~ m/^[1-9]\d{0,5}$/ or badproto \*RO, "bad byte count";
+ $nbytes =~ m/^[1-9]\d{0,5}$|^0$/ or badproto \*RO, "bad byte count";
my $d;
my $got = read $fh, $d, $nbytes;
$got==$nbytes or badproto_badread $fh, "data block";
@@ -444,24 +462,28 @@ our %defcfg = ('dgit.default.distro' => 'debian',
'dgit.default.archive-query' => 'madison:',
'dgit.default.sshpsql-dbname' => 'service=projectb',
'dgit-distro.debian.archive-query' => 'ftpmasterapi:',
- 'dgit-distro.debian.git-host' => 'dgit-git.debian.net',
- 'dgit-distro.debian.git-user-force' => 'dgit',
- 'dgit-distro.debian.git-proto' => 'git+ssh://',
- 'dgit-distro.debian.git-path' => '/dgit/debian/repos',
- 'dgit-distro.debian.git-check' => 'ssh-cmd',
+ 'dgit-distro.debian.git-check' => 'url',
+ 'dgit-distro.debian.git-check-suffix' => '/info/refs',
+ 'dgit-distro.debian.new-private-pushers' => 't',
+ 'dgit-distro.debian/push.git-url' => '',
+ 'dgit-distro.debian/push.git-host' => 'push.dgit.debian.org',
+ 'dgit-distro.debian/push.git-user-force' => 'dgit',
+ 'dgit-distro.debian/push.git-proto' => 'git+ssh://',
+ 'dgit-distro.debian/push.git-path' => '/dgit/debian/repos',
+ 'dgit-distro.debian/push.git-create' => 'true',
+ 'dgit-distro.debian/push.git-check' => 'ssh-cmd',
'dgit-distro.debian.archive-query-url', 'https://api.ftp-master.debian.org/',
- 'dgit-distro.debian.archive-query-tls-key',
- '/etc/ssl/certs/%HOST%.pem:/etc/dgit/%HOST%.pem',
+# 'dgit-distro.debian.archive-query-tls-key',
+# '/etc/ssl/certs/%HOST%.pem:/etc/dgit/%HOST%.pem',
+# ^ this does not work because curl is broken nowadays
+# Fixing #790093 properly will involve providing providing the key
+# in some pacagke and maybe updating these paths.
#
# 'dgit-distro.debian.archive-query-tls-curl-args',
# '--ca-path=/etc/ssl/ca-debian',
# ^ this is a workaround but works (only) on DSA-administered machines
- 'dgit-distro.debian.diverts.alioth' => '/alioth',
- 'dgit-distro.debian/alioth.git-host' => 'git.debian.org',
- 'dgit-distro.debian/alioth.git-user-force' => '',
- 'dgit-distro.debian/alioth.git-proto' => 'git+ssh://',
- 'dgit-distro.debian/alioth.git-path' => '/git/dgit-repos/repos',
- 'dgit-distro.debian/alioth.git-create' => 'ssh-cmd',
+ 'dgit-distro.debian.git-url' => 'https://git.dgit.debian.org',
+ 'dgit-distro.debian.git-url-suffix' => '',
'dgit-distro.debian.upload-host' => 'ftp-master', # for dput
'dgit-distro.debian.mirror' => 'http://ftp.debian.org/debian/',
'dgit-distro.debian.backports-quirk' => '(squeeze)-backports*',
@@ -481,20 +503,43 @@ our %defcfg = ('dgit.default.distro' => 'debian',
'dgit-distro.test-dummy.upload-host' => 'test-dummy',
);
+our %gitcfg;
+
+sub git_slurp_config () {
+ local ($debuglevel) = $debuglevel-2;
+ local $/="\0";
+
+ my @cmd = (@git, qw(config -z --get-regexp .*));
+ debugcmd "|",@cmd;
+
+ open GITS, "-|", @cmd or failedcmd @cmd;
+ while () {
+ chomp or die;
+ printdebug "=> ", (messagequote $_), "\n";
+ m/\n/ or die "$_ ?";
+ push @{ $gitcfg{$`} }, $'; #';
+ }
+ $!=0; $?=0;
+ close GITS
+ or ($!==0 && $?==256)
+ or failedcmd @cmd;
+}
+
+sub git_get_config ($) {
+ my ($c) = @_;
+ my $l = $gitcfg{$c};
+ printdebug"C $c ".(defined $l ? messagequote "'$l'" : "undef")."\n"
+ if $debuglevel >= 4;
+ $l or return undef;
+ @$l==1 or badcfg "multiple values for $c" if @$l > 1;
+ return $l->[0];
+}
+
sub cfg {
foreach my $c (@_) {
return undef if $c =~ /RETURN-UNDEF/;
- my @cmd = (@git, qw(config --), $c);
- my $v;
- {
- local ($debuglevel) = $debuglevel-2;
- $v = cmdoutput_errok @cmd;
- };
- if ($?==0) {
- return $v;
- } elsif ($?!=256) {
- failedcmd @cmd;
- }
+ my $v = git_get_config($c);
+ return $v if defined $v;
my $dv = $defcfg{$c};
return $dv if defined $dv;
}
@@ -529,6 +574,68 @@ sub access_quirk () {
return ('none',undef);
}
+our $access_forpush;
+
+sub parse_cfg_bool ($$$) {
+ my ($what,$def,$v) = @_;
+ $v //= $def;
+ return
+ $v =~ m/^[ty1]/ ? 1 :
+ $v =~ m/^[fn0]/ ? 0 :
+ badcfg "$what needs t (true, y, 1) or f (false, n, 0) not \`$v'";
+}
+
+sub access_forpush_config () {
+ my $d = access_basedistro();
+
+ return 1 if
+ $new_package &&
+ parse_cfg_bool('new-private-pushers', 0,
+ cfg("dgit-distro.$d.new-private-pushers",
+ 'RETURN-UNDEF'));
+
+ my $v = cfg("dgit-distro.$d.readonly", 'RETURN-UNDEF');
+ $v //= 'a';
+ return
+ $v =~ m/^[ty1]/ ? 0 : # force readonly, forpush = 0
+ $v =~ m/^[fn0]/ ? 1 : # force nonreadonly, forpush = 1
+ $v =~ m/^[a]/ ? '' : # auto, forpush = ''
+ badcfg "readonly needs t (true, y, 1) or f (false, n, 0) or a (auto)";
+}
+
+sub access_forpush () {
+ $access_forpush //= access_forpush_config();
+ return $access_forpush;
+}
+
+sub pushing () {
+ die "$access_forpush ?" if ($access_forpush // 1) ne 1;
+ badcfg "pushing but distro is configured readonly"
+ if access_forpush_config() eq '0';
+ $access_forpush = 1;
+ $supplementary_message = <<'END' unless $we_are_responder;
+Push failed, before we got started.
+You can retry the push, after fixing the problem, if you like.
+END
+ finalise_opts_opts();
+}
+
+sub notpushing () {
+ finalise_opts_opts();
+}
+
+sub supplementary_message ($) {
+ my ($msg) = @_;
+ if (!$we_are_responder) {
+ $supplementary_message = $msg;
+ return;
+ } elsif ($protovsn >= 3) {
+ responder_send_command "supplementary-message ".length($msg)
+ or die $!;
+ print PO $msg or die $!;
+ }
+}
+
sub access_distros () {
# Returns list of distros to try, in order
#
@@ -542,10 +649,15 @@ sub access_distros () {
my (undef,$quirkdistro) = access_quirk();
unshift @l, $quirkdistro;
unshift @l, $instead_distro;
- return grep { defined } @l;
+ @l = grep { defined } @l;
+
+ if (access_forpush()) {
+ @l = map { ("$_/push", $_) } @l;
+ }
+ @l;
}
-sub access_cfg (@) {
+sub access_cfg_cfgs (@) {
my (@keys) = @_;
my @cfgs;
# The nesting of these loops determines the search order. We put
@@ -572,6 +684,12 @@ sub access_cfg (@) {
}
push @cfgs, map { "dgit.default.$_" } @realkeys;
push @cfgs, @rundef;
+ return @cfgs;
+}
+
+sub access_cfg (@) {
+ my (@keys) = @_;
+ my (@cfgs) = access_cfg_cfgs(@keys);
my $value = cfg(@cfgs);
return $value;
}
@@ -615,15 +733,19 @@ sub access_gituserhost () {
sub access_giturl (;$) {
my ($optional) = @_;
my $url = access_cfg('git-url','RETURN-UNDEF');
- if (!defined $url) {
+ my $suffix;
+ if (!length $url) {
my $proto = access_cfg('git-proto', 'RETURN-UNDEF');
return undef unless defined $proto;
$url =
$proto.
access_gituserhost().
access_cfg('git-path');
+ } else {
+ $suffix = access_cfg('git-url-suffix','RETURN-UNDEF');
}
- return "$url/$package.git";
+ $suffix //= '.git';
+ return "$url/$package$suffix";
}
sub parsecontrolfh ($$;$) {
@@ -717,13 +839,18 @@ sub archive_api_query_cmd ($) {
fail "for $url: stat $key: $!" unless $!==ENOENT;
next;
}
- push @cmd, "--cacert", $key, "--capath", "/dev/enoent";
+ fail "config requested specific TLS key but do not know".
+ " how to get curl to use exactly that EE key ($key)";
+# push @cmd, "--cacert", $key, "--capath", "/dev/enoent";
+# # Sadly the above line does not work because of changes
+# # to gnutls. The real fix for #790093 may involve
+# # new curl options.
last;
}
# Fixing #790093 properly will involve providing a value
# for this on clients.
- my $keys = access_cfg('archive-query-tls-curl-ca-args','RETURN-UNDEF');
- push @cmd, split / /, $keys if defined $keys;
+ my $kargs = access_cfg('archive-query-tls-curl-ca-args','RETURN-UNDEF');
+ push @cmd, split / /, $kargs if defined $kargs;
}
push @cmd, $url.$subpath;
return @cmd;
@@ -1010,13 +1137,32 @@ sub check_for_git () {
if ($r =~ m/^divert (\w+)$/) {
my $divert=$1;
my ($usedistro,) = access_distros();
+ # NB that if we are pushing, $usedistro will be $distro/push
$instead_distro= cfg("dgit-distro.$usedistro.diverts.$divert");
$instead_distro =~ s{^/}{ access_basedistro()."/" }e;
- printdebug "diverting $divert so using distro $instead_distro\n";
+ progress "diverting to $divert (using config for $instead_distro)";
return check_for_git();
}
failedcmd @cmd unless $r =~ m/^[01]$/;
return $r+0;
+ } elsif ($how eq 'url') {
+ my $prefix = access_cfg('git-check-url','git-url');
+ my $suffix = access_cfg('git-check-suffix','git-suffix',
+ 'RETURN-UNDEF') // '.git';
+ my $url = "$prefix/$package$suffix";
+ my @cmd = (qw(curl -sS -I), $url);
+ my $result = cmdoutput @cmd;
+ $result =~ m/^\S+ (404|200) /s or
+ fail "unexpected results from git check query - ".
+ Dumper($prefix, $result);
+ my $code = $1;
+ if ($code eq '404') {
+ return 0;
+ } elsif ($code eq '200') {
+ return 1;
+ } else {
+ die;
+ }
} elsif ($how eq 'true') {
return 1;
} elsif ($how eq 'false') {
@@ -1070,7 +1216,21 @@ sub mktree_in_ud_from_only_subdir () {
$dirs[0] =~ m#^([^/]+)/\.$# or die;
my $dir = $1;
changedir $dir;
- fail "source package contains .git directory" if stat_exists '.git';
+
+ my @gitscmd = qw(find -name .git -prune -print0);
+ debugcmd "|",@gitscmd;
+ open GITS, "-|", @gitscmd or failedcmd @gitscmd;
+ {
+ local $/="\0";
+ while () {
+ chomp or die;
+ print STDERR "$us: warning: removing from source package: ",
+ (messagequote $_), "\n";
+ rmtree $_;
+ }
+ }
+ $!=0; $?=0; close GITS or failedcmd @gitscmd;
+
mktree_in_ud_here();
my $format=get_source_format();
if (madformat($format)) {
@@ -1137,6 +1297,69 @@ sub clogp_authline ($) {
return $authline;
}
+sub vendor_patches_distro ($$) {
+ my ($checkdistro, $what) = @_;
+ return unless defined $checkdistro;
+
+ my $series = "debian/patches/\L$checkdistro\E.series";
+ printdebug "checking for vendor-specific $series ($what)\n";
+
+ if (!open SERIES, "<", $series) {
+ die "$series $!" unless $!==ENOENT;
+ return;
+ }
+ while () {
+ next unless m/\S/;
+ next if m/^\s+\#/;
+
+ print STDERR <error;
+ close SERIES;
+}
+
+sub check_for_vendor_patches () {
+ # This dpkg-source feature doesn't seem to be documented anywhere!
+ # But it can be found in the changelog (reformatted):
+
+ # commit 4fa01b70df1dc4458daee306cfa1f987b69da58c
+ # Author: Raphael Hertzog
+ # Date: Sun Oct 3 09:36:48 2010 +0200
+
+ # dpkg-source: correctly create .pc/.quilt_series with alternate
+ # series files
+ #
+ # If you have debian/patches/ubuntu.series and you were
+ # unpacking the source package on ubuntu, quilt was still
+ # directed to debian/patches/series instead of
+ # debian/patches/ubuntu.series.
+ #
+ # debian/changelog | 3 +++
+ # scripts/Dpkg/Source/Package/V3/quilt.pm | 4 +++-
+ # 2 files changed, 6 insertions(+), 1 deletion(-)
+
+ use Dpkg::Vendor;
+ vendor_patches_distro($ENV{DEB_VENDOR}, "DEB_VENDOR");
+ vendor_patches_distro(Dpkg::Vendor::get_current_vendor(),
+ "Dpkg::Vendor \`current vendor'");
+ vendor_patches_distro(access_basedistro(),
+ "distro being accessed");
+}
+
sub generate_commit_from_dsc () {
prep_ud();
changedir $ud;
@@ -1169,6 +1392,7 @@ sub generate_commit_from_dsc () {
runcmd @cmd;
my ($tree,$dir) = mktree_in_ud_from_only_subdir();
+ check_for_vendor_patches() if madformat($dsc->{format});
runcmd qw(sh -ec), 'dpkg-parsechangelog >../changelog.tmp';
my $clogp = parsecontrol('../changelog.tmp',"commit's changelog");
my $authline = clogp_authline $clogp;
@@ -1271,12 +1495,33 @@ sub ensure_we_have_orig () {
}
sub git_fetch_us () {
- runcmd_ordryrun_local @git, qw(fetch),access_giturl(),fetchspec();
- if (deliberately_not_fast_forward) {
- runcmd_ordryrun_local @git, qw(fetch -p), access_giturl(),
- map { "+refs/$_/*:".lrfetchrefs."/$_/*" }
- qw(tags heads);
- }
+ my @specs = (fetchspec());
+ push @specs,
+ map { "+refs/$_/*:".lrfetchrefs."/$_/*" }
+ qw(tags heads);
+ runcmd_ordryrun_local @git, qw(fetch -p -n -q), access_giturl(), @specs;
+
+ my %here;
+ my $tagpat = debiantag('*',access_basedistro);
+
+ git_for_each_ref("refs/tags/".$tagpat, sub {
+ my ($objid,$objtype,$fullrefname,$reftail) = @_;
+ printdebug "currently $fullrefname=$objid\n";
+ $here{$fullrefname} = $objid;
+ });
+ git_for_each_ref(lrfetchrefs."/tags/".$tagpat, sub {
+ my ($objid,$objtype,$fullrefname,$reftail) = @_;
+ my $lref = "refs".substr($fullrefname, length lrfetchrefs);
+ printdebug "offered $lref=$objid\n";
+ if (!defined $here{$lref}) {
+ my @upd = (@git, qw(update-ref), $lref, $objid, '');
+ runcmd_ordryrun_local @upd;
+ } elsif ($here{$lref} eq $objid) {
+ } else {
+ print STDERR \
+ "Not updateting $lref from $here{$lref} to $objid.\n";
+ }
+ });
}
sub fetch_from_archive () {
@@ -1383,6 +1628,38 @@ END
return 1;
}
+sub set_local_git_config ($$) {
+ my ($k, $v) = @_;
+ runcmd @git, qw(config), $k, $v;
+}
+
+sub setup_mergechangelogs () {
+ my $driver = 'dpkg-mergechangelogs';
+ my $cb = "merge.$driver";
+ my $attrs = '.git/info/attributes';
+ ensuredir '.git/info';
+
+ open NATTRS, ">", "$attrs.new" or die "$attrs.new $!";
+ if (!open ATTRS, "<", $attrs) {
+ $!==ENOENT or die "$attrs: $!";
+ } else {
+ while () {
+ chomp;
+ next if m{^debian/changelog\s};
+ print NATTRS $_, "\n" or die $!;
+ }
+ ATTRS->error and die $!;
+ close ATTRS;
+ }
+ print NATTRS "debian/changelog merge=$driver\n" or die $!;
+ close NATTRS;
+
+ set_local_git_config "$cb.name", 'debian/changelog merge driver';
+ set_local_git_config "$cb.driver", 'dpkg-mergechangelogs -m %O %A %B %A';
+
+ rename "$attrs.new", "$attrs" or die "$attrs: $!";
+}
+
sub clone ($) {
my ($dstdir) = @_;
canonicalise_suite();
@@ -1393,7 +1670,7 @@ sub clone ($) {
runcmd @git, qw(init -q);
my $giturl = access_giturl(1);
if (defined $giturl) {
- runcmd @git, qw(config), "remote.$remotename.fetch", fetchspec();
+ set_local_git_config "remote.$remotename.fetch", fetchspec();
open H, "> .git/HEAD" or die $!;
print H "ref: ".lref()."\n" or die $!;
close H or die $!;
@@ -1412,6 +1689,7 @@ sub clone ($) {
$vcsgiturl =~ s/\s+-b\s+\S+//g;
runcmd @git, qw(remote add vcs-git), $vcsgiturl;
}
+ setup_mergechangelogs();
runcmd @git, qw(reset --hard), lrref();
printdone "ready for work in $dstdir";
}
@@ -1498,7 +1776,7 @@ sub push_parse_changelog ($) {
$package = getfield $clogp, 'Source';
my $cversion = getfield $clogp, 'Version';
- my $tag = debiantag($cversion);
+ my $tag = debiantag($cversion, access_basedistro);
runcmd @git, qw(check-ref-format), $tag;
my $dscfn = dscfn($cversion);
@@ -1592,6 +1870,10 @@ sub sign_changes ($) {
sub dopush ($) {
my ($forceflag) = @_;
printdebug "actually entering push\n";
+ supplementary_message(<<'END');
+Push failed, while preparing your push.
+You can retry the push, after fixing the problem, if you like.
+END
prep_ud();
access_giturl(); # check that success is vaguely likely
@@ -1624,6 +1906,7 @@ sub dopush ($) {
runcmd qw(dpkg-source -x --),
$dscpath =~ m#^/# ? $dscpath : "../../../$dscpath";
my ($tree,$dir) = mktree_in_ud_from_only_subdir();
+ check_for_vendor_patches() if madformat($dsc->{format});
changedir '../../../..';
my $diffopt = $debuglevel>0 ? '--exit-code' : '--quiet';
my @diffcmd = (@git, qw(diff), $diffopt, $tree);
@@ -1640,12 +1923,6 @@ sub dopush ($) {
failedcmd @diffcmd;
}
}
-#fetch from alioth
-#do fast forward check and maybe fake merge
-# if (!is_fast_fwd(mainbranch
-# runcmd @git, qw(fetch -p ), "$alioth_git/$package.git",
-# map { lref($_).":".rref($_) }
-# (uploadbranch());
my $head = git_rev_parse('HEAD');
if (!$changesfile) {
my $multi = "$buildproductsdir/".
@@ -1681,6 +1958,11 @@ sub dopush ($) {
my $tfn = sub { ".git/dgit/tag$_[0]"; };
my $tagobjfn;
+ supplementary_message(<<'END');
+Push failed, while signing the tag.
+You can retry the push, after fixing the problem, if you like.
+END
+ # If we manage to sign but fail to record it anywhere, it's fine.
if ($we_are_responder) {
$tagobjfn = $tfn->('.signed.tmp');
responder_receive_files('signed-tag', $tagobjfn);
@@ -1691,12 +1973,19 @@ sub dopush ($) {
$changesfile,$changesfile,
$tfn);
}
+ supplementary_message(<<'END');
+Push failed, *after* signing the tag.
+If you want to try again, you should use a new version number.
+END
my $tag_obj_hash = cmdoutput @git, qw(hash-object -w -t tag), $tagobjfn;
runcmd_ordryrun @git, qw(verify-tag), $tag_obj_hash;
runcmd_ordryrun_local @git, qw(update-ref), "refs/tags/$tag", $tag_obj_hash;
- runcmd_ordryrun @git, qw(tag -v --), $tag;
+ supplementary_message(<<'END');
+Push failed, while updating the remote git repository - see messages above.
+If you want to try again, you should use a new version number.
+END
if (!check_for_git()) {
create_remote_git_repo();
}
@@ -1704,6 +1993,10 @@ sub dopush ($) {
$forceflag."HEAD:".rrref(), $forceflag."refs/tags/$tag";
runcmd_ordryrun @git, qw(update-ref -m), 'dgit push', lrref(), 'HEAD';
+ supplementary_message(<<'END');
+Push failed, after updating the remote git repository.
+If you want to try again, you must use a new version number.
+END
if ($we_are_responder) {
my $dryrunsuffix = act_local() ? "" : ".tmp";
responder_receive_files('signed-dsc-changes',
@@ -1718,16 +2011,25 @@ sub dopush ($) {
sign_changes $changesfile;
}
+ supplementary_message(<<'END');
+Push failed, while uploading package(s) to the archive server.
+You can retry the upload of exactly these same files with dput of:
+ $changesfile
+If that .changes file is broken, you will need to use a new version
+number for your next attempt at the upload.
+END
my $host = access_cfg('upload-host','RETURN-UNDEF');
my @hostarg = defined($host) ? ($host,) : ();
runcmd_ordryrun @dput, @hostarg, $changesfile;
printdone "pushed and uploaded $cversion";
+ supplementary_message('');
responder_send_command("complete");
}
sub cmd_clone {
parseopts();
+ notpushing();
my $dstdir;
badusage "-p is not allowed with clone; specify as argument instead"
if defined $package;
@@ -1775,6 +2077,7 @@ sub branchsuite () {
}
sub fetchpullargs () {
+ notpushing();
if (!defined $package) {
my $sourcep = parsecontrol('debian/control','debian/control');
$package = getfield $sourcep, 'Source';
@@ -1809,6 +2112,7 @@ sub cmd_pull {
sub cmd_push {
parseopts();
+ pushing();
badusage "-p is not allowed with dgit push" if defined $package;
check_not_dirty();
my $clogp = parsechangelog();
@@ -1824,13 +2128,19 @@ sub cmd_push {
if ($new_package) {
local ($package) = $existing_package; # this is a hack
canonicalise_suite();
- }
- if (defined $specsuite && $specsuite ne $isuite) {
+ } else {
canonicalise_suite();
- $csuite eq $specsuite or
+ }
+ if (defined $specsuite &&
+ $specsuite ne $isuite &&
+ $specsuite ne $csuite) {
fail "dgit push: changelog specifies $isuite ($csuite)".
" but command line specifies $specsuite";
}
+ supplementary_message(<<'END');
+Push failed, while checking state of the archive.
+You can retry the push, after fixing the problem, if you like.
+END
if (check_for_git()) {
git_fetch_us();
}
@@ -1871,6 +2181,8 @@ sub cmd_remote_push_build_host {
$we_are_responder = 1;
$us .= " (build host)";
+ pushing();
+
open PI, "<&STDIN" or die $!;
open STDIN, "/dev/null" or die $!;
open PO, ">&STDOUT" or die $!;
@@ -1879,11 +2191,16 @@ sub cmd_remote_push_build_host {
autoflush STDOUT 1;
$vsnwant //= 1;
- fail "build host has dgit rpush protocol version".
- " $rpushprotovsn but invocation host has $vsnwant"
- unless grep { $rpushprotovsn eq $_ } split /,/, $vsnwant;
+ ($protovsn) = grep {
+ $vsnwant =~ m{^(?:.*,)?$_(?:,.*)?$}
+ } @rpushprotovsn_support;
+
+ fail "build host has dgit rpush protocol versions ".
+ (join ",", @rpushprotovsn_support).
+ " but invocation host has $vsnwant"
+ unless defined $protovsn;
- responder_send_command("dgit-remote-push-ready $rpushprotovsn");
+ responder_send_command("dgit-remote-push-ready $protovsn");
changedir $dir;
&cmd_push;
@@ -1919,6 +2236,7 @@ sub i_method {
}
sub cmd_rpush {
+ pushing();
my $host = nextarg;
my $dir;
if ($host =~ m/^((?:[^][]|\[[^][]*\])*)\:/) {
@@ -1928,7 +2246,8 @@ sub cmd_rpush {
$dir = nextarg;
}
$dir =~ s{^-}{./-};
- my @rargs = ($dir,$rpushprotovsn);
+ my @rargs = ($dir);
+ push @rargs, join ",", @rpushprotovsn_support;
my @rdgit;
push @rdgit, @dgit;
push @rdgit, @ropts;
@@ -1946,7 +2265,9 @@ sub cmd_rpush {
}
$i_child_pid = open2(\*RO, \*RI, @cmd);
changedir $i_tmp;
- initiator_expect { m/^dgit-remote-push-ready/ };
+ ($protovsn) = initiator_expect { m/^dgit-remote-push-ready (\S+)/ };
+ die "$protovsn ?" unless grep { $_ eq $protovsn } @rpushprotovsn_support;
+ $supplementary_message = '' unless $protovsn >= 3;
for (;;) {
my ($icmd,$iargs) = initiator_expect {
m/^(\S+)(?: (.*))?$/;
@@ -1962,6 +2283,11 @@ sub i_resp_progress ($) {
progress $msg;
}
+sub i_resp_supplementary_message ($) {
+ my ($rhs) = @_;
+ $supplementary_message = protocol_read_bytes \*RO, $rhs;
+}
+
sub i_resp_complete {
my $pid = $i_child_pid;
$i_child_pid = undef; # prevents killing some other process with same pid
@@ -2262,7 +2588,7 @@ sub quiltify ($$) {
my $s = $abbrev->($notp);
my $c = $notp->{Child};
$s .= "..".$abbrev->($c) if $c;
- $s .= ": ".$c->{Whynot};
+ $s .= ": ".$notp->{Whynot};
return $s;
};
if ($quilt_mode eq 'linear') {
@@ -2346,6 +2672,8 @@ sub build_maybe_quilt_fixup () {
return unless madformat $format;
# sigh
+ check_for_vendor_patches();
+
# Our objective is:
# - honour any existing .pc in case it has any strangeness
# - determine the git commit corresponding to the tip of
@@ -2470,7 +2798,7 @@ END
commit_quilty_patch();
if ($mustdeletepc) {
- runcmd @git, qw(rm -rq .pc);
+ runcmd @git, qw(rm -rqf .pc);
commit_admin "Commit removal of .pc (quilt series tracking data)";
}
@@ -2502,8 +2830,18 @@ sub quilt_fixup_editor () {
sub clean_tree () {
if ($cleanmode eq 'dpkg-source') {
runcmd_ordryrun_local @dpkgbuildpackage, qw(-T clean);
+ } elsif ($cleanmode eq 'dpkg-source-d') {
+ runcmd_ordryrun_local @dpkgbuildpackage, qw(-d -T clean);
} elsif ($cleanmode eq 'git') {
runcmd_ordryrun_local @git, qw(clean -xdf);
+ } elsif ($cleanmode eq 'git-ff') {
+ runcmd_ordryrun_local @git, qw(clean -xdff);
+ } elsif ($cleanmode eq 'check') {
+ my $leftovers = cmdoutput @git, qw(clean -xdn);
+ if (length $leftovers) {
+ print STDERR $leftovers, "\n" or die $!;
+ fail "tree contains uncommitted files and --clean=check specified";
+ }
} elsif ($cleanmode eq 'none') {
} else {
die "$cleanmode ?";
@@ -2512,10 +2850,12 @@ sub clean_tree () {
sub cmd_clean () {
badusage "clean takes no additional arguments" if @ARGV;
+ notpushing();
clean_tree();
}
sub build_prep () {
+ notpushing();
badusage "-p is not allowed when building" if defined $package;
check_not_dirty();
clean_tree();
@@ -2553,17 +2893,35 @@ sub changesopts () {
return @opts;
}
+sub massage_dbp_args ($) {
+ my ($cmd) = @_;
+ return unless $cleanmode =~ m/git|none/;
+ debugcmd '#massaging#', @$cmd if $debuglevel>1;
+ my @newcmd = shift @$cmd;
+ # -nc has the side effect of specifying -b if nothing else specified
+ push @newcmd, '-nc';
+ # and some combinations of -S, -b, et al, are errors, rather than
+ # later simply overriding earlier
+ push @newcmd, '-F' unless grep { m/^-[bBASF]$/ } @$cmd;
+ push @newcmd, @$cmd;
+ @$cmd = @newcmd;
+}
+
sub cmd_build {
build_prep();
- runcmd_ordryrun_local @dpkgbuildpackage, qw(-us -uc), changesopts(), @ARGV;
+ my @dbp = (@dpkgbuildpackage, qw(-us -uc), changesopts(), @ARGV);
+ massage_dbp_args \@dbp;
+ runcmd_ordryrun_local @dbp;
printdone "build successful\n";
}
sub cmd_git_build {
build_prep();
+ my @dbp = @dpkgbuildpackage;
+ massage_dbp_args \@dbp;
my @cmd =
(qw(git-buildpackage -us -uc --git-no-sign-tags),
- "--git-builder=@dpkgbuildpackage");
+ "--git-builder=@dbp");
unless (grep { m/^--git-debian-branch|^--git-ignore-branch/ } @ARGV) {
canonicalise_suite();
push @cmd, "--git-debian-branch=".lbranch();
@@ -2580,6 +2938,9 @@ sub build_source {
if ($cleanmode eq 'dpkg-source') {
runcmd_ordryrun_local (@dpkgbuildpackage, qw(-us -uc -S)),
changesopts();
+ } elsif ($cleanmode eq 'dpkg-source-d') {
+ runcmd_ordryrun_local (@dpkgbuildpackage, qw(-us -uc -S -d)),
+ changesopts();
} else {
my $pwd = must_getcwd();
my $leafdir = basename $pwd;
@@ -2604,7 +2965,7 @@ sub cmd_sbuild {
changedir "..";
my $pat = "${package}_".(stripepoch $version)."_*.changes";
if (act_local()) {
- stat_exist $dscfn or fail "$dscfn (in parent directory): $!";
+ stat_exists $dscfn or fail "$dscfn (in parent directory): $!";
stat_exists $sourcechanges
or fail "$sourcechanges (in parent directory): $!";
foreach my $cf (glob $pat) {
@@ -2653,6 +3014,11 @@ sub cmd_clone_dgit_repos_server {
exec @cmd or fail "exec git clone: $!\n";
}
+sub cmd_setup_mergechangelogs {
+ badusage "no arguments allowed to dgit setup-mergechangelogs" if @ARGV;
+ setup_mergechangelogs();
+}
+
#---------- argument parsing and main program ----------
sub cmd_version {
@@ -2693,7 +3059,7 @@ sub parseopts () {
} elsif (m/^--since-version=([^_]+|_)$/) {
push @ropts, $_;
$changes_since_version = $1;
- } elsif (m/^--([-0-9a-z]+)=(.*)/s &&
+ } elsif (m/^--([-0-9a-z]+)=(.+)/s &&
($om = $opts_opt_map{$1}) &&
length $om->[0]) {
push @ropts, $_;
@@ -2717,7 +3083,7 @@ sub parseopts () {
} elsif (m/^--build-products-dir=(.*)/s) {
push @ropts, $_;
$buildproductsdir = $1;
- } elsif (m/^--clean=(dpkg-source|git|none)$/s) {
+ } elsif (m/^--clean=(dpkg-source(?:-d)?|git|git-ff|check|none)$/s) {
push @ropts, $_;
$cleanmode = $1;
} elsif (m/^--clean=(.*)$/s) {
@@ -2789,9 +3155,18 @@ sub parseopts () {
} elsif (s/^-wg$//s) {
push @ropts, $&;
$cleanmode = 'git';
+ } elsif (s/^-wgf$//s) {
+ push @ropts, $&;
+ $cleanmode = 'git-ff';
} elsif (s/^-wd$//s) {
push @ropts, $&;
$cleanmode = 'dpkg-source';
+ } elsif (s/^-wdd$//s) {
+ push @ropts, $&;
+ $cleanmode = 'dpkg-source-d';
+ } elsif (s/^-wc$//s) {
+ push @ropts, $&;
+ $cleanmode = 'check';
} else {
badusage "unknown short option \`$_'";
}
@@ -2800,11 +3175,41 @@ sub parseopts () {
}
}
+sub finalise_opts_opts () {
+ foreach my $k (keys %opts_opt_map) {
+ my $om = $opts_opt_map{$k};
+
+ my $v = access_cfg("cmd-$k", 'RETURN-UNDEF');
+ if (defined $v) {
+ badcfg "cannot set command for $k"
+ unless length $om->[0];
+ $om->[0] = $v;
+ }
+
+ foreach my $c (access_cfg_cfgs("opts-$k")) {
+ my $vl = $gitcfg{$c};
+ printdebug "CL $c ",
+ ($vl ? join " ", map { shellquote } @$vl : ""),
+ "\n" if $debuglevel >= 4;
+ next unless $vl;
+ badcfg "cannot configure options for $k"
+ if $opts_opt_cmdonly{$k};
+ my $insertpos = $opts_cfg_insertpos{$k};
+ @$om = ( @$om[0..$insertpos-1],
+ @$vl,
+ @$om[$insertpos..$#$om] );
+ }
+ }
+}
+
if ($ENV{$fakeeditorenv}) {
+ git_slurp_config();
quilt_fixup_editor();
}
parseopts();
+git_slurp_config();
+
print STDERR "DRY RUN ONLY\n" if $dryrun_level > 1;
print STDERR "DAMP RUN - WILL MAKE LOCAL (UNSIGNED) CHANGES\n"
if $dryrun_level == 1;
@@ -2816,6 +3221,7 @@ my $cmd = shift @ARGV;
$cmd =~ y/-/_/;
if (!defined $quilt_mode) {
+ local $access_forpush;
$quilt_mode = cfg('dgit.force.quilt-mode', 'RETURN-UNDEF')
// access_cfg('quilt-mode', 'RETURN-UNDEF')
// 'linear';