X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=dgit;h=e46da88a00d4e8906722d4a288bd32e3a5061b94;hb=59235957912b6f55e150d533749aad62b1647e8e;hp=1968d8c1b7895f0dcedd17d47532acb417d3bf7c;hpb=30ecd0c98b1076d3245f225f331349c5901b890e;p=dgit.git
diff --git a/dgit b/dgit
index 1968d8c1..e46da88a 100755
--- a/dgit
+++ b/dgit
@@ -24,15 +24,18 @@ use Data::Dumper;
use LWP::UserAgent;
use Dpkg::Control::Hash;
use File::Path;
+use File::Temp qw(tempdir);
use File::Basename;
use Dpkg::Version;
use POSIX;
+use IPC::Open2;
our $our_version = 'UNRELEASED'; ###substituted###
our $isuite = 'unstable';
our $idistro;
our $package;
+our @ropts;
our $sign = 1;
our $dryrun = 0;
@@ -52,6 +55,8 @@ our (@dput) = qw(dput);
our (@debsign) = qw(debsign);
our (@gpg) = qw(gpg);
our (@sbuild) = qw(sbuild -A);
+our (@ssh) = 'ssh';
+our (@dgit) = qw(dgit);
our (@dpkgbuildpackage) = qw(dpkg-buildpackage -i\.git/ -I.git);
our (@dpkgsource) = qw(dpkg-source -i\.git/ -I.git);
our (@dpkggenchanges) = qw(dpkg-genchanges);
@@ -63,17 +68,23 @@ our %opts_opt_map = ('dget' => \@dget,
'debsign' => \@debsign,
'gpg' => \@gpg,
'sbuild' => \@sbuild,
+ 'ssh' => \@ssh,
+ 'dgit' => \@dgit,
'dpkg-source' => \@dpkgsource,
'dpkg-buildpackage' => \@dpkgbuildpackage,
'dpkg-genchanges' => \@dpkggenchanges,
'ch' => \@changesopts,
'mergechanges' => \@mergechanges);
+our %opts_opt_cmdonly = ('gpg' => 1);
+
our $keyid;
our $debug = 0;
open DEBUG, ">/dev/null" or die $!;
+autoflush STDOUT 1;
+
our $remotename = 'dgit';
our @ourdscfield = qw(Dgit Vcs-Dgit-Master);
our $branchprefix = 'dgit';
@@ -104,6 +115,9 @@ sub dscfn ($) {
sub changesopts () { return @changesopts[1..$#changesopts]; }
our $us = 'dgit';
+our $debugprefix = ' ';
+
+sub printdebug { print DEBUG $debugprefix, @_ or die $!; }
sub fail { die "$us: @_\n"; }
@@ -119,55 +133,154 @@ sub fetchspec () {
return "+".rrref().":".lrref();
}
-our $ua;
+#---------- remote protocol support, common ----------
-sub responder_send_command ($) {
- my ($command) = @_;
- return unless $we_are_responder;
- # called even without $we_are_responder
- print DEBUG "<< $command\n";
- print $command, "\n" or die $!;
-}
+# remote push initiator/responder protocol:
+# < dgit-remote-push-ready [optional extra info ignored by old initiators]
+#
+# > file parsed-changelog
+# [indicates that output of dpkg-parsechangelog follows]
+# > data-block NBYTES
+# > [NBYTES bytes of data (no newline)]
+# [maybe some more blocks]
+# > data-end
+#
+# > file dsc
+# [etc]
+#
+# > file changes
+# [etc]
+#
+# > param head HEAD
+#
+# > want signed-tag
+# [indicates that signed tag is wanted]
+# < data-block NBYTES
+# < [NBYTES bytes of data (no newline)]
+# [maybe some more blocks]
+# < data-end
+# < files-end
+#
+# > want signed-dsc-changes
+# < data-block NBYTES [transfer of signed dsc]
+# [etc]
+# < data-block NBYTES [transfer of signed changes]
+# [etc]
+# < files-end
+#
+# > complete
-sub progress {
- if ($we_are_responder) {
- my $m = join '', @_;
- responder_send_command "progress ".length($m) or die $!;
- print $m or die $!;
+sub badproto ($$) {
+ my ($fh, $m) = @_;
+ fail "connection lost: $!" if $fh->error;
+ fail "connection terminated" if $fh->eof;
+ fail "protocol violation; $m not expected";
+}
+
+sub protocol_expect (&$) {
+ my ($match, $fh) = @_;
+ local $_;
+ $_ = <$fh>;
+ defined && chomp or badproto $fh, "eof";
+ if (wantarray) {
+ my @r = &$match;
+ return @r if @r;
} else {
- print @_, "\n";
+ my $r = &$match;
+ return $r if $r;
}
+ badproto $fh, "\`$_'";
}
-sub protocol_send_file ($) {
- my ($fh, $cmdprefix, $ourfn) = @_;
+sub protocol_send_file ($$) {
+ my ($fh, $ourfn) = @_;
open PF, "<", $ourfn or die "$ourfn: $!";
- print $fh "$cmdprefix begin\n" or die $!;
for (;;) {
my $d;
my $got = read PF, $d, 65536;
die "$ourfn: $!" unless defined $got;
last if $got;
- print $fh "$keyword block ".length($d)."\n" or die $!;
+ print $fh "data-block ".length($d)."\n" or die $!;
print $d or die $!;
}
- print $fh "$keyword end\n" or die $!;
+ print $fh "data-end\n" or die $!;
close PF;
}
+sub protocol_read_bytes ($$) {
+ my ($fh, $nbytes) = @_;
+ $nbytes =~ m/^\d{1,6}$/ or badproto \*RO, "bad byte count";
+ my $d;
+ my $got = read $fh, $d, $nbytes;
+ $got==$nbytes or badproto $fh, "eof during data block";
+ return $d;
+}
+
+sub protocol_receive_file ($$) {
+ my ($fh, $ourfn) = @_;
+ open PF, ">", $ourfn or die "$ourfn: $!";
+ for (;;) {
+ my ($y,$l) = protocol_expect {
+ m/^data-block (.*})$|data-end$/;
+ length $1 ? (1,$1) : (0);
+ } \*STDIN;
+ last unless $y;
+ my $d = protocol_read_bytes \*STDIN, $1;
+ print PF $d or die $!;
+ }
+ printdebug "received into $ourfn\n";
+}
+
+#---------- remote protocol support, responder ----------
+
+sub responder_send_command ($) {
+ my ($command) = @_;
+ return unless $we_are_responder;
+ # called even without $we_are_responder
+ printdebug "<< $command\n";
+ print $command, "\n" or die $!;
+}
+
sub responder_send_file ($$) {
my ($keyword, $ourfn) = @_;
return unless $we_are_responder;
- print DEBUG "responder sending $keyword $ourfn\n";
- protocol_send_file(\*STDOUT, "upload $keyword");
+ printdebug "[[ $keyword $ourfn\n";
+ responder_send_command "file $keyword";
+ protocol_send_file \*STDOUT, $ourfn;
}
sub responder_receive_files ($@) {
my ($keyword, @ourfns) = @_;
die unless $we_are_responder;
-
+ printdebug "]] $keyword @ourfns\n";
+ responder_send_command "want $keyword";
+ foreach my $fn (@ourfns) {
+ protocol_receive_file \*STDIN, $fn;
+ }
+ protocol_expect { m/^files-end$/ } \*STDIN;
}
+#---------- remote protocol support, initiator ----------
+
+sub initiator_expect (&) {
+ my ($match) = @_;
+ protocol_expect { &$match } \*RO;
+}
+
+#---------- end remote code ----------
+
+sub progress {
+ if ($we_are_responder) {
+ my $m = join '', @_;
+ responder_send_command "progress ".length($m) or die $!;
+ print $m or die $!;
+ } else {
+ print @_, "\n";
+ }
+}
+
+our $ua;
+
sub url_get {
if (!$ua) {
$ua = LWP::UserAgent->new();
@@ -194,13 +307,13 @@ sub shellquote {
push @out, $_;
}
}
- return join '', @out;
+ return join ' ', @out;
}
sub printcmd {
my $fh = shift @_;
my $intro = shift @_;
- print $fh $intro or die $!;
+ print $fh $intro," " or die $!;
print $fh shellquote @_ or die $!;
print $fh "\n" or die $!;
}
@@ -219,7 +332,7 @@ sub failedcmd {
}
sub runcmd {
- printcmd(\*DEBUG,"+",@_) if $debug>0;
+ printcmd(\*DEBUG,$debugprefix."+",@_) if $debug>0;
$!=0; $?=0;
failedcmd @_ if system @_;
}
@@ -234,16 +347,16 @@ sub printdone {
sub cmdoutput_errok {
die Dumper(\@_)." ?" if grep { !defined } @_;
- printcmd(\*DEBUG,"|",@_) if $debug>0;
+ printcmd(\*DEBUG,$debugprefix."|",@_) if $debug>0;
open P, "-|", @_ or die $!;
my $d;
$!=0; $?=0;
{ local $/ = undef; $d =
; }
die $! if P->error;
- if (!close P) { print DEBUG "=>!$?\n" if $debug>0; return undef; }
+ if (!close P) { printdebug "=>!$?\n" if $debug>0; return undef; }
chomp $d;
$d =~ m/^.*/;
- print DEBUG "=> \`$&'",(length $' ? '...' : ''),"\n" if $debug>0; #';
+ printdebug "=> \`$&'",(length $' ? '...' : ''),"\n" if $debug>0; #';
return $d;
}
@@ -254,7 +367,7 @@ sub cmdoutput {
}
sub dryrun_report {
- printcmd(\*STDERR,"#",@_);
+ printcmd(\*STDERR,$debugprefix."#",@_);
}
sub runcmd_ordryrun {
@@ -265,6 +378,14 @@ sub runcmd_ordryrun {
}
}
+sub runcmd_ordryrun_local {
+ if ($dryrun <= 1) {
+ runcmd @_;
+ } else {
+ dryrun_report @_;
+ }
+}
+
sub shell_cmd {
my ($first_shell, @cmd) = @_;
return qw(sh -ec), $first_shell.'; exec "$@"', 'x', @cmd;
@@ -276,9 +397,11 @@ main usages:
dgit [dgit-opts] fetch|pull [dgit-opts] [suite]
dgit [dgit-opts] build [git-buildpackage-opts|dpkg-buildpackage-opts]
dgit [dgit-opts] push [dgit-opts] [suite]
+ dgit [dgit-opts] rpush build-host:build-dir ...
important dgit options:
-k sign tag and package with instead of default
--dry-run -n do not change anything, but go through the motions
+ --damp-run -L like --dry-run but make local changes, without signing
--new -N allow introducing a new package
--debug -D increase debug level
-c= set git config option (used directly by dgit too)
@@ -293,6 +416,11 @@ sub badusage {
exit 8;
}
+sub nextarg {
+ @ARGV or badusage "too few arguments";
+ return scalar shift @ARGV;
+}
+
sub cmd_help () {
print $helpmsg or die $!;
exit 0;
@@ -346,6 +474,24 @@ sub access_cfg (@) {
return $value;
}
+sub string_to_ssh ($) {
+ my ($spec) = @_;
+ if ($spec =~ m/\s/) {
+ return qw(sh -ec), 'exec '.$spec.' "$@"', 'x';
+ } else {
+ return ($spec);
+ }
+}
+
+sub access_cfg_ssh () {
+ my $gitssh = access_cfg('ssh', 'RETURN-UNDEF');
+ if (!defined $gitssh) {
+ return @ssh;
+ } else {
+ return string_to_ssh $gitssh;
+ }
+}
+
sub access_someuserhost ($) {
my ($some) = @_;
my $user = access_cfg("$some-user",'username');
@@ -437,7 +583,7 @@ sub archive_query_sshdakls ($$) {
my ($proto,$data) = @_;
$data =~ s/:.*// or badcfg "invalid sshdakls method string \`$data'";
my $dakls = cmdoutput
- access_cfg('ssh'), $data, qw(dak ls -asource),"-s$isuite",$package;
+ access_cfg_ssh, $data, qw(dak ls -asource),"-s$isuite",$package;
return madison_parse($dakls);
}
@@ -445,7 +591,7 @@ sub canonicalise_suite_sshdakls ($$) {
my ($proto,$data) = @_;
$data =~ m/:/ or badcfg "invalid sshdakls method string \`$data'";
my @cmd =
- (access_cfg('ssh'), $`,
+ (access_cfg_ssh, $`,
"set -e; cd $';".
" if test -h $isuite; then readlink $isuite; exit 0; fi;".
" if test -d $isuite; then echo $isuite; exit 0; fi;".
@@ -511,9 +657,9 @@ sub get_archive_dsc () {
next;
}
my $dscfh = new IO::File \$dscdata, '<' or die $!;
- print DEBUG Dumper($dscdata) if $debug>1;
+ printdebug Dumper($dscdata) if $debug>1;
$dsc = parsecontrolfh($dscfh,$dscurl, allow_pgp=>1);
- print DEBUG Dumper($dsc) if $debug>1;
+ printdebug Dumper($dsc) if $debug>1;
my $fmt = getfield $dsc, 'Format';
fail "unsupported source format $fmt, sorry" unless $format_ok{$fmt};
return;
@@ -526,7 +672,7 @@ sub check_for_git () {
my $how = access_cfg('git-check');
if ($how eq 'ssh-cmd') {
my @cmd =
- (access_cfg('ssh'),access_gituserhost(),
+ (access_cfg_ssh, access_gituserhost(),
" set -e; cd ".access_cfg('git-path').";".
" if test -d $package.git; then echo 1; else echo 0; fi");
my $r= cmdoutput @cmd;
@@ -541,7 +687,7 @@ sub create_remote_git_repo () {
my $how = access_cfg('git-create');
if ($how eq 'ssh-cmd') {
runcmd_ordryrun
- (access_cfg('ssh'),access_gituserhost(),
+ (access_cfg_ssh, access_gituserhost(),
"set -e; cd ".access_cfg('git-path').";".
" cp -a _template $package.git");
} else {
@@ -730,7 +876,7 @@ sub ensure_we_have_orig () {
$origurl .= "/$f";
die "$f ?" unless $f =~ m/^${package}_/;
die "$f ?" if $f =~ m#/#;
- runcmd_ordryrun shell_cmd 'cd ..', @dget,'--',$origurl;
+ runcmd_ordryrun_local shell_cmd 'cd ..', @dget,'--',$origurl;
}
}
@@ -751,7 +897,7 @@ sub is_fast_fwd ($$) {
}
sub git_fetch_us () {
- runcmd_ordryrun @git, qw(fetch),access_giturl(),fetchspec();
+ runcmd_ordryrun_local @git, qw(fetch),access_giturl(),fetchspec();
}
sub fetch_from_archive () {
@@ -785,7 +931,7 @@ sub fetch_from_archive () {
} else {
die "$lrref_fn $!";
}
- print DEBUG "previous reference hash=$lastpush_hash\n";
+ printdebug "previous reference hash=$lastpush_hash\n";
my $hash;
if (defined $dsc_hash) {
fail "missing git history even though dsc has hash -".
@@ -819,7 +965,7 @@ Package not found in the archive, but has allegedly been pushed using dgit.
$later_warning_msg
END
} else {
- print DEBUG "nothing found!\n";
+ printdebug "nothing found!\n";
if (defined $skew_warning_vsn) {
print STDERR <$clogf",
@git, qw(cat-file blob), "$hash:debian/changelog";
my $gotclogp = parsechangelog("-l$clogf");
my $got_vsn = getfield $gotclogp, 'Version';
- print DEBUG "SKEW CHECK GOT $got_vsn\n";
+ printdebug "SKEW CHECK GOT $got_vsn\n";
if (version_compare_string($got_vsn, $skew_warning_vsn) < 0) {
print STDERR < 1;
mkdir $dstdir or die "$dstdir $!";
chdir "$dstdir" or die "$dstdir $!";
runcmd @git, qw(init -q);
@@ -882,7 +1028,7 @@ sub clone ($) {
if (check_for_git()) {
progress "fetching existing git history";
git_fetch_us();
- runcmd_ordryrun @git, qw(fetch origin);
+ runcmd_ordryrun_local @git, qw(fetch origin);
} else {
progress "starting new git history";
}
@@ -901,7 +1047,7 @@ sub fetch () {
sub pull () {
fetch();
- runcmd_ordryrun @git, qw(merge -m),"Merge from $csuite [dgit]",
+ runcmd_ordryrun_local @git, qw(merge -m),"Merge from $csuite [dgit]",
lrref();
printdone "fetched to ".lrref()." and merged into HEAD";
}
@@ -909,7 +1055,7 @@ sub pull () {
sub check_not_dirty () {
return if $ignoredirty;
my @cmd = (@git, qw(diff --quiet HEAD));
- printcmd(\*DEBUG,"+",@cmd) if $debug>0;
+ printcmd(\*DEBUG,$debugprefix."+",@cmd) if $debug>0;
$!=0; $?=0; system @cmd;
return if !$! && !$?;
if (!$! && $?==256) {
@@ -922,25 +1068,20 @@ sub check_not_dirty () {
sub commit_quilty_patch () {
my $output = cmdoutput @git, qw(status --porcelain);
my %adds;
- my $bad=0;
foreach my $l (split /\n/, $output) {
next unless $l =~ m/\S/;
if ($l =~ m{^(?:\?\?| M) (.pc|debian/patches)}) {
$adds{$1}++;
- } else {
- print STDERR "git status: $l\n";
- $bad++;
}
}
- fail "unexpected output from git status (is tree clean?)" if $bad;
if (!%adds) {
progress "nothing quilty to commit, ok.";
return;
}
- runcmd_ordryrun @git, qw(add), sort keys %adds;
+ runcmd_ordryrun_local @git, qw(add), sort keys %adds;
my $m = "Commit Debian 3.0 (quilt) metadata";
progress "$m";
- runcmd_ordryrun @git, qw(commit -m), $m;
+ runcmd_ordryrun_local @git, qw(commit -m), $m;
}
sub madformat ($) {
@@ -970,7 +1111,7 @@ sub push_parse_changelog ($) {
return ($clogp, $cversion, $tag, $dscfn);
}
-sub push_parse_dsc ($$) {
+sub push_parse_dsc ($$$) {
my ($dscfn,$dscfnwhat, $cversion) = @_;
$dsc = parsecontrol($dscfn,$dscfnwhat);
my $dversion = getfield $dsc, 'Version';
@@ -996,6 +1137,8 @@ sub push_mktag ($$$$$$$$) {
" does not match changelog \`$clogp->{$field}'";
}
+ my $cversion = getfield $clogp, 'Version';
+
# We make the git tag by hand because (a) that makes it easier
# to control the "tagger" (b) we can do remote signing
my $authline = clogp_authline $clogp;
@@ -1030,8 +1173,19 @@ END
return ($tagobjfn);
}
+sub sign_changes ($) {
+ my ($changesfile) = @_;
+ if ($sign) {
+ my @debsign_cmd = @debsign;
+ push @debsign_cmd, "-k$keyid" if defined $keyid;
+ push @debsign_cmd, "-p$gpg[0]" if $gpg[0] ne 'gpg';
+ push @debsign_cmd, $changesfile;
+ runcmd_ordryrun @debsign_cmd;
+ }
+}
+
sub dopush () {
- print DEBUG "actually entering push\n";
+ printdebug "actually entering push\n";
prep_ud();
my $clogpfn = ".git/dgit/changelog.822.tmp";
@@ -1051,7 +1205,7 @@ sub dopush () {
push_parse_dsc("../$dscfn", $dscfn, $cversion);
my $format = getfield $dsc, 'Format';
- print DEBUG "format $format\n";
+ printdebug "format $format\n";
if (madformat($format)) {
commit_quilty_patch();
}
@@ -1061,8 +1215,8 @@ sub dopush () {
runcmd qw(dpkg-source -x --), "../../../../$dscfn";
my ($tree,$dir) = mktree_in_ud_from_only_subdir();
chdir '../../../..' or die $!;
- printcmd \*DEBUG,"+",@_;
my @diffcmd = (@git, qw(diff --exit-code), $tree);
+ printcmd \*DEBUG,$debugprefix."+",@diffcmd;
$!=0; $?=0;
if (system @diffcmd) {
if ($! && $?==256) {
@@ -1095,7 +1249,7 @@ sub dopush () {
}
}
- responder_send_file('changes',$changesfn);
+ responder_send_file('changes',$changesfile);
my $tfn = sub { ".git/dgit/tag$_[0]"; };
my ($tagobjfn) =
@@ -1108,7 +1262,7 @@ sub dopush () {
my $tag_obj_hash = cmdoutput @git, qw(hash-object -w -t tag), $tagobjfn;
runcmd_ordryrun @git, qw(verify-tag), $tag_obj_hash;
- runcmd_ordryrun @git, qw(update-ref), "refs/tags/$tag", $tag_obj_hash;
+ runcmd_ordryrun_local @git, qw(update-ref), "refs/tags/$tag", $tag_obj_hash;
runcmd_ordryrun @git, qw(tag -v --), $tag;
if (!check_for_git()) {
@@ -1118,26 +1272,22 @@ sub dopush () {
runcmd_ordryrun @git, qw(update-ref -m), 'dgit push', lrref(), 'HEAD';
if (!$we_are_responder) {
- if (!$dryrun) {
+ if ($dryrun <= 1) {
rename "../$dscfn.tmp","../$dscfn" or die "$dscfn $!";
} else {
progress "[new .dsc left in $dscfn.tmp]";
}
}
- if ($sign) {
- if ($we_are_responder) {
- my $dryrunsuffix = $dryrun ? ".tmp" : "";
- responder_receive_files('signed-changes-dsc',
- "$changesfile$dryrunsuffix",
- "../$dscfn$dryrunsuffix");
- } else {
- my @debsign_cmd = @debsign;
- push @debsign_cmd, "-k$keyid" if defined $keyid;
- push @debsign_cmd, $changesfile;
- runcmd_ordryrun @debsign_cmd;
- }
+ if ($we_are_responder) {
+ my $dryrunsuffix = $dryrun > 1 ? ".tmp" : "";
+ responder_receive_files('signed-dsc-changes',
+ "../$dscfn$dryrunsuffix",
+ "$changesfile$dryrunsuffix");
+ } else {
+ sign_changes $changesfile;
}
+
runcmd_ordryrun @git, qw(push),access_giturl(),"refs/tags/$tag";
my $host = access_cfg('upload-host','RETURN-UNDEF');
my @hostarg = defined($host) ? ($host,) : ();
@@ -1249,6 +1399,8 @@ sub cmd_push {
dopush();
}
+#---------- remote commands' implementation ----------
+
sub cmd_remote_push_responder {
my ($nrargs) = shift @ARGV;
my (@rargs) = @ARGV[0..$nrargs-1];
@@ -1256,11 +1408,137 @@ sub cmd_remote_push_responder {
die unless @rargs;
my ($dir) = @rargs;
chdir $dir or die "$dir: $!";
- $we_are_remote = 1;
+ $we_are_responder = 1;
+ $debugprefix = ' ';
+ autoflush STDOUT 1;
responder_send_command("dgit-remote-push-ready");
&cmd_push;
}
+our $i_tmp;
+
+sub i_cleanup {
+ local ($@);
+ return unless defined $i_tmp;
+ chdir "/" or die $!;
+ eval { rmtree $i_tmp; };
+}
+
+sub i_method {
+ my ($base,$selector,@args) = @_;
+ $selector =~ s/\-/_/g;
+ { no strict qw(refs); &{"${base}_${selector}"}(@args); }
+}
+
+sub cmd_rpush {
+ my $host = nextarg;
+ my $dir;
+ if ($host =~ m/^((?:[^][]|\[[^][]*\])*)\:/) {
+ $host = $1;
+ $dir = $'; #';
+ } else {
+ $dir = nextarg;
+ }
+ $dir =~ s{^-}{./-};
+ my @rargs = ($dir);
+ my @rdgit;
+ push @rdgit, @dgit;
+ push @rdgit, @ropts;
+ push @rdgit, qw(remote-push-responder), (scalar @rargs), @rargs;
+ push @rdgit, @ARGV;
+ my @cmd = (@ssh, $host, shellquote @rdgit);
+ printcmd \*DEBUG,$debugprefix."+",@cmd;
+ eval {
+ $i_tmp = tempdir();
+ my $pid = open2(\*RO, \*RI, @cmd);
+ chdir $i_tmp or die "$i_tmp $!";
+ initiator_expect { m/^dgit-remote-push-ready/ };
+ for (;;) {
+ my ($icmd,$iargs) = initiator_expect {
+ m/^(\S+)(?: (.*))?$/;
+ ($1,$2);
+ };
+ i_method "i_resp", $icmd, $iargs;
+ }
+ };
+ i_cleanup();
+ die $@;
+}
+
+sub i_resp_progress ($) {
+ my ($rhs) = @_;
+ my $msg = protocol_read_bytes \*RO, $rhs;
+ progress $msg;
+}
+
+sub i_resp_complete {
+ i_cleanup();
+ exit 0;
+}
+
+sub i_resp_file ($) {
+ my ($keyword) = @_;
+ my $localname = i_method "i_localname", $keyword;
+ my $localpath = "$i_tmp/$localname";
+ stat $localpath and badproto \*RO, "file $keyword ($localpath) twice";
+ protocol_receive_file \*RO, $localpath;
+}
+
+our %i_param;
+
+sub i_param ($) {
+ $_[0] =~ m/^(\S+) (.*)$/;
+ $i_param{$1} = $2;
+}
+
+our %i_wanted;
+
+sub i_resp_want ($) {
+ my ($keyword) = @_;
+ die "$keyword ?" if $i_wanted{$keyword}++;
+ my @localpaths = i_method "i_want", $keyword;
+ printdebug "]] $keyword @localpaths\n";
+ foreach my $localpath (@localpaths) {
+ protocol_send_file \*RI, $localpath;
+ }
+ print RI "end-files\n" or die $!;
+}
+
+our ($i_clogp, $i_version, $i_tag, $i_dscfn);
+
+sub i_localname_parsed_changelog { return "remote-changelog.822"; }
+sub i_localname_changes { return "remote.changes"; }
+sub i_localname_dsc {
+ ($i_clogp, $i_version, $i_tag, $i_dscfn) =
+ push_parse_changelog 'remote-changelog.822';
+ die if $i_dscfn =~ m#/|^\W#;
+ return $i_dscfn;
+}
+
+sub i_want_signed_tag {
+ defined $i_param{'head'} && defined $i_dscfn
+ or badproto \*RO, "sequencing error";
+ my $head = $i_param{'head'};
+ die if $head =~ m/[^0-9a-f]/ || $head !~ m/^../;
+
+ push_parse_dsc $i_dscfn, 'remote dsc',
+
+ push_mktag $head, $i_clogp, $i_tag,
+ $dsc, $i_dscfn,
+ 'remote.changes', 'remote changes',
+ 'tag.tag';
+
+ return 'tag.tag';
+}
+
+sub i_want_signed_dsc_changes {
+ rename "$i_dscfn.tmp","$i_dscfn" or die "$i_dscfn $!";
+ sign_changes 'remote.changes';
+ return ($i_dscfn, 'remote.changes');
+}
+
+#---------- building etc. ----------
+
our $version;
our $sourcechanges;
our $dscfn;
@@ -1305,7 +1583,7 @@ END
local $ENV{'EDITOR'} = cmdoutput qw(realpath --), $0;
local $ENV{'VISUAL'} = $ENV{'EDITOR'};
local $ENV{$fakeeditorenv} = cmdoutput qw(realpath --), $descfn;
- runcmd_ordryrun @dpkgsource, qw(--commit .), $patchname;
+ runcmd_ordryrun_local @dpkgsource, qw(--commit .), $patchname;
}
if (!open P, '>>', ".pc/applied-patches") {
@@ -1350,7 +1628,7 @@ sub cmd_build {
badusage "dgit build implies --clean=dpkg-source"
if $cleanmode ne 'dpkg-source';
build_prep();
- runcmd_ordryrun @dpkgbuildpackage, qw(-us -uc), changesopts(), @ARGV;
+ runcmd_ordryrun_local @dpkgbuildpackage, qw(-us -uc), changesopts(), @ARGV;
printdone "build successful\n";
}
@@ -1366,7 +1644,7 @@ sub cmd_git_build {
push @cmd, "--git-debian-branch=".lbranch();
}
push @cmd, changesopts();
- runcmd_ordryrun @cmd, @ARGV;
+ runcmd_ordryrun_local @cmd, @ARGV;
printdone "build successful\n";
}
@@ -1375,10 +1653,11 @@ sub build_source {
$sourcechanges = "${package}_".(stripepoch $version)."_source.changes";
$dscfn = dscfn($version);
if ($cleanmode eq 'dpkg-source') {
- runcmd_ordryrun (@dpkgbuildpackage, qw(-us -uc -S)), changesopts();
+ runcmd_ordryrun_local (@dpkgbuildpackage, qw(-us -uc -S)),
+ changesopts();
} else {
if ($cleanmode eq 'git') {
- runcmd_ordryrun @git, qw(clean -xdf);
+ runcmd_ordryrun_local @git, qw(clean -xdf);
} elsif ($cleanmode eq 'none') {
} else {
die "$cleanmode ?";
@@ -1386,9 +1665,9 @@ sub build_source {
my $pwd = cmdoutput qw(env - pwd);
my $leafdir = basename $pwd;
chdir ".." or die $!;
- runcmd_ordryrun @dpkgsource, qw(-b --), $leafdir;
+ runcmd_ordryrun_local @dpkgsource, qw(-b --), $leafdir;
chdir $pwd or die $!;
- runcmd_ordryrun qw(sh -ec),
+ runcmd_ordryrun_local qw(sh -ec),
'exec >$1; shift; exec "$@"','x',
"../$sourcechanges",
@dpkggenchanges, qw(-S), changesopts();
@@ -1405,7 +1684,7 @@ sub cmd_sbuild {
build_source();
chdir ".." or die $!;
my $pat = "${package}_".(stripepoch $version)."_*.changes";
- if (!$dryrun) {
+ if ($dryrun <= 1) {
stat $dscfn or fail "$dscfn (in parent directory): $!";
stat $sourcechanges or fail "$sourcechanges (in parent directory): $!";
foreach my $cf (glob $pat) {
@@ -1416,7 +1695,7 @@ sub cmd_sbuild {
runcmd_ordryrun @sbuild, @ARGV, qw(-d), $isuite, $dscfn;
runcmd_ordryrun @mergechanges, glob $pat;
my $multichanges = "${package}_".(stripepoch $version)."_multi.changes";
- if (!$dryrun) {
+ if ($dryrun <= 1) {
stat $multichanges or fail "$multichanges: $!";
}
printdone "build successful, results in $multichanges\n" or die $!;
@@ -1429,6 +1708,8 @@ sub cmd_quilt_fixup {
build_maybe_quilt_fixup();
}
+#---------- argument parsing and main program ----------
+
sub cmd_version {
print "dgit version $our_version\n" or die $!;
exit 0;
@@ -1436,39 +1717,60 @@ sub cmd_version {
sub parseopts () {
my $om;
+
+ if (defined $ENV{'DGIT_SSH'}) {
+ @ssh = string_to_ssh $ENV{'DGIT_SSH'};
+ } elsif (defined $ENV{'GIT_SSH'}) {
+ @ssh = ($ENV{'GIT_SSH'});
+ }
+
while (@ARGV) {
last unless $ARGV[0] =~ m/^-/;
$_ = shift @ARGV;
last if m/^--?$/;
if (m/^--/) {
if (m/^--dry-run$/) {
+ push @ropts, $_;
+ $dryrun=2;
+ } elsif (m/^--damp-run$/) {
+ push @ropts, $_;
$dryrun=1;
} elsif (m/^--no-sign$/) {
+ push @ropts, $_;
$sign=0;
} elsif (m/^--help$/) {
cmd_help();
} elsif (m/^--version$/) {
cmd_version();
} elsif (m/^--new$/) {
+ push @ropts, $_;
$new_package=1;
} elsif (m/^--(\w+)=(.*)/s &&
($om = $opts_opt_map{$1}) &&
length $om->[0]) {
+ push @ropts, $_;
$om->[0] = $2;
} elsif (m/^--(\w+):(.*)/s &&
+ !$opts_opt_cmdonly{$1} &&
($om = $opts_opt_map{$1})) {
+ push @ropts, $_;
push @$om, $2;
} elsif (m/^--existing-package=(.*)/s) {
+ push @ropts, $_;
$existing_package = $1;
} elsif (m/^--distro=(.*)/s) {
+ push @ropts, $_;
$idistro = $1;
} elsif (m/^--clean=(dpkg-source|git|none)$/s) {
+ push @ropts, $_;
$cleanmode = $1;
} elsif (m/^--clean=(.*)$/s) {
badusage "unknown cleaning mode \`$1'";
} elsif (m/^--ignore-dirty$/s) {
+ push @ropts, $_;
$ignoredirty = 1;
} elsif (m/^--no-quilt-fixup$/s) {
+ push @ropts, $_;
$noquilt = 1;
} else {
badusage "unknown long option \`$_'";
@@ -1476,30 +1778,44 @@ sub parseopts () {
} else {
while (m/^-./s) {
if (s/^-n/-/) {
+ push @ropts, $&;
+ $dryrun=2;
+ } elsif (s/^-L/-/) {
+ push @ropts, $&;
$dryrun=1;
} elsif (s/^-h/-/) {
cmd_help();
} elsif (s/^-D/-/) {
+ push @ropts, $&;
open DEBUG, ">&STDERR" or die $!;
+ autoflush DEBUG 1;
$debug++;
} elsif (s/^-N/-/) {
+ push @ropts, $&;
$new_package=1;
} elsif (m/^-[vm]/) {
+ push @ropts, $&;
push @changesopts, $_;
$_ = '';
} elsif (s/^-c(.*=.*)//s) {
+ push @ropts, $&;
push @git, '-c', $1;
} elsif (s/^-d(.*)//s) {
+ push @ropts, $&;
$idistro = $1;
} elsif (s/^-C(.*)//s) {
+ push @ropts, $&;
$changesfile = $1;
} elsif (s/^-k(.*)//s) {
$keyid=$1;
} elsif (s/^-wn//s) {
+ push @ropts, $&;
$cleanmode = 'none';
} elsif (s/^-wg//s) {
+ push @ropts, $&;
$cleanmode = 'git';
} elsif (s/^-wd//s) {
+ push @ropts, $&;
$cleanmode = 'dpkg-source';
} else {
badusage "unknown short option \`$_'";
@@ -1516,7 +1832,8 @@ if ($ENV{$fakeeditorenv}) {
delete $ENV{'DGET_UNPACK'};
parseopts();
-print STDERR "DRY RUN ONLY\n" if $dryrun;
+print STDERR "DRY RUN ONLY\n" if $dryrun > 1;
+print STDERR "DAMP RUN - WILL MAKE LOCAL (UNSIGNED) CHANGES\n" if $dryrun == 1;
if (!@ARGV) {
print STDERR $helpmsg or die $!;
exit 8;