summaryrefslogtreecommitdiff
path: root/dgit
diff options
context:
space:
mode:
Diffstat (limited to 'dgit')
-rwxr-xr-xdgit876
1 files changed, 664 insertions, 212 deletions
diff --git a/dgit b/dgit
index 9b83159..97962f6 100755
--- a/dgit
+++ b/dgit
@@ -60,6 +60,7 @@ $SIG{INT} = 'DEFAULT'; # work around #932841
our @rpushprotovsn_support = qw(6 5 4); # Reverse order!
our $protovsn;
+our $rpush_verb; # "push" or "push-source"
our $cmd;
our $subcommand;
@@ -82,13 +83,14 @@ our $existing_package = 'dpkg';
our $cleanmode;
our $changes_since_version;
our $rmchanges;
+our $keep_playground;
our $overwrite_version; # undef: not specified; '': check changelog
our $quilt_mode;
our $quilt_upstream_commitish;
our $quilt_upstream_commitish_used;
our $quilt_upstream_commitish_message;
our $quilt_options_re = 'gbp|dpm|baredebian(?:\+tarball|\+git)?';
-our $quilt_modes_re = "linear|smash|auto|nofix|nocheck|unapplied|$quilt_options_re";
+our $quilt_modes_re = "linear|smash|try-linear|auto|single|nofix|nocheck|unapplied|$quilt_options_re";
our $splitview_mode;
our $splitview_modes_re = qr{auto|always|never};
our $dodep14tag;
@@ -102,8 +104,9 @@ our $chase_dsc_distro=1;
our %forceopts = map { $_=>0 }
qw(unrepresentable unsupported-source-format
dsc-changes-mismatch changes-origs-exactly
- uploading-binaries uploading-source-only
+ uploading-binaries uploading-old-version uploading-source-only
reusing-version
+ push-tainted
import-gitapply-absurd
import-gitapply-no-absurd
import-dsc-with-dgit-field);
@@ -127,7 +130,7 @@ our (@curl) = (qw(curl --proto-redir), '-all,http,https', qw(-L));
our (@dput) = qw(dput);
our (@debsign) = qw(debsign);
our (@gpg) = qw(gpg);
-our (@sbuild) = (qw(sbuild --no-source));
+our (@sbuild) = (qw(sbuild --no-source --no-source-only-changes));
our (@ssh) = 'ssh';
our (@dgit) = qw(dgit);
our (@git_debrebase) = qw(git-debrebase);
@@ -189,20 +192,20 @@ our $do_split_brain;
# (currently, split brain only implemented iff
# madformat_wantfixup && quiltmode_splitting)
#
-# source format sane `3.0 (quilt)'
-# madformat_wantfixup()
-#
-# quilt mode normal quiltmode
-# (eg linear) _splitbrain
-#
-# ------------ ------------------------------------------------
-#
-# no split no q cache no q cache forbidden,
-# brain PM on master q fixup on master prevented
-# !do_split_brain() PM on master
-#
-# split brain no q cache q fixup cached, to dgit view
-# PM in dgit view PM in dgit view
+# source format | sane `3.0 (quilt)'
+# | madformat_wantfixup()
+# |
+# quilt mode | normal quiltmode
+# | (eg linear) _splitbrain
+# |
+# -------------------+-------------------------------------------------
+# |
+# no split | no q cache no q cache forbidden,
+# brain | PM on master q fixup on master prevented
+# !do_split_brain() | PM on master
+# |
+# split brain | no q cache q fixup cached, to dgit view
+# | PM in dgit view PM in dgit view
#
# PM = pseudomerge to make ff, due to overwrite (or split view)
# "no q cache" = do not record in cache on build, do not check cache
@@ -292,12 +295,15 @@ sub no_such_package () {
sub deliberately ($) {
my ($enquiry) = @_;
- return !!grep { $_ eq "--deliberately-$enquiry" } @deliberatelies;
+ return !!grep {
+ $_ eq "--deliberately-$enquiry" or
+ $_ eq "--deliberately-TEST-dgit-only-$enquiry"
+ } @deliberatelies;
}
sub deliberately_not_fast_forward () {
foreach (qw(not-fast-forward fresh-repo)) {
- return 1 if deliberately($_) || deliberately("TEST-dgit-only-$_");
+ return 1 if deliberately($_);
}
}
@@ -324,6 +330,16 @@ sub gbp_pq {
return opts_opt_multi_cmd [], @gbp_pq;
}
+sub gbp_pq_pc_aside (&) {
+ my ($f) = @_;
+ my $undo = rename ".pc", "../pc-aside";
+ confess "$!" unless $undo || $!==ENOENT;
+ $f->();
+ if ($undo) {
+ rename "../pc-aside", ".pc", or confess $!;
+ }
+}
+
sub dgit_privdir () {
our $dgit_privdir_made //= ensure_a_playground 'dgit';
}
@@ -433,9 +449,13 @@ sub branch_is_gdr ($) {
!defined git_cat_file "$walk~:debian" and
!quiltify_trees_differ "$walk~", $walk
) {
- # (gdr classification of parent: BreakwaterStart
- printdebug "branch_is_gdr $walk unmarked BreakwaterStart YES\n";
- return 1;
+ # (gdr classification of parent: BreakwaterStart We cannot
+ # process this using git-debrebase, because this can misrecognise
+ # other kinds of branch contents, eg as in #1025451. Not doing
+ # this via gdr is OK, because the normal quilt linearisation will
+ # do - doing it via gdr is just an optimisation.
+ printdebug "branch_is_gdr $walk unmarked BreakwaterStart NO\n";
+ return 0;
}
# (gdr classification: Upstream Packaging Mixed Changelog)
printdebug "branch_is_gdr $walk plain\n"
@@ -447,8 +467,12 @@ sub branch_is_gdr ($) {
#---------- remote protocol support, common ----------
# remote push initiator/responder protocol:
-# $ dgit remote-push-build-host <n-rargs> <rargs>... <push-args>...
+# $ dgit remote-push-source-build-host <n-rargs> <rargs>... <push-args>...
# where <rargs> is <push-host-dir> <supported-proto-vsn>,... ...
+# < dgit-remote-push-source-ready <actual-proto-vsn>
+#
+# Or for push-built,
+# $ dgit remote-push-build-host <n-rargs> <rargs>... <push-args>...
# < dgit-remote-push-ready <actual-proto-vsn>
#
# occasionally:
@@ -482,6 +506,7 @@ sub branch_is_gdr ($) {
#
# > param buildinfo-filename P_V_X.buildinfo # zero or more times
# > file buildinfo # for buildinfos to sign
+# # not for push-source
#
# > previously REFNAME=OBJNAME # if --deliberately-not-fast-forward
# # goes into tag, for replay prevention
@@ -729,11 +754,13 @@ our %defcfg = ('dgit.default.distro' => 'debian',
'dgit.default.sshpsql-dbname' => 'service=projectb',
'dgit.default.aptget-components' => 'main',
'dgit.default.source-only-uploads' => 'ok',
+ 'dgit.default.policy-query-supported-ssh' => 'unknown',
'dgit.dsc-url-proto-ok.http' => 'true',
'dgit.dsc-url-proto-ok.https' => 'true',
'dgit.dsc-url-proto-ok.git' => 'true',
'dgit.vcs-git.suites', => 'sid', # ;-separated
'dgit.default.dsc-url-proto-ok' => 'false',
+ 'dgit.default.push-subcmd' => 'warn,built',
# 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"
@@ -744,6 +771,7 @@ our %defcfg = ('dgit.default.distro' => 'debian',
'dgit-distro.debian.git-check-suffix' => '/info/refs',
'dgit-distro.debian.new-private-pushers' => 't',
'dgit-distro.debian.source-only-uploads' => 'not-wholly-new',
+ 'dgit-distro.debian.policy-query-supported-ssh' => 'true',
'dgit-distro.debian/push.git-url' => '',
'dgit-distro.debian/push.git-host' => 'push.dgit.debian.org',
'dgit-distro.debian/push.git-user-force' => 'dgit',
@@ -767,8 +795,8 @@ our %defcfg = ('dgit.default.distro' => 'debian',
'dgit-distro.debian.mirror' => 'http://ftp.debian.org/debian/',
'dgit-distro.debian-security.archive-query' => 'aptget:',
'dgit-distro.debian-security.mirror' => 'http://security.debian.org/debian-security/',
- 'dgit-distro.debian-security.aptget-suite-map' => 's#-security$#/updates#',
- 'dgit-distro.debian-security.aptget-suite-rmap' => 's#$#-security#',
+ 'dgit-distro.debian-security.aptget-suite-map' => 's#buster-security$#buster/updates#',
+ 'dgit-distro.debian-security.aptget-suite-rmap' => 's#buster$#buster-security#',
'dgit-distro.debian-security.nominal-distro' => 'debian',
'dgit-distro.debian.backports-quirk' => '(squeeze)-backports*',
'dgit-distro.debian-backports.mirror' => 'http://backports.debian.org/debian-backports/',
@@ -1865,6 +1893,13 @@ sub git_add_write_tree () {
return git_write_tree();
}
+sub git_diff_programmatic (@) {
+ # Ideally we would unset various git.diff config options here,
+ # but there doesn't seem to be a way to *unset*
+ # something on the command line
+ (@git, qw(-c color.ui=never diff --no-ext-diff), @_)
+}
+
sub remove_stray_gits ($) {
my ($what) = @_;
my @gitscmd = qw(find -name .git -prune -print0);
@@ -1895,7 +1930,7 @@ sub mktree_in_ud_from_only_subdir ($;$) {
remove_stray_gits($what);
mktree_in_ud_here();
if (!$raw) {
- my ($format, $fopts) = get_source_format();
+ my $format = get_source_format();
if (madformat($format)) {
rmtree '.pc';
}
@@ -2035,10 +2070,12 @@ sub test_source_only_changes ($) {
foreach my $l (split /\n/, getfield $changes, 'Files') {
$l =~ m/\S+$/ or next;
# \.tar\.[a-z0-9]+ covers orig.tar and the tarballs in native packages
- unless ($& =~ m/(?:\.dsc|\.diff\.gz|$tarball_f_ext_re|_source\.buildinfo)$/) {
- print f_ "purportedly source-only changes polluted by %s\n", $&;
- return 0;
- }
+ $_ = $&;
+ next if m/(?:\.dsc|\.diff\.gz|$tarball_f_ext_re)$/;
+ next if m/_source\.buildinfo$/;
+ print STDERR
+ f_ "purportedly source-only changes polluted by %s\n", $&;
+ return 0;
}
return 1;
}
@@ -2153,8 +2190,11 @@ sub clogp_authline ($) {
# are by now preceded by @ (or "). It seems safer to punt on
# "..." for now rather than attempting to dequote or something.
$author =~ s#,.*##ms unless $author =~ m/"/;
- my $date = cmdoutput qw(date), '+%s %z', qw(-d), getfield($clogp,'Date');
- my $authline = "$author $date";
+ my $date = getfield($clogp,'Date');
+ # try to pass through the changelog entry's timezone offset
+ my $tz = $date =~ m{ ([-+]\d{4})$} ? $1 : " +0000";
+ $date = cmdoutput qw(date), '+%s', qw(-d), $date;
+ my $authline = "$author $date $tz";
$authline =~ m/$git_authline_re/o or
fail f_ "unexpected commit author line format \`%s'".
" (was generated from changelog Maintainer field)",
@@ -2240,11 +2280,50 @@ sub dotdot_bpd_transfer_origs ($$$) {
# checks is_orig_file_of_vsn and if
# calls $wanted->{$leaf} and expects boolish
+ my $dotdot = $maindir;
+ $dotdot =~ s{/[^/]+$}{};
+
+ my %dupes;
+ my $dupe_scan = sub {
+ my ($dir, $why_token) = @_;
+
+ if (!opendir SD, $dir) {
+ return if $! == ENOENT;
+ fail "opendir $why_token ($dir): $!";
+ }
+ while ($!=0, defined(my $leaf = readdir SD)) {
+ next unless is_orig_file_of_vsn $leaf, $upstreamversion;
+ next if $leaf =~ m{$orig_f_sig_re$};
+ next unless $leaf =~ m{\.tar(?:\.\w+)?$};
+ my $base = "$`.tar";
+ push @{ $dupes{$base}{$leaf} }, [$why_token, $dir];
+ }
+ die "$dir; $!" if $!;
+ };
+ $dupe_scan->($dotdot, "..");
+ $dupe_scan->(bpd_abs(), 'build-products-dir') if $buildproductsdir ne '..';
+
+ my $dupes_found = 0;
+ foreach my $base (sort keys %dupes) {
+ my $leaves = $dupes{$base};
+ next if keys(%$leaves) == 1;
+ $dupes_found = 1;
+ print STDERR f_
+ "%s: multiple representations of similar orig %s:\n",
+ $us, $base;
+ foreach my $leaf (keys %$leaves) {
+ foreach my $found (@{ $leaves->{$leaf} }) {
+ print STDERR f_ " %s: in %s (%s)\n",
+ $leaf, @$found;
+ }
+ }
+ }
+ fail __ "Duplicate/inconsistent orig tarballs. Delete the spurious ones."
+ if $dupes_found;
+
return if $buildproductsdir eq '..';
my $warned;
- my $dotdot = $maindir;
- $dotdot =~ s{/[^/]+$}{};
opendir DD, $dotdot or fail "opendir .. ($dotdot): $!";
while ($!=0, defined(my $leaf = readdir DD)) {
{
@@ -2295,6 +2374,55 @@ sub dotdot_bpd_transfer_origs ($$$) {
closedir DD;
}
+sub import_r1authline ($$) {
+ my ($clogp_r, $upstreamv) = @_;
+ my $r1clogp;
+
+ my @clogcmd = qw(dpkg-parsechangelog --format rfc822 --all);
+
+ printdebug "import clog search...\n";
+ parsechangelog_loop \@clogcmd, (__ "package changelog"), sub {
+ my ($thisstanza, $desc) = @_;
+ no warnings qw(exiting);
+
+ $$clogp_r //= $thisstanza;
+
+ printdebug "import clog $thisstanza->{version} $desc...\n";
+
+ # We look for the first (most recent) changelog entry whose
+ # version number is lower than the upstream version of this
+ # package. Then the last (least recent) previous changelog
+ # entry is treated as the one which introduced this upstream
+ # version and used for the synthetic commits for the upstream
+ # tarballs.
+
+ # One might think that a more sophisticated algorithm would be
+ # necessary. But: we do not want to scan the whole changelog
+ # file. Stopping when we see an earlier version, which
+ # necessarily then is an earlier upstream version, is the only
+ # realistic way to do that. Then, either the earliest
+ # changelog entry we have seen so far is indeed the earliest
+ # upload of this upstream version; or there are only changelog
+ # entries relating to later upstream versions (which is not
+ # possible unless the changelog and .dsc disagree about the
+ # version). Then it remains to choose between the physically
+ # last entry in the file, and the one with the lowest version
+ # number. If these are not the same, we guess that the
+ # versions were created in a non-monotonic order rather than
+ # that the changelog entries have been misordered.
+
+ printdebug "import clog $thisstanza->{version} vs $upstreamv...\n";
+
+ last if version_compare($thisstanza->{version}, $upstreamv) < 0;
+ $r1clogp = $thisstanza;
+
+ printdebug "import clog $r1clogp->{version} becomes r1\n";
+ };
+
+ $r1clogp //= $$clogp_r; # maybe there's only one entry;
+ return clogp_authline $r1clogp;
+}
+
sub import_tarball_tartrees ($$) {
my ($upstreamv, $dfi) = @_;
# cwd should be the playground
@@ -2415,65 +2543,33 @@ sub import_tarball_commits ($$) {
my $any_orig = grep { $_->{Orig} } @$tartrees;
- my @clogcmd = qw(dpkg-parsechangelog --format rfc822 --all);
my $clogp;
- my $r1clogp;
-
- printdebug "import clog search...\n";
- parsechangelog_loop \@clogcmd, (__ "package changelog"), sub {
- my ($thisstanza, $desc) = @_;
- no warnings qw(exiting);
-
- $clogp //= $thisstanza;
-
- printdebug "import clog $thisstanza->{version} $desc...\n";
-
- last if !$any_orig; # we don't need $r1clogp
-
- # We look for the first (most recent) changelog entry whose
- # version number is lower than the upstream version of this
- # package. Then the last (least recent) previous changelog
- # entry is treated as the one which introduced this upstream
- # version and used for the synthetic commits for the upstream
- # tarballs.
-
- # One might think that a more sophisticated algorithm would be
- # necessary. But: we do not want to scan the whole changelog
- # file. Stopping when we see an earlier version, which
- # necessarily then is an earlier upstream version, is the only
- # realistic way to do that. Then, either the earliest
- # changelog entry we have seen so far is indeed the earliest
- # upload of this upstream version; or there are only changelog
- # entries relating to later upstream versions (which is not
- # possible unless the changelog and .dsc disagree about the
- # version). Then it remains to choose between the physically
- # last entry in the file, and the one with the lowest version
- # number. If these are not the same, we guess that the
- # versions were created in a non-monotonic order rather than
- # that the changelog entries have been misordered.
-
- printdebug "import clog $thisstanza->{version} vs $upstreamv...\n";
-
- last if version_compare($thisstanza->{version}, $upstreamv) < 0;
- $r1clogp = $thisstanza;
-
- printdebug "import clog $r1clogp->{version} becomes r1\n";
- };
-
- $clogp or fail __ "package changelog has no entries!";
-
+ my $r1authline;
+ if ($any_orig) {
+ if (!eval {
+ local $failmsg_prefix = ' ';
+ $r1authline = import_r1authline(\$clogp, $upstreamv);
+ $clogp or fail __ "package changelog has no entries!";
+ 1;
+ }) {
+ chomp $@;
+ print STDERR f_ <<END, $upstreamv, $@;
+warning: unable to find/parse changelog entry for first import of %s:
+%s
+END
+ }
+ }
+ # Runs if $any_orig clause didn't set $clogp
+ $clogp //= parsechangelog();
my $authline = clogp_authline $clogp;
+ # Runs if $any_orig clause didn't set $r1authline
+ $r1authline //= $authline;
+
my $changes = getfield $clogp, 'Changes';
$changes =~ s/^\n//; # Changes: \n
my $cversion = getfield $clogp, 'Version';
- my $r1authline;
if (@$tartrees) {
- $r1clogp //= $clogp; # maybe there's only one entry;
- $r1authline = clogp_authline $r1clogp;
- # Strictly, r1authline might now be wrong if it's going to be
- # unused because !$any_orig. Whatever.
-
printdebug "import tartrees authline $authline\n";
printdebug "import tartrees r1authline $r1authline\n";
@@ -2545,7 +2641,9 @@ sub generate_commits_from_dsc () {
printdebug "considering saving $f: ";
- if (rename_link_xf 1, $f, $upper_f) {
+ if (!act_local()) {
+ printdebug "no - dry run.\n";
+ } elsif (rename_link_xf 1, $f, $upper_f) {
printdebug "linked.\n";
} elsif ((printdebug "($@) "),
$! != EEXIST) {
@@ -2676,14 +2774,57 @@ END
local $ENV{DGIT_ABSURD_DEBUG} = $debuglevel if $use_absurd;
local $ENV{PATH} = $path if $use_absurd;
+ if ($use_absurd) {
+ # We filter the series file, to contain only things
+ # that are actually requests to apply a patch.
+ #
+ # This is needed because sometimes a series file can
+ # contain strange things that gbp pq cannot cope with.
+ # Eg, form feeds. See #1030093.
+ rename "debian/patches/series", "../series.orig"
+ or confess "$!";
+ open OS, "../series.orig" or confess $!;
+ open NS, ">debian/patches/series" or confess $!;
+ while (<OS>) {
+ s/\#.*//;
+ s/^\s+//;
+ s/\s+$//;
+ next unless m/\S/;
+ print NS "$_\n" or confess $!;
+ }
+ confess $! if OS->error;
+ close NS or confess $!;
+ runcmd @git, qw(add debian/patches/series);
+ # This commit is spurious, but we must commit for gbp
+ # pq to work. We filter it out of the branch later.
+ runcmd @git, qw(commit --quiet --allow-empty -m), <<END;
+INTERNAL commit to launder series file
+
+This commit should not escape into a public branch!
+If you see it, this is due to a bug in dgit.
+
+[dgit ($our_version) INTERNAL-quilt-fixup-series]
+END
+ }
+
my @showcmd = (gbp_pq, qw(import));
my @realcmd = shell_cmd
- 'exec >/dev/null 2>>../../gbp-pq-output', @showcmd;
- debugcmd "+",@realcmd;
- if (system @realcmd) {
- die f_ "%s failed: %s\n",
- +(shellquote @showcmd),
- failedcmd_waitstatus();
+ 'exec >/dev/null 2>../../gbp-pq-output', @showcmd;
+ gbp_pq_pc_aside(sub {
+ debugcmd "+",@realcmd;
+ if (system @realcmd) {
+ die f_ "%s failed: %s\n",
+ +(shellquote @showcmd),
+ failedcmd_waitstatus();
+ }
+ });
+
+ if ($use_absurd) {
+ # Perhaps we should be using git-filter-branch,
+ # but that's really considerably more awkward.
+ runcmd_quieten
+ @git, qw(rebase --keep-empty --allow-empty-message
+ --onto unpa~1 unpa);
}
my $gapplied = git_rev_parse('HEAD');
@@ -2699,7 +2840,7 @@ END
if ($use_absurd) {
File::Copy::copy("../../absurd-apply-warnings", \*STDERR)
- or die $!;
+ or confess $!;
}
};
last unless $@;
@@ -2968,10 +3109,10 @@ END
my $want = $wantr{$rrefname};
next if $got eq $want;
if (!defined $objgot{$want}) {
- fail __ <<END unless act_local();
+ fail f_ <<END, $rrefname unless act_local();
--dry-run specified but we actually wanted the results of git fetch,
so this is not going to work. Try running dgit fetch first,
-or using --damp-run instead of --dry-run.
+or using --damp-run instead of --dry-run. (Wanted: %s.)
END
print STDERR f_ <<END, $lrefname, $want;
warning: git ls-remote suggests we want %s
@@ -3367,7 +3508,7 @@ END
if ($lastfetch_mergeinput &&
!version_compare( (mergeinfo_version $lastfetch_mergeinput),
(mergeinfo_version $mergeinputs[0]) )) {
- @mergeinputs = ($lastfetch_mergeinput);
+ @mergeinputs = ($lastfetch_mergeinput);
}
} elsif ($lastpush_hash) {
# only in git, not in the archive yet
@@ -4034,19 +4175,13 @@ END
}
sub get_source_format () {
- my %options;
+ my @options;
if (open F, "debian/source/options") {
while (<F>) {
next if m/^\s*\#/;
next unless m/\S/;
s/\s+$//; # ignore missing final newline
- if (m/\s*\#\s*/) {
- my ($k, $v) = ($`, $'); #');
- $v =~ s/^"(.*)"$/$1/;
- $options{$k} = $v;
- } else {
- $options{$_} = 1;
- }
+ push @options, $_;
}
F->error and confess "$!";
close F;
@@ -4062,7 +4197,7 @@ sub get_source_format () {
F->error and confess "$!";
close F;
chomp;
- return ($_, \%options);
+ return wantarray ? ($_, \@options) : $_;
}
sub madformat_wantfixup ($) {
@@ -4148,9 +4283,10 @@ sub pseudomerge_version_check ($$) {
progress f_
"Checking package changelog for archive version %s ...", $v;
my $cd;
+ my $vclogp;
eval {
my @xa = ("-f$v", "-t$v");
- my $vclogp = parsechangelog @xa;
+ $vclogp = parsechangelog @xa;
my $gf = sub {
my ($fn) = @_;
[ (getfield $vclogp, $fn),
@@ -4172,6 +4308,11 @@ sub pseudomerge_version_check ($$) {
Your tree seems to based on earlier (not uploaded) %s.
END
if $cd->[0] =~ m/UNRELEASED/;
+ fail f_ <<END, $v, $v
+d/changelog entry for %s is unfinalised!
+Your tree seems to based on earlier (not uploaded) %s.
+END
+ unless defined $vclogp->{Date};
}
}
@@ -4263,7 +4404,7 @@ sub splitbrain_pseudomerge ($$$$) {
print STDERR <<END.(__ <<ENDT);
$@
END
-| Not fast forward; maybe --overwrite is needed ? Please see dgit(1).
+| Not fast forward; maybe --trust-changelog is needed ? Please see dgit(1).
ENDT
finish -1;
}
@@ -4488,6 +4629,68 @@ sub sign_changes ($) {
}
}
+sub tainted_objects_precheck ($$) {
+ my ($json, $dgithead) = @_;
+ my %taints;
+ ROW: foreach my $row (@{ decode_json $json }) {
+ foreach my $override (@{ $row->{overrides} }) {
+ if ($override =~ m{^--deliberately-} && deliberately($')) {
+ printdebug "overriding $row->{gitobjid} $override\n";
+ next ROW;
+ }
+ }
+ my $objid = $row->{gitobjid};
+ my ($gtype, $dummy) = git_cat_file $objid, undef;
+ next if $gtype eq 'missing';
+ if ($row->{gitobjtype} and $gtype ne $row->{gitobjtype}) {
+ print STDERR f_ <<'END', $objid, $gtype, $row->{gitobjtype};
+warning: server says object %s type %s is tainted, but here it has type %s
+END
+ }
+ $taints{$objid}{Type} = $gtype;
+ push @{ $taints{$objid}{Rows} }, $row;
+ }
+
+ open GRL, "-|",
+ @git, qw(rev-list --objects --in-commit-order --pretty=format:),
+ $dgithead
+ or confess "$!";
+ my $trouble = 0;
+ my %hinted;
+ my $found = sub {
+ my ($objid) = @_;
+ my $t = $taints{$objid};
+ return unless $t;
+
+ foreach my $row (@{ $t->{Rows} }) {
+ # If it was actually overridding we don't get here, asd
+ # don't call tainted_objects_message. Instead, the server
+ # will send such a message to our stderr (sadly, untranslated).
+ my $ovstatus =
+ (grep m{^--deliberately-}, @{ $row->{overrides} })
+ ? '' : undef;
+ print STDERR tainted_objects_message $row, $ovstatus, \%hinted;
+ $trouble = 1;
+ }
+ };
+ my $c_commit;
+ while (<GRL>) {
+ if (m{^commit (\w+)$}) {
+ $c_commit = $1;
+ $found->($1, __ 'commit');
+ } elsif (m{(^\w{20}\w*) } && defined $c_commit) {
+ $found->($1, f_ 'object within commit %s', $c_commit);
+ } else {
+ confess "$_ ?";
+ }
+ }
+ GRL->error and die $!;
+ close GRL or confess "$? $!";
+ forceable_fail [qw(push-tainted)],
+ __ "pushing tainted objects (which server would reject)"
+ if $trouble;
+}
+
sub dopush () {
printdebug "actually entering push\n";
@@ -4499,6 +4702,7 @@ END
git_fetch_us();
}
my $archive_hash = fetch_from_archive();
+ my $archive_dsc = $dsc;
if (!$archive_hash) {
$new_package or
fail __ "package appears to be new in this suite;".
@@ -4556,6 +4760,16 @@ END
my $upstreamversion = upstreamversion $clogp->{Version};
+ if (defined $archive_dsc &&
+ version_compare($archive_dsc->{Version}, $cversion) >= 0 &&
+ !forceing [qw(uploading-old-version)]) {
+ fail f_ <<'END', $archive_dsc->{Version}, $csuite, $cversion;
+You seem to be trying to push an old version.
+Version current in archive: %s (in suite %s)
+Version you are trying to upload: %s
+END
+ }
+
if (madformat_wantfixup($format)) {
# user might have not used dgit build, so maybe do this now:
if (do_split_brain()) {
@@ -4603,7 +4817,7 @@ END
fail __ "dgit push: HEAD is not a descendant".
" of the archive's version.\n".
"To overwrite the archive's contents,".
- " pass --overwrite[=VERSION].\n".
+ " pass --trust-changelog, or --overwrite=VERSION.\n".
"To rewrite history, if permitted by the archive,".
" use --deliberately-not-fast-forward.";
}
@@ -4717,8 +4931,8 @@ ENDT
if !$hasdebs;
} elsif ($sourceonlypolicy eq 'not-wholly-new') {
forceable_fail [qw(uploading-source-only)],
- f_ "source-only upload, even though package is entirely NEW\n".
- "(this is contrary to policy in %s)",
+ f_ "source-only upload, though package appears entirely NEW\n".
+ "(this is probably contrary to policy in %s)",
access_nomdistro()
if !$hasdebs
&& $new_package
@@ -4728,6 +4942,32 @@ ENDT
$sourceonlypolicy;
}
+ # Try to detect if we're about to be rejected due to tainted objects
+ my $pq_supported = access_cfg 'policy-query-supported-ssh';
+ $pq_supported =~ m{^(?:false|true|unknown)$} or badcfg f_
+ "policy-query-supported-ssh value '%s' must be false/true/unknown",
+ $pq_supported;
+ if ($pq_supported !~ m/false/) {
+ my @cmd =
+ (access_cfg_ssh, access_gituserhost(),
+ access_runeinfo("policy-client-query $package tainted-objects ".
+ join " ", $csuite).
+ " true");
+ my $json = cmdoutput_errok @cmd;
+ if (!defined $json) {
+ # "unknown" means try the call, but don't mind if it
+ # fails. (This is OK, as a best effort, because then the
+ # server will enforce the check and this machinery is just
+ # to prevent late failures.)
+ failedcmd @cmd unless $pq_supported =~ m/unknown/;
+ } else {
+ printdebug "tainted-objects: $json\n";
+ if (length $json) {
+ tainted_objects_precheck $json, $dgithead;
+ }
+ }
+ }
+
# Perhaps adjust .dsc to contain right set of origs
changes_update_origs_from_dsc($dsc, $changes, $upstreamversion,
$changesfile)
@@ -4944,9 +5184,9 @@ sub cmd_fetch {
sub cmd_pull {
parseopts();
fetchpullargs();
- determine_whether_split_brain get_source_format();
+ my $format = get_source_format();
+ determine_whether_split_brain $format;
if (do_split_brain()) {
- my ($format, $fopts) = get_source_format();
madformat($format) and fail f_ <<END, $quilt_mode
dgit pull not yet supported in split view mode (including with view-splitting quilt modes)
END
@@ -5063,14 +5303,60 @@ sub prep_push () {
}
}
-sub cmd_push {
+sub cmd_push_built {
prep_push();
+ if (do_split_brain()) {
+ # We may need to make the split brain view now, bedcause if
+ # the user built the package other than with dgit they may
+ # have a correct .dsc, but not populated the cache.
+ #
+ # Do this only in split brain mode, since we don't actually
+ # want to update HEAD!
+ build_maybe_quilt_fixup();
+ }
dopush();
}
+sub cmd_push {
+ some_push_alias('push', \&cmd_push_source, \&cmd_push_built,
+ [qw(dgit.default.push-subcmd)], sub {
+ my ($spec) = @_;
+ f_ 'dgit push, but dgit.default.push-subcmd set to %s', $spec
+ });
+}
+sub cmd_rpush {
+ some_push_alias('rpush', \&cmd_rpush_source, \&cmd_rpush_built,
+ [qw(dgit.default.rpush-subcmd
+ dgit.default.push-subcmd)], sub {
+ my ($spec) = @_;
+ f_ 'dgit rpush, but dgit.default.[r]push-subcmd set to %s', $spec
+ });
+}
+sub some_push_alias ($$@) {
+ my ($verb, $if_source, $if_built, $cfgs, $badvalue_msg) = @_;
+ my $spec = cfg @$cfgs;
+
+ if ($spec eq 'source') {
+ $if_source->();
+ } elsif ($spec eq 'built') {
+ $if_built->();
+ } elsif ($spec eq 'warn,built') {
+ print STDERR f_ <<'END', $verb,$verb,$verb;
+warning: "dgit %s" currently means "dgit %s-built" (by default)
+warning: but is going to change to "dgit %s-source". See dgit!(1).
+END
+ $if_built->();
+ } else {
+ fail $badvalue_msg->($spec);
+ }
+}
+
#---------- remote commands' implementation ----------
-sub pre_remote_push_build_host {
+sub pre_remote_push_build_host { core_pre_rpush_bh('push'); }
+sub pre_remote_push_source_build_host { core_pre_rpush_bh('push-source'); }
+sub core_pre_rpush_bh ($) {
+ ($rpush_verb) = @_;
my ($nrargs) = shift @ARGV;
my (@rargs) = @ARGV[0..$nrargs-1];
@ARGV = @ARGV[$nrargs..$#ARGV];
@@ -5101,11 +5387,12 @@ sub pre_remote_push_build_host {
unless defined $protovsn;
changedir $dir;
+
+ responder_send_command("dgit-remote-$rpush_verb-ready $protovsn");
}
-sub cmd_remote_push_build_host {
- responder_send_command("dgit-remote-push-ready $protovsn");
- &cmd_push;
-}
+
+sub cmd_remote_push_build_host { &cmd_push_built; }
+sub cmd_remote_push_source_build_host { &cmd_push_source; }
sub pre_remote_push_responder { pre_remote_push_build_host(); }
sub cmd_remote_push_responder { cmd_remote_push_build_host(); }
@@ -5143,10 +5430,16 @@ sub i_method {
{ no strict qw(refs); &{"${base}_${selector}"}(@args); }
}
-sub pre_rpush () {
- not_necessarily_a_tree();
-}
-sub cmd_rpush {
+sub pre_rpush_source () { not_necessarily_a_tree(); }
+sub pre_rpush_built () { not_necessarily_a_tree(); }
+sub pre_rpush () { not_necessarily_a_tree(); }
+
+sub cmd_rpush_source { rpush_core('push-source'); }
+sub cmd_rpush_built { rpush_core('push'); }
+
+sub rpush_core ($) {
+ ($rpush_verb) = @_;
+
my $host = nextarg;
my $dir;
if ($host =~ m/^((?:[^][]|\[[^][]*\])*)\:/) {
@@ -5161,7 +5454,7 @@ sub cmd_rpush {
my @rdgit;
push @rdgit, @dgit;
push @rdgit, @ropts;
- push @rdgit, qw(remote-push-build-host), (scalar @rargs), @rargs;
+ push @rdgit, "remote-$rpush_verb-build-host", (scalar @rargs), @rargs;
push @rdgit, @ARGV;
my @cmd = (@ssh, $host, shellquote @rdgit);
debugcmd "+",@cmd;
@@ -5178,7 +5471,7 @@ sub cmd_rpush {
}
$i_child_pid = open2(\*RO, \*RI, @cmd);
changedir $i_tmp;
- ($protovsn) = initiator_expect { m/^dgit-remote-push-ready (\S+)/ };
+ ($protovsn) = initiator_expect { m/^dgit-remote-$rpush_verb-ready (\S+)/ };
die "$protovsn ?" unless grep { $_ eq $protovsn } @rpushprotovsn_support;
for (;;) {
@@ -5295,6 +5588,9 @@ sub i_localname_buildinfo ($) {
return $&;
}
sub i_file_buildinfo {
+ $rpush_verb eq 'push'
+ or badproto \*RO, "buildinfo file but verb is $rpush_verb";
+
my $bi = $i_param{'buildinfo-filename'};
my $bd = parsecontrol "$i_tmp/$bi", $bi;
my $ch = parsecontrol "$i_tmp/$i_changesfn", 'changes';
@@ -5317,7 +5613,12 @@ sub i_localname_changes {
$i_changesfn =~ s/\.dsc$/_dgit.changes/ or die;
return $i_changesfn;
}
-sub i_file_changes { }
+sub i_file_changes {
+ my $ch = parsecontrol "$i_tmp/$i_changesfn", 'changes';
+ unless ($rpush_verb eq 'push' || test_source_only_changes($ch)) {
+ fail __ "build-host-supplied changes file is not source-only";
+ }
+}
sub i_want_signed_tag {
printdebug Dumper(\%i_param, $i_dscfn);
@@ -5364,13 +5665,14 @@ our $dscfn;
our $fakeeditorenv = 'DGIT_FAKE_EDITOR_QUILT';
-sub quiltify_dpkg_commit ($$$;$) {
- my ($patchname,$author,$msg, $xinfo) = @_;
+sub quiltify_make_dpkg_patch ($$$$$;$) {
+ my ($oldtreeish,$newtreeish, $patchname,$author,$msg, $xinfo) = @_;
$xinfo //= '';
mkpath '.git/dgit'; # we are in playtree
- my $descfn = ".git/dgit/quilt-description.tmp";
- open O, '>', $descfn or confess "$descfn: $!";
+ my $patchfn = "debian/patches/$patchname";
+ ensuredir dirname $patchfn;
+ open O, '>', $patchfn or confess "$patchfn: $!";
$msg =~ s/\n+/\n\n/;
print O <<END or confess "$!";
From: $author
@@ -5380,12 +5682,19 @@ ${xinfo}Subject: $msg
END
close O or confess "$!";
- {
- local $ENV{'EDITOR'} = cmdoutput qw(realpath --), $0;
- local $ENV{'VISUAL'} = $ENV{'EDITOR'};
- local $ENV{$fakeeditorenv} = cmdoutput qw(realpath --), $descfn;
- runcmd @dpkgsource, qw(--commit --include-removal .), $patchname;
- }
+ my @diffcmd = (git_diff_programmatic, $oldtreeish, $newtreeish,
+ '--', ':!/debian', ':!/.pc');
+ runcmd qw(sh -ec), 'exec >>"$1"; shift; exec "$@"', 'x', $patchfn,
+ @diffcmd;
+
+ open S, ">> debian/patches/series" or confess "$!";
+ print S "$patchname\n" or confess "$!";
+ close S or confess "$!";
+}
+
+sub normalise_mode_strip_exec ($) {
+ my ($m) = @_;
+ return $m eq '100755' ? '100644' : $m;
}
sub quiltify_trees_differ ($$;$$$) {
@@ -5397,8 +5706,8 @@ sub quiltify_trees_differ ($$;$$$) {
# if $ignorenamesr is defined, $ingorenamesr->{$fn}
# is set for each modified .gitignore filename $fn
# if $unrepres is defined, array ref to which is appeneded
- # a list of unrepresentable changes (removals of upstream files
- # (as messages)
+ # a list of unrepresentable changes (changes that dpkg-source
+ # cannot apply even just during unpack).
local $/=undef;
my @cmd = (@git, qw(diff-tree -z --no-renames));
push @cmd, qw(--name-only) unless $unrepres;
@@ -5419,13 +5728,15 @@ sub quiltify_trees_differ ($$;$$$) {
if ($unrepres) {
eval {
- die __ "not a plain file or symlink\n"
+ die __ "not a plain file\n"
unless $newmode =~ m/^(?:10|12)\d{4}$/ ||
$oldmode =~ m/^(?:10|12)\d{4}$/;
if ($oldmode =~ m/[^0]/ &&
$newmode =~ m/[^0]/) {
# both old and new files exist
- die __ "mode or type changed\n" if $oldmode ne $newmode;
+ die __ "mode or type changed in unsupported way\n" if
+ normalise_mode_strip_exec($oldmode) ne
+ normalise_mode_strip_exec($newmode);
die __ "modified symlink\n" unless $newmode =~ m/^10/;
} elsif ($oldmode =~ m/[^0]/) {
# deletion
@@ -5433,10 +5744,9 @@ sub quiltify_trees_differ ($$;$$$) {
unless $oldmode =~ m/^10/;
} else {
# creation
- die __ "creation with non-default mode\n"
+ die __ "creation with non-default mode, or symlink\n"
unless $newmode =~ m/^100644$/ or
- $newmode =~ m/^100755$/ or
- $newmode =~ m/^120000$/;
+ $newmode =~ m/^100755$/;
}
};
if ($@) {
@@ -5453,6 +5763,24 @@ sub quiltify_trees_differ ($$;$$$) {
return $r;
}
+sub quiltify_check_unrepresentable ($) {
+ my ($unrepres) = @_;
+ return unless @$unrepres;
+ if ($quilt_mode =~ m/baredebian/) {
+ # With baredebian, even if the upstream commitish has this
+ # problem, we don't want to print this message, as nothing
+ # is going to try to make a patch out of it anyway.
+ return;
+ }
+ print STDERR f_ "dgit: cannot represent change: %s: %s\n",
+ $_->[1], $_->[0]
+ foreach @$unrepres;
+
+ forceable_fail [qw(unrepresentable)], __ <<END;
+HEAD has changes to .orig[s] which are not representable by `3.0 (quilt)'
+END
+}
+
sub quiltify_tree_sentinelfiles ($) {
# lists the `sentinel' files present in the tree
my ($x) = @_;
@@ -5530,7 +5858,9 @@ ENDU
if ($quilt_mode =~ m/gbp|unapplied|baredebian/ &&
($diffbits->{O2A} & 01)) { # some patches
progress __ "dgit view: creating patches-applied version using gbp pq";
- runcmd shell_cmd 'exec >/dev/null', gbp_pq, qw(import);
+ gbp_pq_pc_aside(sub {
+ runcmd shell_cmd 'exec >/dev/null', gbp_pq, qw(import);
+ });
# gbp pq import creates a fresh branch; push back to dgit-view
runcmd @git, qw(update-ref refs/heads/dgit-view HEAD);
runcmd @git, qw(checkout -q dgit-view);
@@ -5555,6 +5885,7 @@ END
" to record .gitignore changes",
$gipatch
if (stat _)[7];
+ # TODO: The "Subject:" ought not to be translated
print GIPATCH +(__ <<END).<<ENDU or die "$gipatch: $!";
Subject: Update .gitignore from Debian packaging branch
@@ -5567,8 +5898,9 @@ END
---
ENDU
close GIPATCH or die "$gipatch: $!";
- runcmd shell_cmd "exec >>$gipatch", @git, qw(diff),
- $unapplied, $headref, "--", sort keys %$editedignores;
+ runcmd shell_cmd "exec >>$gipatch",
+ git_diff_programmatic $unapplied, $headref, "--",
+ sort keys %$editedignores;
open SERIES, "+>>", "debian/patches/series" or confess "$!";
defined seek SERIES, -1, 2 or $!==EINVAL or confess "$!";
my $newline;
@@ -5733,7 +6065,7 @@ sub quiltify ($$$$) {
fail __
"quilt history linearisation failed. Search \`quilt fixup' in dgit(7).\n";
} elsif ($quilt_mode eq 'smash') {
- } elsif ($quilt_mode eq 'auto') {
+ } elsif ($quilt_mode eq 'try-linear') {
progress __ "quilt fixup cannot be linear, smashing...";
} else {
confess "$quilt_mode ?";
@@ -5744,7 +6076,9 @@ sub quiltify ($$$$) {
my $ncommits = 3;
my $msg = cmdoutput @git, qw(log), "-n$ncommits";
- quiltify_dpkg_commit "auto-$version-$target-$time",
+ quiltify_make_dpkg_patch
+ $oldtiptree, $target,
+ "auto-$version-$target-$time",
(getfield $clogp, 'Maintainer'),
(f_ "Automatically generated patch (%s)\n".
"Last (up to) %s git changes, FYI:\n\n",
@@ -5848,22 +6182,14 @@ sub quiltify ($$$$) {
$index++) { }
$!==ENOENT or confess "$patchname$index $!";
- runcmd @git, qw(checkout -q), $cc;
-
- # We use the tip's changelog so that dpkg-source doesn't
- # produce complaining messages from dpkg-parsechangelog. None
- # of the information dpkg-source gets from the changelog is
- # actually relevant - it gets put into the original message
- # which dpkg-source provides our stunt editor, and then
- # overwritten.
- runcmd @git, qw(checkout -q), $target, qw(debian/changelog);
-
- quiltify_dpkg_commit "$patchname$index", $author, $msg,
+ quiltify_make_dpkg_patch
+ $p->{Commit} ,$cc,
+ "$patchname$index", $author, $msg,
"Date: $commitdate\n".
"X-Dgit-Generated: $clogp->{Version} $cc\n";
- runcmd @git, qw(checkout -q), $cc, qw(debian/changelog);
}
+ runcmd @git, qw(checkout -q), $target;
}
sub build_maybe_quilt_fixup () {
@@ -5905,12 +6231,19 @@ sub build_maybe_quilt_fixup () {
}
chdir '..';
- if ($fopts->{'single-debian-patch'}) {
+ if (grep m{^single-debian-patch$}, @$fopts) {
fail f_
"quilt mode %s does not make sense (or is not supported) with single-debian-patch",
$quilt_mode
if quiltmode_splitting();
- quilt_fixup_singlepatch($clogp, $headref, $upstreamversion);
+
+ # We always use dpkg-source --commit in this case, because
+ # otherwise we can generate source packages that trigger horrible
+ # bugs in dpkg-source.
+ # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1018984
+ quilt_fixup_dpkgsource_singlepatch($clogp, $headref, $upstreamversion);
+ } elsif ($quilt_mode =~ m/single/) {
+ quilt_fixup_git_singlepatch($clogp, $headref, $upstreamversion);
} else {
quilt_fixup_multipatch($clogp, $headref, $upstreamversion,
$splitbrain_cachekey);
@@ -5987,7 +6320,7 @@ END
ENDU
}
-sub quilt_fixup_singlepatch ($$$) {
+sub quilt_fixup_dpkgsource_singlepatch ($$$) {
my ($clogp, $headref, $upstreamversion) = @_;
progress __ "starting quiltify (single-debian-patch)";
@@ -5997,12 +6330,21 @@ sub quilt_fixup_singlepatch ($$$) {
# get it to generate debian/patches/debian-changes, it is
# necessary to build the source package.
+ # We "trust" dpkg-source to get this right, and in particular, to
+ # complain about things it can't represent. However, this is not
+ # always the case. See the test cases in tests/tests/unrepresentable
+ # which have as outcome "LATE-EP:...". When this happens, quilt fixup
+ # succeeds and dgit push fails. This is one of the reasons we
+ # deprecate this in the docs. In principle, we could do better by
+ # rejecting these situations earlier, but we would need another
+ # lot of recapitulation of dpkg-source behaviours.
+
unpack_playtree_linkorigs($upstreamversion, sub { });
unpack_playtree_need_cd_work($headref);
rmtree("debian/patches");
- runcmd @dpkgsource, qw(-b .);
+ runcmd @dpkgsource, qw(--include-removal -b .);
changedir "..";
runcmd @dpkgsource, qw(-x), (srcfn $version, ".dsc");
rename srcfn("$upstreamversion", "/debian/patches"),
@@ -6014,6 +6356,53 @@ sub quilt_fixup_singlepatch ($$$) {
commit_quilty_patch();
}
+sub quilt_fixup_git_singlepatch ($$$) {
+ my ($clogp, $headref, $upstreamversion) = @_;
+
+ progress f_ "regenerating patch using git diff (--quilt=%s)",
+ $quilt_mode;
+
+ my $unapplied=quilt_fakedsc2unapplied($headref, $upstreamversion);
+ changedir '..';
+
+ my @unrepres;
+ my $o2h = quiltify_trees_differ($unapplied,$headref, 1, undef,\@unrepres);
+ unpack_playtree_need_cd_work($headref);
+
+ quiltify_check_unrepresentable \@unrepres;
+
+ rmtree("debian/patches");
+
+ if ($o2h) {
+ quiltify_make_dpkg_patch
+ $unapplied, $headref,
+ 'dgit-changes', '', <<END, '';
+Debian changes
+
+The Debian packaging of $package is maintained in git, using a workflow
+similar to the one described in dgit-maint-merge(7).
+The Debian delta is represented by this one combined patch; there isn't a
+patch queue that can be represented as a quilt series.
+
+A detailed breakdown of the changes is available from their canonical
+representation -- git commits in the packaging repository.
+For example, to see the changes made by the Debian maintainer in the first
+upload of upstream version 1.2.3, you could use:
+
+ % git clone https://git.dgit.debian.org/$package
+ % cd $package
+ % git log --oneline 1.2.3..debian/1.2.3-1 -- . ':!debian'
+
+(If you have dgit, use `dgit clone $package`, rather than plain `git clone`.)
+
+We don't use debian/source/options single-debian-patch because it has bugs.
+Therefore, NMUs etc. may nevertheless have made additional patches.
+END
+ }
+
+ commit_quilty_patch();
+}
+
sub quilt_need_fake_dsc ($) {
# cwd should be playground
my ($upstreamversion) = @_;
@@ -6050,10 +6439,63 @@ END
my @files=qw(debian/source/format debian/rules
debian/control debian/changelog);
foreach my $maybe (qw(debian/patches debian/source/options
+ debian/source/include-binaries
debian/tests/control)) {
next unless stat_exists "$maindir/$maybe";
push @files, $maybe;
}
+ if (open IB, "$maindir/debian/source/include-binaries") {
+ BFILE: while (<IB>) {
+ s{^[ \t]*}{};
+ s{[ \t\n]*$}{};
+ next if m{^\#};
+ next unless length;
+ our $include_binaries_warning;
+ $include_binaries_warning++ or
+ print STDERR __
+ "warning: package uses dpkg-source include-binaries feature - not all changes are visible in patches!\n";
+
+ my @bpath;
+ my $bfile_in = $_;
+ my $bpath_chk;
+ foreach my $ent (split m{/}, $bfile_in) {
+ my $wrong = sub {
+ no warnings qw(exiting);
+ print STDERR f_
+ "warning: ignoring bad include-binaries file %s: %s\n", $bfile_in, $_[0];
+ next BFILE;
+ };
+ $wrong->(f_ "forbidden path component '%s'", $ent)
+ if grep { $_ eq $ent } '', '.', '..';
+ if (!@bpath) { # check first component
+ # dpkg-source doesn't like files in debian/ which it
+ # considers binary, so the user may have listed
+ # them. We should silently ignore this. #1026918.
+ if ($ent eq 'debian') {
+ no warnings qw(exiting);
+ next BFILE;
+ }
+ $wrong->(f_ "path starts with '%s'", $ent)
+ if grep { $_ eq $ent } qw(.git);
+ }
+ push @bpath, $ent;
+ $bpath_chk = join '/', @bpath;
+ if (!lstat "$maindir/$bpath_chk") {
+ confess "$maindir/$bpath_chk" unless $!==ENOENT;
+ next BFILE;
+ } elsif (-f _ || -d _) {
+ } else {
+ $wrong->(f_ "path to '%s' not a plain file or directory",
+ $bpath_chk);
+ }
+ };
+ push @files, $bpath_chk;
+ }
+ IB->error and confess "$!";
+ close IB;
+ } else {
+ $! == ENOENT || confess "$!";
+ }
my $debtar= srcfn $fakeversion,'.debian.tar';
runcmd qw(tar -cf), "./$debtar", qw(-C), $maindir, @files;
@@ -6181,44 +6623,22 @@ sub quilt_fixup_multipatch ($$$) {
# - determine the git commit corresponding to the tip of
# the patch stack (if there is one)
# - if there is such a git commit, convert each subsequent
- # git commit into a quilt patch with dpkg-source --commit
+ # git commit into a quilt patch, simulating dpkg-source --commit
# - otherwise convert all the differences in the tree into
# a single git commit
#
# To do this we:
- # Our git tree doesn't necessarily contain .pc. (Some versions of
- # dgit would include the .pc in the git tree.) If there isn't
- # one, we need to generate one by unpacking the patches that we
- # have.
- #
- # We first look for a .pc in the git tree. If there is one, we
- # will use it. (This is not the normal case.)
- #
- # Otherwise need to regenerate .pc so that dpkg-source --commit
- # can work. We do this as follows:
+ # So we need to find out what the tree for the tip of the patch
+ # stack is.
# 1. Collect all relevant .orig from parent directory
# 2. Generate a debian.tar.gz out of
# debian/{patches,rules,source/format,source/options}
# 3. Generate a fake .dsc containing just these fields:
# Format Source Version Files
# 4. Extract the fake .dsc
- # Now the fake .dsc has a .pc directory.
- # (In fact we do this in every case, because in future we will
- # want to search for a good base commit for generating patches.)
#
- # Then we can actually do the dpkg-source --commit
- # 1. Make a new working tree with the same object
- # store as our main tree and check out the main
- # tree's HEAD.
- # 2. Copy .pc from the fake's extraction, if necessary
- # 3. Run dpkg-source --commit
- # 4. If the result has changes to debian/, then
- # - git add them them
- # - git add .pc if we had a .pc in-tree
- # - git commit
- # 5. If we had a .pc in-tree, delete it, and git commit
- # 6. Back in the main tree, fast forward to the new HEAD
+ # Then we can actually do the fake dpkg-source --commit.
# Another situation we may have to cope with is gbp-style
# patches-unapplied trees.
@@ -6275,7 +6695,7 @@ sub quilt_fixup_multipatch ($$$) {
}
$headref = git_rev_parse('HEAD');
- chdir '..';
+ changedir '..';
}
my $unapplied=quilt_fakedsc2unapplied($headref, $upstreamversion);
@@ -6284,6 +6704,7 @@ sub quilt_fixup_multipatch ($$$) {
my @bbcmd = (qw(sh -ec), 'exec dpkg-source --before-build . >/dev/null');
$!=0; $?=-1;
+ debugcmd "+",@bbcmd;
if (system @bbcmd) {
failedcmd @bbcmd if $? < 0;
fail __ <<END;
@@ -6301,7 +6722,7 @@ END
my $mustdeletepc=0;
if (stat_exists ".pc") {
-d _ or die;
- progress __ "Tree already contains .pc - will use it then delete it.";
+ progress __ "Tree already contains .pc - will delete it.";
$mustdeletepc=1;
} else {
rename '../fake/.pc','.pc' or confess "$!";
@@ -6435,17 +6856,7 @@ END
$us, $dl[0], $dl[1], $dl[3], $dl[4],
$us, $uhead_whatshort, $dl[2], $uhead_whatshort, $dl[5];
- if (@unrepres && $quilt_mode !~ m/baredebian/) {
- # With baredebian, even if the upstream commitish has this
- # problem, we don't want to print this message, as nothing
- # is going to try to make a patch out of it anyway.
- print STDERR f_ "dgit: cannot represent change: %s: %s\n",
- $_->[1], $_->[0]
- foreach @unrepres;
- forceable_fail [qw(unrepresentable)], __ <<END;
-HEAD has changes to .orig[s] which are not representable by `3.0 (quilt)'
-END
- }
+ quiltify_check_unrepresentable(\@unrepres);
my @failsuggestion;
if ($onlydebian) {
@@ -6647,13 +7058,28 @@ sub build_or_push_prep_early () {
}
sub build_or_push_prep_modes () {
- my ($format) = get_source_format();
+ my ($format, $fopts) = get_source_format();
determine_whether_split_brain($format);
fail __ "dgit: --include-dirty is not supported with split view".
" (including with view-splitting quilt modes)"
if do_split_brain() && $includedirty;
+ if (grep m{^tar-ignore$}, @$fopts) {
+ if ((cmdoutput qw(git ls-files :.gitignore :*/.gitignore)) ne '') {
+ # The source package won't be faithful; bail with an explanation.
+ fail __ <<'END';
+tree has .gitignore(s) but debian/source/options has 'tar-ignore'
+Try 'tar-ignore=.git' in d/s/options instead. (See #908747.)
+END
+ } else {
+ print STDERR f_ <<'END', $us;
+%s: warning: debian/source/options contains bare 'tar-ignore'
+This can cause .gitignore files to be improperly omitted. See #908747.
+END
+ }
+ }
+
if (madformat_wantfixup $format and $quilt_mode =~ m/baredebian$/) {
($quilt_upstream_commitish, $quilt_upstream_commitish_used,
$quilt_upstream_commitish_message)
@@ -7021,7 +7447,7 @@ sub build_source {
}
# confess unless !!$made_split_brain == do_split_brain();
- my @cmd = (@dpkgsource, qw(-b --));
+ my @cmd = (@dpkgsource, qw(-b --include-removal));
my $leafdir;
if (building_source_in_playtree()) {
$leafdir = 'work';
@@ -7037,6 +7463,14 @@ sub build_source {
unpack_playtree_need_cd_work($headref);
changedir '..';
}
+
+ # We are presenting dpkg-source with a tree with no .pc directory.
+ # Without this option, dpkg-source tries to guess if it should
+ # mess about (un)applying patches. Depending on what precisely is
+ # in the patches, it can guess wrong.
+ push @cmd, qw(--no-preparation);
+
+ runcmd @cmd, qw(--), $leafdir;
} else {
$leafdir = basename $maindir;
@@ -7058,13 +7492,18 @@ sub build_source {
}
changedir '..';
+ runcmd_ordryrun_local @cmd, qw(--), $leafdir;
}
- runcmd_ordryrun_local @cmd, $leafdir;
changedir $leafdir;
- runcmd_ordryrun_local qw(sh -ec),
- 'exec >../$1; shift; exec "$@"','x', $sourcechanges,
- @dpkggenchanges, qw(-S), changesopts();
+ my @gencmd = (qw(sh -ec),
+ 'exec >../$1; shift; exec "$@"','x', $sourcechanges,
+ @dpkggenchanges, qw(-S), changesopts());
+ if (building_source_in_playtree()) {
+ runcmd @gencmd;
+ } else {
+ runcmd_ordryrun_local @gencmd;
+ }
changedir '..';
printdebug "moving $dscfn, $sourcechanges, etc. to ".bpd_abs()."\n";
@@ -7073,6 +7512,7 @@ sub build_source {
my $mv = sub {
my ($why, $l) = @_;
printdebug " renaming ($why) $l\n";
+ return unless act_local();
rename_link_xf 0, "$l", bpd_abs()."/$l"
or fail f_ "put in place new built file (%s): %s", $l, $@;
};
@@ -7263,7 +7703,7 @@ sub cmd_import_dsc {
# we don't normally need this so import it here
use Dpkg::Source::Package;
my $dp = new Dpkg::Source::Package filename => $dscfn,
- require_valid_signature => $needsig;
+ options => { require_valid_signature => $needsig };
{
local $SIG{__WARN__} = sub {
print STDERR $_[0];
@@ -7638,9 +8078,13 @@ sub parseopts () {
} elsif (m/^--no-chase-dsc-distro$/s) {
push @ropts, $_;
$chase_dsc_distro = 0;
- } elsif (m/^--overwrite$/s) {
+ } elsif (m/^--collab-non-dgit$/s) {
push @ropts, $_;
$overwrite_version = '';
+ $splitview_mode = 'always';
+ } elsif (m/^(?:--trust-changelog|--overwrite)$/s) {
+ push @ropts, '--overwrite'; # TODO, eventually, change this
+ $overwrite_version = '';
} elsif (m/^--split-(?:view|brain)$/s) {
push @ropts, $_;
$splitview_mode = 'always';
@@ -7663,6 +8107,9 @@ sub parseopts () {
push @ropts, $_;
$v =~ s#^(?!refs/)#refs/heads/#;
$internal_object_save{$k} = $v;
+ } elsif (m/^--(no-)?keep-playground$/s) {
+ push @ropts, $_;
+ $keep_playground = !$1;
} elsif (m/^--(no-)?rm-old-changes$/s) {
push @ropts, $_;
$rmchanges = !$1;
@@ -7827,6 +8274,7 @@ sub parseopts_late_defaults () {
$quilt_mode = $1;
}
$quilt_mode =~ s/^(baredebian)\+git$/$1/;
+ $quilt_mode =~ s/^auto$/try-linear/;
foreach my $moc (@modeopt_cfgs) {
local $access_forpush;
@@ -7870,6 +8318,7 @@ if (!@ARGV) {
finish 8;
}
$cmd = $subcommand = shift @ARGV;
+my $orig_cmd = $cmd;
$cmd =~ y/-/_/;
my $pre_fn = ${*::}{"pre_$cmd"};
@@ -7882,7 +8331,10 @@ if ($invoked_in_git_tree) {
git_slurp_config();
my $fn = ${*::}{"cmd_$cmd"};
-$fn or badusage f_ "unknown operation %s", $cmd;
+$fn or badusage f_ "unknown operation %s", $orig_cmd;
$fn->();
+changedir '/'; # rmtree complains if our cwd is inside what we remove
+rmtree $playground if defined($playground) && !$keep_playground;
+
finish 0;