chiark / gitweb /
i18n: dgit: mark some messages (2)
[dgit.git] / dgit
diff --git a/dgit b/dgit
index 0da0c7f..1fde60b 100755 (executable)
--- a/dgit
+++ b/dgit
@@ -20,6 +20,7 @@
 
 END { $? = $Debian::Dgit::ExitStatus::desired // -1; };
 use Debian::Dgit::ExitStatus;
 
 END { $? = $Debian::Dgit::ExitStatus::desired // -1; };
 use Debian::Dgit::ExitStatus;
+use Debian::Dgit::I18n;
 
 use strict;
 
 
 use strict;
 
@@ -37,6 +38,7 @@ use Dpkg::Version;
 use Dpkg::Compression;
 use Dpkg::Compression::Process;
 use POSIX;
 use Dpkg::Compression;
 use Dpkg::Compression::Process;
 use POSIX;
+use Locale::gettext;
 use IPC::Open2;
 use Digest::SHA;
 use Digest::MD5;
 use IPC::Open2;
 use Digest::SHA;
 use Digest::MD5;
@@ -100,9 +102,6 @@ our %format_ok = map { $_=>1 } ("1.0","3.0 (native)","3.0 (quilt)");
 
 our $suite_re = '[-+.0-9a-z]+';
 our $cleanmode_re = 'dpkg-source(?:-d)?|git|git-ff|check|none';
 
 our $suite_re = '[-+.0-9a-z]+';
 our $cleanmode_re = 'dpkg-source(?:-d)?|git|git-ff|check|none';
-our $orig_f_comp_re = qr{orig(?:-$extra_orig_namepart_re)?};
-our $orig_f_sig_re = '\\.(?:asc|gpg|pgp)';
-our $orig_f_tail_re = "$orig_f_comp_re\\.tar(?:\\.\\w+)?(?:$orig_f_sig_re)?";
 
 our $git_authline_re = '^([^<>]+) \<(\S+)\> (\d+ [-+]\d+)$';
 our $splitbraincache = 'dgit-intern/quilt-cache';
 
 our $git_authline_re = '^([^<>]+) \<(\S+)\> (\d+ [-+]\d+)$';
 our $splitbraincache = 'dgit-intern/quilt-cache';
@@ -116,7 +115,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 (@dput) = qw(dput);
 our (@debsign) = qw(debsign);
 our (@gpg) = qw(gpg);
-our (@sbuild) = qw(sbuild);
+our (@sbuild) = (qw(sbuild --no-source));
 our (@ssh) = 'ssh';
 our (@dgit) = qw(dgit);
 our (@git_debrebase) = qw(git-debrebase);
 our (@ssh) = 'ssh';
 our (@dgit) = qw(dgit);
 our (@git_debrebase) = qw(git-debrebase);
@@ -201,15 +200,13 @@ sub lref () { return "refs/heads/".lbranch(); }
 sub lrref () { return "refs/remotes/$remotename/".server_branch($csuite); }
 sub rrref () { return server_ref($csuite); }
 
 sub lrref () { return "refs/remotes/$remotename/".server_branch($csuite); }
 sub rrref () { return server_ref($csuite); }
 
-sub stripepoch ($) {
-    my ($vsn) = @_;
-    $vsn =~ s/^\d+\://;
-    return $vsn;
-}
-
 sub srcfn ($$) {
 sub srcfn ($$) {
-    my ($vsn,$sfx) = @_;
-    return "${package}_".(stripepoch $vsn).$sfx
+    my ($vsn, $sfx) = @_;
+    return &source_file_leafname($package, $vsn, $sfx);
+}
+sub is_orig_file_of_vsn ($$) {
+    my ($f, $upstreamvsn) = @_;
+    return is_orig_file_of_p_v($f, $package, $upstreamvsn);
 }
 
 sub dscfn ($) {
 }
 
 sub dscfn ($) {
@@ -222,12 +219,6 @@ sub changespat ($;$) {
     return "${package}_".(stripepoch $vsn)."_".($arch//'*').".changes";
 }
 
     return "${package}_".(stripepoch $vsn)."_".($arch//'*').".changes";
 }
 
-sub upstreamversion ($) {
-    my ($vsn) = @_;
-    $vsn =~ s/-[^-]+$//;
-    return $vsn;
-}
-
 our $us = 'dgit';
 initdebug('');
 
 our $us = 'dgit';
 initdebug('');
 
@@ -241,24 +232,29 @@ END {
     }
 };
 
     }
 };
 
-sub badcfg { print STDERR "$us: invalid configuration: @_\n"; finish 12; }
+sub badcfg {
+    print STDERR f_ "%s: invalid configuration: %s\n", $us, "@_";
+    finish 12;
+}
 
 sub forceable_fail ($$) {
     my ($forceoptsl, $msg) = @_;
     fail $msg unless grep { $forceopts{$_} } @$forceoptsl;
 
 sub forceable_fail ($$) {
     my ($forceoptsl, $msg) = @_;
     fail $msg unless grep { $forceopts{$_} } @$forceoptsl;
-    print STDERR "warning: overriding problem due to --force:\n". $msg;
+    print STDERR +(__ "warning: overriding problem due to --force:\n"). $msg;
 }
 
 sub forceing ($) {
     my ($forceoptsl) = @_;
     my @got = grep { $forceopts{$_} } @$forceoptsl;
     return 0 unless @got;
 }
 
 sub forceing ($) {
     my ($forceoptsl) = @_;
     my @got = grep { $forceopts{$_} } @$forceoptsl;
     return 0 unless @got;
-    print STDERR
- "warning: skipping checks or functionality due to --force-$got[0]\n";
+    print STDERR f_
+       "warning: skipping checks or functionality due to --force-%s\n",
+       $got[0];
 }
 
 sub no_such_package () {
 }
 
 sub no_such_package () {
-    print STDERR "$us: package $package does not exist in suite $isuite\n";
+    print STDERR f_ "%s: package %s does not exist in suite %s\n",
+       $us, $package, $isuite;
     finish 4;
 }
 
     finish 4;
 }
 
@@ -300,6 +296,14 @@ sub bpd_abs () {
     return $r;
 }
 
     return $r;
 }
 
+sub get_tree_of_commit ($) {
+    my ($commitish) = @_;
+    my $cdata = cmdoutput @git, qw(cat-file commit), $commitish;
+    $cdata =~ m/\n\n/;  $cdata = $`;
+    $cdata =~ m/^tree (\w+)$/m or confess "cdata $cdata ?";
+    return $1;
+}
+
 sub branch_gdr_info ($$) {
     my ($symref, $head) = @_;
     my ($status, $msg, $current, $ffq_prev, $gdrlast) =
 sub branch_gdr_info ($$) {
     my ($symref, $head) = @_;
     my ($status, $msg, $current, $ffq_prev, $gdrlast) =
@@ -311,21 +315,91 @@ sub branch_gdr_info ($$) {
     return ($ffq_prev, $gdrlast);
 }
 
     return ($ffq_prev, $gdrlast);
 }
 
-sub branch_is_gdr ($$) {
-    my ($symref, $head) = @_;
-    my ($ffq_prev, $gdrlast) = branch_gdr_info($symref, $head);
-    return 0 unless $ffq_prev || $gdrlast;
-    return 1;
-}
-
 sub branch_is_gdr_unstitched_ff ($$$) {
     my ($symref, $head, $ancestor) = @_;
     my ($ffq_prev, $gdrlast) = branch_gdr_info($symref, $head);
     return 0 unless $ffq_prev;
 sub branch_is_gdr_unstitched_ff ($$$) {
     my ($symref, $head, $ancestor) = @_;
     my ($ffq_prev, $gdrlast) = branch_gdr_info($symref, $head);
     return 0 unless $ffq_prev;
-    return 0 unless is_fast_fwd $ancestor, $ffq_prev;
+    return 0 unless !defined $ancestor or is_fast_fwd $ancestor, $ffq_prev;
     return 1;
 }
 
     return 1;
 }
 
+sub branch_is_gdr ($) {
+    my ($head) = @_;
+    # This is quite like git-debrebase's keycommits.
+    # We have our own implementation because:
+    #  - our algorighm can do fewer tests so is faster
+    #  - it saves testing to see if gdr is installed
+
+    # NB we use this jsut for deciding whether to run gdr make-patches
+    # Before reusing this algorithm for somthing else, its
+    # suitability should be reconsidered.
+
+    my $walk = $head;
+    local $Debian::Dgit::debugcmd_when_debuglevel = 3;
+    printdebug "branch_is_gdr $head...\n";
+    my $get_patches = sub {
+       my $t = git_cat_file "$_[0]:debian/patches", [qw(missing tree)];
+       return $t // '';
+    };
+    my $tip_patches = $get_patches->($head);
+  WALK:
+    for (;;) {
+       my $cdata = git_cat_file $walk, 'commit';
+       my ($hdrs,$msg) = $cdata =~ m{\n\n} ? ($`,$') : ($cdata,'');
+       if ($msg =~ m{^\[git-debrebase\ (
+                         anchor | changelog | make-patches | 
+                         merged-breakwater | pseudomerge
+                     ) [: ] }mx) {
+           # no need to analyse this - it's sufficient
+           # (gdr classifications: Anchor, MergedBreakwaters)
+           # (made by gdr: Pseudomerge, Changelog)
+           printdebug "branch_is_gdr  $walk gdr $1 YES\n";
+           return 1;
+       }
+       my @parents = ($hdrs =~ m/^parent (\w+)$/gm);
+       if (@parents==2) {
+           my $walk_tree = get_tree_of_commit $walk;
+           foreach my $p (@parents) {
+               my $p_tree = get_tree_of_commit $p;
+               if ($p_tree eq $walk_tree) { # pseudomerge contriburor
+                   # (gdr classification: Pseudomerge; not made by gdr)
+                   printdebug "branch_is_gdr  $walk unmarked pseudomerge\n"
+                       if $debuglevel >= 2;
+                   $walk = $p;
+                   next WALK;
+               }
+           }
+           # some other non-gdr merge
+           # (gdr classification: VanillaMerge, DgitImportUnpatched, ?)
+           printdebug "branch_is_gdr  $walk ?-2-merge NO\n";
+           return 0;
+       }
+       if (@parents>2) {
+           # (gdr classification: ?)
+           printdebug "branch_is_gdr  $walk ?-octopus NO\n";
+           return 0;
+       }
+       if ($get_patches->($walk) ne $tip_patches) {
+           # Our parent added, removed, or edited patches, and wasn't
+           # a gdr make-patches commit.  gdr make-patches probably
+           # won't do that well, then.
+           # (gdr classification of parent: AddPatches or ?)
+           printdebug "branch_is_gdr  $walk ?-patches NO\n";
+           return 0;
+       }
+       if ($tip_patches eq '' and
+           !defined git_cat_file "$walk:debian") {
+           # (gdr classification of parent: BreakwaterStart
+           printdebug "branch_is_gdr  $walk unmarked BreakwaterStart YES\n";
+           return 1;
+       }
+       # (gdr classification: Upstream Packaging Mixed Changelog)
+       printdebug "branch_is_gdr  $walk plain\n"
+           if $debuglevel >= 2;
+       $walk = $parents[0];
+    }
+}
+
 #---------- remote protocol support, common ----------
 
 # remote push initiator/responder protocol:
 #---------- remote protocol support, common ----------
 
 # remote push initiator/responder protocol:
@@ -398,28 +472,28 @@ sub i_child_report () {
     die unless $got == $i_child_pid;
     $i_child_pid = undef;
     return undef unless $?;
     die unless $got == $i_child_pid;
     $i_child_pid = undef;
     return undef unless $?;
-    return "build host child ".waitstatusmsg();
+    return f_ "build host child %s", waitstatusmsg();
 }
 
 sub badproto ($$) {
     my ($fh, $m) = @_;
 }
 
 sub badproto ($$) {
     my ($fh, $m) = @_;
-    fail "connection lost: $!" if $fh->error;
-    fail "protocol violation; $m not expected";
+    fail f_ "connection lost: %s", $! if $fh->error;
+    fail f_ "protocol violation; %s not expected", $m;
 }
 
 sub badproto_badread ($$) {
     my ($fh, $wh) = @_;
 }
 
 sub badproto_badread ($$) {
     my ($fh, $wh) = @_;
-    fail "connection lost: $!" if $!;
+    fail f_ "connection lost: %s", $! if $!;
     my $report = i_child_report();
     fail $report if defined $report;
     my $report = i_child_report();
     fail $report if defined $report;
-    badproto $fh, "eof (reading $wh)";
+    badproto $fh, f_ "eof (reading %s)", $wh;
 }
 
 sub protocol_expect (&$) {
     my ($match, $fh) = @_;
     local $_;
     $_ = <$fh>;
 }
 
 sub protocol_expect (&$) {
     my ($match, $fh) = @_;
     local $_;
     $_ = <$fh>;
-    defined && chomp or badproto_badread $fh, "protocol message";
+    defined && chomp or badproto_badread $fh, __ "protocol message";
     if (wantarray) {
        my @r = &$match;
        return @r if @r;
     if (wantarray) {
        my @r = &$match;
        return @r if @r;
@@ -427,7 +501,7 @@ sub protocol_expect (&$) {
        my $r = &$match;
        return $r if $r;
     }
        my $r = &$match;
        return $r if $r;
     }
-    badproto $fh, "\`$_'";
+    badproto $fh, f_ "\`%s'", $_;
 }
 
 sub protocol_send_file ($$) {
 }
 
 sub protocol_send_file ($$) {
@@ -448,10 +522,10 @@ sub protocol_send_file ($$) {
 
 sub protocol_read_bytes ($$) {
     my ($fh, $nbytes) = @_;
 
 sub protocol_read_bytes ($$) {
     my ($fh, $nbytes) = @_;
-    $nbytes =~ m/^[1-9]\d{0,5}$|^0$/ 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;
     my $d;
     my $got = read $fh, $d, $nbytes;
-    $got==$nbytes or badproto_badread $fh, "data block";
+    $got==$nbytes or badproto_badread $fh, __ "data block";
     return $d;
 }
 
     return $d;
 }
 
@@ -532,7 +606,8 @@ sub url_get {
     progress "downloading $what...";
     my $r = $ua->get(@_) or die $!;
     return undef if $r->code == 404;
     progress "downloading $what...";
     my $r = $ua->get(@_) or die $!;
     return undef if $r->code == 404;
-    $r->is_success or fail "failed to fetch $what: ".$r->status_line;
+    $r->is_success or fail f_ "failed to fetch %s: %s",
+       $what, $r->status_line;
     return $r->decoded_content(charset => 'none');
 }
 
     return $r->decoded_content(charset => 'none');
 }
 
@@ -543,9 +618,9 @@ sub act_scary () { return !$dryrun_level; }
 
 sub printdone {
     if (!$dryrun_level) {
 
 sub printdone {
     if (!$dryrun_level) {
-       progress "$us ok: @_";
+       progress f_ "%s ok: %s", $us, "@_";
     } else {
     } else {
-       progress "would be ok: @_ (but dry run only)";
+       progress f_ "would be ok: %s (but dry run only)", "@_";
     }
 }
 
     }
 }
 
@@ -569,7 +644,7 @@ sub runcmd_ordryrun_local {
     }
 }
 
     }
 }
 
-our $helpmsg = <<END;
+our $helpmsg = i_ <<END;
 main usages:
   dgit [dgit-opts] clone [dgit-opts] package [suite] [./dir|/dir]
   dgit [dgit-opts] fetch|pull [dgit-opts] [suite]
 main usages:
   dgit [dgit-opts] clone [dgit-opts] package [suite] [./dir|/dir]
   dgit [dgit-opts] fetch|pull [dgit-opts] [suite]
@@ -588,17 +663,17 @@ important dgit options:
   -c<name>=<value>    set git config option (used directly by dgit too)
 END
 
   -c<name>=<value>    set git config option (used directly by dgit too)
 END
 
-our $later_warning_msg = <<END;
+our $later_warning_msg = i_ <<END;
 Perhaps the upload is stuck in incoming.  Using the version from git.
 END
 
 sub badusage {
 Perhaps the upload is stuck in incoming.  Using the version from git.
 END
 
 sub badusage {
-    print STDERR "$us: @_\n", $helpmsg or die $!;
+    print STDERR f_ "%s: %s\n%s", $us, "@_", __ $helpmsg or die $!;
     finish 8;
 }
 
 sub nextarg {
     finish 8;
 }
 
 sub nextarg {
-    @ARGV or badusage "too few arguments";
+    @ARGV or badusage __ "too few arguments";
     return scalar shift @ARGV;
 }
 
     return scalar shift @ARGV;
 }
 
@@ -606,7 +681,7 @@ sub pre_help () {
     not_necessarily_a_tree();
 }
 sub cmd_help () {
     not_necessarily_a_tree();
 }
 sub cmd_help () {
-    print $helpmsg or die $!;
+    print __ $helpmsg or die $!;
     finish 0;
 }
 
     finish 0;
 }
 
@@ -708,8 +783,9 @@ sub git_get_config ($) {
                           "undef")."\n"
            if $debuglevel >= 4;
        $l or next;
                           "undef")."\n"
            if $debuglevel >= 4;
        $l or next;
-       @$l==1 or badcfg "multiple values for $c".
-           " (in $src git config)" if @$l > 1;
+       @$l==1 or badcfg
+           f_ "multiple values for %s (in %s git config)", $c, $src
+           if @$l > 1;
        return $l->[0];
     }
     return undef;
        return $l->[0];
     }
     return undef;
@@ -727,8 +803,10 @@ sub cfg {
            return $dv;
        }
     }
            return $dv;
        }
     }
-    badcfg "need value for one of: @_\n".
-       "$us: distro or suite appears not to be (properly) supported";
+    badcfg f_
+       "need value for one of: %s\n".
+       "%s: distro or suite appears not to be (properly) supported",
+       "@_", $us;
 }
 
 sub not_necessarily_a_tree () {
 }
 
 sub not_necessarily_a_tree () {
@@ -767,7 +845,8 @@ sub access_nomdistro () {
     my $base = access_basedistro();
     my $r = cfg("dgit-distro.$base.nominal-distro",'RETURN-UNDEF') // $base;
     $r =~ m/^$distro_re$/ or badcfg
     my $base = access_basedistro();
     my $r = cfg("dgit-distro.$base.nominal-distro",'RETURN-UNDEF') // $base;
     $r =~ m/^$distro_re$/ or badcfg
- "bad syntax for (nominal) distro \`$r' (does not match /^$distro_re$/)";
+       f_ "bad syntax for (nominal) distro \`%s' (does not match %s)",
+       $r, "/^$distro_re$/";
     return $r;
 }
 
     return $r;
 }
 
@@ -781,7 +860,7 @@ sub access_quirk () {
        $re =~ s/[^-0-9a-z_\%*()]/\\$&/ig;
        $re =~ s/\*/.*/g;
        $re =~ s/\%/([-0-9a-z_]+)/
        $re =~ s/[^-0-9a-z_\%*()]/\\$&/ig;
        $re =~ s/\*/.*/g;
        $re =~ s/\%/([-0-9a-z_]+)/
-           or $re =~ m/[()]/ or badcfg "backports-quirk needs \% or ( )";
+           or $re =~ m/[()]/ or badcfg __ "backports-quirk needs \% or ( )";
        if ($isuite =~ m/^$re$/) {
            return ('backports',"$basedistro-backports",$1);
        }
        if ($isuite =~ m/^$re$/) {
            return ('backports',"$basedistro-backports",$1);
        }
@@ -797,7 +876,8 @@ sub parse_cfg_bool ($$$) {
     return
        $v =~ m/^[ty1]/ ? 1 :
        $v =~ m/^[fn0]/ ? 0 :
     return
        $v =~ m/^[ty1]/ ? 1 :
        $v =~ m/^[fn0]/ ? 0 :
-       badcfg "$what needs t (true, y, 1) or f (false, n, 0) not \`$v'";
+       badcfg f_ "%s needs t (true, y, 1) or f (false, n, 0) not \`%s'",
+           $what, $v;
 }      
 
 sub access_forpush_config () {
 }      
 
 sub access_forpush_config () {
@@ -815,7 +895,8 @@ sub access_forpush_config () {
        $v =~ m/^[ty1]/ ? 0 : # force readonly,    forpush = 0
        $v =~ m/^[fn0]/ ? 1 : # force nonreadonly, forpush = 1
        $v =~ m/^[a]/  ? '' : # auto,              forpush = ''
        $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)";
+       badcfg __
+           "readonly needs t (true, y, 1) or f (false, n, 0) or a (auto)";
 }
 
 sub access_forpush () {
 }
 
 sub access_forpush () {
@@ -824,12 +905,12 @@ sub access_forpush () {
 }
 
 sub pushing () {
 }
 
 sub pushing () {
-    confess 'internal error '.Dumper($access_forpush)," ?" if
+    confess +(__ 'internal error').' '.Dumper($access_forpush)," ?" if
        defined $access_forpush and !$access_forpush;
        defined $access_forpush and !$access_forpush;
-    badcfg "pushing but distro is configured readonly"
+    badcfg __ "pushing but distro is configured readonly"
        if access_forpush_config() eq '0';
     $access_forpush = 1;
        if access_forpush_config() eq '0';
     $access_forpush = 1;
-    $supplementary_message = <<'END' unless $we_are_responder;
+    $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
 Push failed, before we got started.
 You can retry the push, after fixing the problem, if you like.
 END
@@ -995,7 +1076,7 @@ our %rmad;
 
 sub archive_query ($;@) {
     my ($method) = shift @_;
 
 sub archive_query ($;@) {
     my ($method) = shift @_;
-    fail "this operation does not support multiple comma-separated suites"
+    fail __ "this operation does not support multiple comma-separated suites"
        if $isuite =~ m/,/;
     my $query = access_cfg('archive-query','RETURN-UNDEF');
     $query =~ s/^(\w+):// or badcfg "invalid archive-query method \`$query'";
        if $isuite =~ m/,/;
     my $query = access_cfg('archive-query','RETURN-UNDEF');
     $query =~ s/^(\w+):// or badcfg "invalid archive-query method \`$query'";
@@ -1041,8 +1122,9 @@ sub archive_api_query_cmd ($) {
                fail "for $url: stat $key: $!" unless $!==ENOENT;
                next;
            }
                fail "for $url: stat $key: $!" unless $!==ENOENT;
                next;
            }
-           fail "config requested specific TLS key but do not know".
-               " how to get curl to use exactly that EE key ($key)";
+           fail f_ "config requested specific TLS key but do not know".
+                   " how to get curl to use exactly that EE key (%s)",
+                   $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
 #          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
@@ -1061,7 +1143,7 @@ sub archive_api_query_cmd ($) {
 sub api_query ($$;$) {
     use JSON;
     my ($data, $subpath, $ok404) = @_;
 sub api_query ($$;$) {
     use JSON;
     my ($data, $subpath, $ok404) = @_;
-    badcfg "ftpmasterapi archive query method takes no data part"
+    badcfg __ "ftpmasterapi archive query method takes no data part"
        if length $data;
     my @cmd = archive_api_query_cmd($subpath);
     my $url = $cmd[$#cmd];
        if length $data;
     my @cmd = archive_api_query_cmd($subpath);
     my $url = $cmd[$#cmd];
@@ -1069,11 +1151,11 @@ sub api_query ($$;$) {
     my $json = cmdoutput @cmd;
     unless ($json =~ s/\d+\d+\d$//) {
        failedcmd_report_cmd undef, @cmd;
     my $json = cmdoutput @cmd;
     unless ($json =~ s/\d+\d+\d$//) {
        failedcmd_report_cmd undef, @cmd;
-       fail "curl failed to print 3-digit HTTP code";
+       fail __ "curl failed to print 3-digit HTTP code";
     }
     my $code = $&;
     return undef if $code eq '404' && $ok404;
     }
     my $code = $&;
     return undef if $code eq '404' && $ok404;
-    fail "fetch of $url gave HTTP code $code"
+    fail f_ "fetch of %s gave HTTP code %s", $url, $code
        unless $url =~ m#^file://# or $code =~ m/^2/;
     return decode_json($json);
 }
        unless $url =~ m#^file://# or $code =~ m/^2/;
     return decode_json($json);
 }
@@ -1089,15 +1171,17 @@ sub canonicalise_suite_ftpmasterapi {
        } qw(codename name);
        push @matched, $entry;
     }
        } qw(codename name);
        push @matched, $entry;
     }
-    fail "unknown suite $isuite" unless @matched;
+    fail f_ "unknown suite %s, maybe -d would help", $isuite
+       unless @matched;
     my $cn;
     eval {
     my $cn;
     eval {
-       @matched==1 or die "multiple matches for suite $isuite\n";
+       @matched==1 or die f_ "multiple matches for suite %s\n", $isuite;
        $cn = "$matched[0]{codename}";
        $cn = "$matched[0]{codename}";
-       defined $cn or die "suite $isuite info has no codename\n";
-       $cn =~ m/^$suite_re$/ or die "suite $isuite maps to bad codename\n";
+       defined $cn or die f_ "suite %s info has no codename\n", $isuite;
+       $cn =~ m/^$suite_re$/
+           or die f_ "suite %s maps to bad codename\n", $isuite;
     };
     };
-    die "bad ftpmaster api response: $@\n".Dumper(\@matched)
+    die +(__ "bad ftpmaster api response: ")."$@\n".Dumper(\@matched)
        if length $@;
     return $cn;
 }
        if length $@;
     return $cn;
 }
@@ -1111,18 +1195,18 @@ sub archive_query_ftpmasterapi {
        eval {
            my $vsn = "$entry->{version}";
            my ($ok,$msg) = version_check $vsn;
        eval {
            my $vsn = "$entry->{version}";
            my ($ok,$msg) = version_check $vsn;
-           die "bad version: $msg\n" unless $ok;
+           die f_ "bad version: %s\n", $msg unless $ok;
            my $component = "$entry->{component}";
            my $component = "$entry->{component}";
-           $component =~ m/^$component_re$/ or die "bad component";
+           $component =~ m/^$component_re$/ or die __ "bad component";
            my $filename = "$entry->{filename}";
            $filename && $filename !~ m#[^-+:._~0-9a-zA-Z/]|^[/.]|/[/.]#
            my $filename = "$entry->{filename}";
            $filename && $filename !~ m#[^-+:._~0-9a-zA-Z/]|^[/.]|/[/.]#
-               or die "bad filename";
+               or die __ "bad filename";
            my $sha256sum = "$entry->{sha256sum}";
            my $sha256sum = "$entry->{sha256sum}";
-           $sha256sum =~ m/^[0-9a-f]+$/ or die "bad sha256sum";
+           $sha256sum =~ m/^[0-9a-f]+$/ or die __ "bad sha256sum";
            push @rows, [ $vsn, "/pool/$component/$filename",
                          $digester, $sha256sum ];
        };
            push @rows, [ $vsn, "/pool/$component/$filename",
                          $digester, $sha256sum ];
        };
-       die "bad ftpmaster api response: $@\n".Dumper($entry)
+       die +(__ "bad ftpmaster api response: ")."$@\n".Dumper($entry)
            if length $@;
     }
     @rows = sort { -version_compare($a->[0],$b->[0]) } @rows;
            if length $@;
     }
     @rows = sort { -version_compare($a->[0],$b->[0]) } @rows;
@@ -1161,15 +1245,15 @@ sub aptget_cache_clean {
 
 sub aptget_lock_acquire () {
     my $lockfile = "$aptget_base/lock";
 
 sub aptget_lock_acquire () {
     my $lockfile = "$aptget_base/lock";
-    open APTGET_LOCK, '>', $lockfile or die "open $lockfile: $!";
-    flock APTGET_LOCK, LOCK_EX or die "lock $lockfile: $!";
+    open APTGET_LOCK, '>', $lockfile or confess "open $lockfile: $!";
+    flock APTGET_LOCK, LOCK_EX or confess "lock $lockfile: $!";
 }
 
 sub aptget_prep ($) {
     my ($data) = @_;
     return if defined $aptget_base;
 
 }
 
 sub aptget_prep ($) {
     my ($data) = @_;
     return if defined $aptget_base;
 
-    badcfg "aptget archive query method takes no data part"
+    badcfg __ "aptget archive query method takes no data part"
        if length $data;
 
     my $cache = $ENV{XDG_CACHE_DIR} // "$ENV{HOME}/.cache";
        if length $data;
 
     my $cache = $ENV{XDG_CACHE_DIR} // "$ENV{HOME}/.cache";
@@ -1184,7 +1268,7 @@ sub aptget_prep ($) {
     ensuredir $aptget_base;
 
     my $quoted_base = $aptget_base;
     ensuredir $aptget_base;
 
     my $quoted_base = $aptget_base;
-    die "$quoted_base contains bad chars, cannot continue"
+    confess "$quoted_base contains bad chars, cannot continue"
        if $quoted_base =~ m/["\\]/; # apt.conf(5) says no escaping :-/
 
     ensuredir $aptget_base;
        if $quoted_base =~ m/["\\]/; # apt.conf(5) says no escaping :-/
 
     ensuredir $aptget_base;
@@ -1200,7 +1284,7 @@ sub aptget_prep ($) {
     cfg_apply_map(\$aptsuites, 'suite map',
                  access_cfg('aptget-suite-map', 'RETURN-UNDEF'));
 
     cfg_apply_map(\$aptsuites, 'suite map',
                  access_cfg('aptget-suite-map', 'RETURN-UNDEF'));
 
-    open SRCS, ">", "$aptget_base/$sourceslist" or die $!;
+    open SRCS, ">", "$aptget_base/$sourceslist" or confess $!;
     printf SRCS "deb-src %s %s %s\n",
        access_cfg('mirror'),
        $aptsuites,
     printf SRCS "deb-src %s %s %s\n",
        access_cfg('mirror'),
        $aptsuites,
@@ -1252,13 +1336,13 @@ END
     my @inreleasefiles = grep { m#/InRelease$# } @releasefiles;
     @releasefiles = @inreleasefiles if @inreleasefiles;
     if (!@releasefiles) {
     my @inreleasefiles = grep { m#/InRelease$# } @releasefiles;
     @releasefiles = @inreleasefiles if @inreleasefiles;
     if (!@releasefiles) {
-       fail <<END;
-apt seemed to not to update dgit's cached Release files for $isuite.
-(Perhaps $cache
+       fail f_ <<END, $isuite, $cache;
+apt seemed to not to update dgit's cached Release files for %s.
+(Perhaps %s
  is on a filesystem mounted `noatime'; if so, please use `relatime'.)
 END
     }
  is on a filesystem mounted `noatime'; if so, please use `relatime'.)
 END
     }
-    die "apt updated too many Release files (@releasefiles), erk"
+    confess "apt updated too many Release files (@releasefiles), erk"
        unless @releasefiles == 1;
 
     ($aptget_releasefile) = @releasefiles;
        unless @releasefiles == 1;
 
     ($aptget_releasefile) = @releasefiles;
@@ -1274,8 +1358,9 @@ sub canonicalise_suite_aptget {
        my $val = $release->{$name};
        if (defined $val) {
            printdebug "release file $name: $val\n";
        my $val = $release->{$name};
        if (defined $val) {
            printdebug "release file $name: $val\n";
-           $val =~ m/^$suite_re$/o or fail
- "Release file ($aptget_releasefile) specifies intolerable $name";
+           $val =~ m/^$suite_re$/o or fail f_
+               "Release file (%s) specifies intolerable %s",
+               $aptget_releasefile, $name;
            cfg_apply_map(\$val, 'suite rmap',
                          access_cfg('aptget-suite-rmap', 'RETURN-UNDEF'));
            return $val
            cfg_apply_map(\$val, 'suite rmap',
                          access_cfg('aptget-suite-rmap', 'RETURN-UNDEF'));
            return $val
@@ -1302,8 +1387,9 @@ sub archive_query_aptget {
        aptget_aptget(), qw(--download-only --only-source source), $package;
 
     my @dscs = <$aptget_base/source/*.dsc>;
        aptget_aptget(), qw(--download-only --only-source source), $package;
 
     my @dscs = <$aptget_base/source/*.dsc>;
-    fail "apt-get source did not produce a .dsc" unless @dscs;
-    fail "apt-get source produced several .dscs (@dscs)" unless @dscs==1;
+    fail __ "apt-get source did not produce a .dsc" unless @dscs;
+    fail f_ "apt-get source produced several .dscs (%s)", "@dscs"
+       unless @dscs==1;
 
     my $pre_dsc = parsecontrol $dscs[0], $dscs[0], 1;
 
 
     my $pre_dsc = parsecontrol $dscs[0], $dscs[0], 1;
 
@@ -1317,6 +1403,7 @@ sub file_in_archive_aptget () { return undef; }
 sub package_not_wholly_new_aptget () { return undef; }
 
 #---------- `dummyapicat' archive query method ----------
 sub package_not_wholly_new_aptget () { return undef; }
 
 #---------- `dummyapicat' archive query method ----------
+# (untranslated, because this is for testing purposes etc.)
 
 sub archive_query_dummycatapi { archive_query_ftpmasterapi @_; }
 sub canonicalise_suite_dummycatapi { canonicalise_suite_ftpmasterapi @_; }
 
 sub archive_query_dummycatapi { archive_query_ftpmasterapi @_; }
 sub canonicalise_suite_dummycatapi { canonicalise_suite_ftpmasterapi @_; }
@@ -1405,10 +1492,11 @@ sub madison_get_parse {
 sub canonicalise_suite_madison {
     # madison canonicalises for us
     my @r = madison_get_parse(@_);
 sub canonicalise_suite_madison {
     # madison canonicalises for us
     my @r = madison_get_parse(@_);
-    @r or fail
-       "unable to canonicalise suite using package $package".
-       " which does not appear to exist in suite $isuite;".
-       " --existing-package may help";
+    @r or fail f_
+       "unable to canonicalise suite using package %s".
+       " which does not appear to exist in suite %s;".
+       " --existing-package may help",
+       $package, $isuite;
     return $r[0][2];
 }
 
     return $r[0][2];
 }
 
@@ -1416,6 +1504,7 @@ sub file_in_archive_madison { return undef; }
 sub package_not_wholly_new_madison { return undef; }
 
 #---------- `sshpsql' archive query method ----------
 sub package_not_wholly_new_madison { return undef; }
 
 #---------- `sshpsql' archive query method ----------
+# (untranslated, because this is obsolete)
 
 sub sshpsql ($$$) {
     my ($data,$runeinfo,$sql) = @_;
 
 sub sshpsql ($$$) {
     my ($data,$runeinfo,$sql) = @_;
@@ -1494,6 +1583,7 @@ sub file_in_archive_sshpsql ($$$) { return undef; }
 sub package_not_wholly_new_sshpsql ($$$) { return undef; }
 
 #---------- `dummycat' archive query method ----------
 sub package_not_wholly_new_sshpsql ($$$) { return undef; }
 
 #---------- `dummycat' archive query method ----------
+# (untranslated, because this is for testing purposes etc.)
 
 sub canonicalise_suite_dummycat ($$) {
     my ($proto,$data) = @_;
 
 sub canonicalise_suite_dummycat ($$) {
     my ($proto,$data) = @_;
@@ -1539,6 +1629,7 @@ sub file_in_archive_dummycat () { return undef; }
 sub package_not_wholly_new_dummycat () { return undef; }
 
 #---------- tag format handling ----------
 sub package_not_wholly_new_dummycat () { return undef; }
 
 #---------- tag format handling ----------
+# (untranslated, because everything should be new tag format by now)
 
 sub access_cfg_tagformats () {
     split /\,/, access_cfg('dgit-tag-format');
 
 sub access_cfg_tagformats () {
     split /\,/, access_cfg('dgit-tag-format');
@@ -1593,12 +1684,12 @@ sub select_tagformat () {
 
 sub canonicalise_suite () {
     return if defined $csuite;
 
 sub canonicalise_suite () {
     return if defined $csuite;
-    fail "cannot operate on $isuite suite" if $isuite eq 'UNRELEASED';
+    fail f_ "cannot operate on %s suite", $isuite if $isuite eq 'UNRELEASED';
     $csuite = archive_query('canonicalise_suite');
     if ($isuite ne $csuite) {
     $csuite = archive_query('canonicalise_suite');
     if ($isuite ne $csuite) {
-       progress "canonical suite name for $isuite is $csuite";
+       progress f_ "canonical suite name for %s is %s", $isuite, $csuite;
     } else {
     } else {
-       progress "canonical suite name is $csuite";
+       progress f_ "canonical suite name is %s", $csuite;
     }
 }
 
     }
 }
 
@@ -1618,13 +1709,13 @@ sub get_archive_dsc () {
            $digester->add($dscdata);
            my $got = $digester->hexdigest();
            $got eq $digest or
            $digester->add($dscdata);
            my $got = $digester->hexdigest();
            $got eq $digest or
-               fail "$dscurl has hash $got but".
-                   " archive told us to expect $digest";
+               fail f_ "%s has hash %s but archive told us to expect %s",
+                       $dscurl, $got, $digest;
        }
        parse_dscdata();
        my $fmt = getfield $dsc, 'Format';
        $format_ok{$fmt} or forceable_fail [qw(unsupported-source-format)],
        }
        parse_dscdata();
        my $fmt = getfield $dsc, 'Format';
        $format_ok{$fmt} or forceable_fail [qw(unsupported-source-format)],
-           "unsupported source format $fmt, sorry";
+           f_ "unsupported source format %s, sorry", $fmt;
            
        $dsc_checked = !!$digester;
        printdebug "get_archive_dsc: Version ".(getfield $dsc, 'Version')."\n";
            
        $dsc_checked = !!$digester;
        printdebug "get_archive_dsc: Version ".(getfield $dsc, 'Version')."\n";
@@ -1651,7 +1742,8 @@ sub check_for_git () {
            # 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;
            # 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;
-           progress "diverting to $divert (using config for $instead_distro)";
+           progress f_ "diverting to %s (using config for %s)",
+                       $divert, $instead_distro;
            return check_for_git();
        }
        failedcmd @cmd unless defined $r and $r =~ m/^[01]$/;
            return check_for_git();
        }
        failedcmd @cmd unless defined $r and $r =~ m/^[01]$/;
@@ -1667,7 +1759,7 @@ sub check_for_git () {
        # curl -sS -I with https_proxy prints
        # HTTP/1.0 200 Connection established
        $result =~ m/^\S+ (404|200) /s or
        # curl -sS -I with https_proxy prints
        # HTTP/1.0 200 Connection established
        $result =~ m/^\S+ (404|200) /s or
-           fail "unexpected results from git check query - ".
+           fail +(__ "unexpected results from git check query - ").
                Dumper($prefix, $result);
        my $code = $1;
        if ($code eq '404') {
                Dumper($prefix, $result);
        my $code = $1;
        if ($code eq '404') {
@@ -1682,7 +1774,7 @@ sub check_for_git () {
     } elsif ($how eq 'false') {
        return 0;
     } else {
     } elsif ($how eq 'false') {
        return 0;
     } else {
-       badcfg "unknown git-check \`$how'";
+       badcfg f_ "unknown git-check \`%s'", $how;
     }
 }
 
     }
 }
 
@@ -1697,7 +1789,7 @@ sub create_remote_git_repo () {
     } elsif ($how eq 'true') {
        # nothing to do
     } else {
     } elsif ($how eq 'true') {
        # nothing to do
     } else {
-       badcfg "unknown git-create \`$how'";
+       badcfg f_ "unknown git-create \`%s'", $how;
     }
 }
 
     }
 }
 
@@ -1734,8 +1826,8 @@ sub remove_stray_gits ($) {
        local $/="\0";
        while (<GITS>) {
            chomp or die;
        local $/="\0";
        while (<GITS>) {
            chomp or die;
-           print STDERR "$us: warning: removing from $what: ",
-               (messagequote $_), "\n";
+           print STDERR f_ "%s: warning: removing from %s: %s\n",
+               $us, $what, (messagequote $_);
            rmtree $_;
        }
     }
            rmtree $_;
        }
     }
@@ -1747,7 +1839,7 @@ sub mktree_in_ud_from_only_subdir ($;$) {
     # changes into the subdir
 
     my (@dirs) = <*/.>;
     # changes into the subdir
 
     my (@dirs) = <*/.>;
-    die "expected one subdir but found @dirs ?" unless @dirs==1;
+    confess "expected one subdir but found @dirs ?" unless @dirs==1;
     $dirs[0] =~ m#^([^/]+)/\.$# or die;
     my $dir = $1;
     changedir $dir;
     $dirs[0] =~ m#^([^/]+)/\.$# or die;
     my $dir = $1;
     changedir $dir;
@@ -1780,7 +1872,7 @@ sub dsc_files_info () {
        foreach (split /\n/, $field) {
            next unless m/\S/;
            m/^(\w+) (\d+) (\S+)$/ or
        foreach (split /\n/, $field) {
            next unless m/\S/;
            m/^(\w+) (\d+) (\S+)$/ or
-               fail "could not parse .dsc $fname line \`$_'";
+               fail f_ "could not parse .dsc %s line \`%s'", $fname, $_;
            my $digester = eval "$module"."->$method;" or die $@;
            push @out, {
                Hash => $1,
            my $digester = eval "$module"."->$method;" or die $@;
            push @out, {
                Hash => $1,
@@ -1791,8 +1883,8 @@ sub dsc_files_info () {
        }
        return @out;
     }
        }
        return @out;
     }
-    fail "missing any supported Checksums-* or Files field in ".
-       $dsc->get_option('name');
+    fail f_ "missing any supported Checksums-* or Files field in %s",
+           $dsc->get_option('name');
 }
 
 sub dsc_files () {
 }
 
 sub dsc_files () {
@@ -1836,8 +1928,9 @@ sub files_compare_inputs (@) {
                if (defined $$re) {
                    $fchecked{$f}{$in_name} = 1;
                    $$re eq $info or
                if (defined $$re) {
                    $fchecked{$f}{$in_name} = 1;
                    $$re eq $info or
-                       fail "hash or size of $f varies in $fname fields".
-                       " (between: ".$showinputs->().")";
+                       fail f_
+              "hash or size of %s varies in %s fields (between: %s)",
+                                $f, $fname, $showinputs->();
                } else {
                    $$re = $info;
                }
                } else {
                    $$re = $info;
                }
@@ -1845,17 +1938,18 @@ sub files_compare_inputs (@) {
            @files = sort @files;
            $expected_files //= \@files;
            "@$expected_files" eq "@files" or
            @files = sort @files;
            $expected_files //= \@files;
            "@$expected_files" eq "@files" or
-               fail "file list in $in_name varies between hash fields!";
+               fail f_ "file list in %s varies between hash fields!",
+                       $in_name;
        }
        $expected_files or
        }
        $expected_files or
-           fail "$in_name has no files list field(s)";
+           fail f_ "%s has no files list field(s)", $in_name;
     }
     printdebug "files_compare_inputs ".Dumper(\%fchecked, \%record)
        if $debuglevel>=2;
 
     grep { keys %$_ == @$inputs-1 } values %fchecked
     }
     printdebug "files_compare_inputs ".Dumper(\%fchecked, \%record)
        if $debuglevel>=2;
 
     grep { keys %$_ == @$inputs-1 } values %fchecked
-       or fail "no file appears in all file lists".
-       " (looked in: ".$showinputs->().")";
+       or fail f_ "no file appears in all file lists (looked in: %s)",
+                  $showinputs->();
 }
 
 sub is_orig_file_in_dsc ($$) {
 }
 
 sub is_orig_file_in_dsc ($$) {
@@ -1867,13 +1961,6 @@ sub is_orig_file_in_dsc ($$) {
     return 1;
 }
 
     return 1;
 }
 
-sub is_orig_file_of_vsn ($$) {
-    my ($f, $upstreamvsn) = @_;
-    my $base = srcfn $upstreamvsn, '';
-    return 0 unless $f =~ m/^\Q$base\E\.$orig_f_tail_re$/;
-    return 1;
-}
-
 # This function determines whether a .changes file is source-only from
 # the point of view of dak.  Thus, it permits *_source.buildinfo
 # files.
 # This function determines whether a .changes file is source-only from
 # the point of view of dak.  Thus, it permits *_source.buildinfo
 # files.
@@ -1901,7 +1988,7 @@ sub test_source_only_changes ($) {
         $l =~ m/\S+$/ or next;
         # \.tar\.[a-z0-9]+ covers orig.tar and the tarballs in native packages
         unless ($& =~ m/(?:\.dsc|\.diff\.gz|\.tar\.[a-z0-9]+|_source\.buildinfo)$/) {
         $l =~ m/\S+$/ or next;
         # \.tar\.[a-z0-9]+ covers orig.tar and the tarballs in native packages
         unless ($& =~ m/(?:\.dsc|\.diff\.gz|\.tar\.[a-z0-9]+|_source\.buildinfo)$/) {
-            print "purportedly source-only changes polluted by $&\n";
+            print f_ "purportedly source-only changes polluted by %s\n", $&;
             return 0;
         }
     }
             return 0;
         }
     }
@@ -1914,7 +2001,7 @@ sub changes_update_origs_from_dsc ($$$$) {
     printdebug "checking origs needed ($upstreamvsn)...\n";
     $_ = getfield $changes, 'Files';
     m/^\w+ \d+ (\S+ \S+) \S+$/m or
     printdebug "checking origs needed ($upstreamvsn)...\n";
     $_ = getfield $changes, 'Files';
     m/^\w+ \d+ (\S+ \S+) \S+$/m or
-       fail "cannot find section/priority from .changes Files field";
+       fail __ "cannot find section/priority from .changes Files field";
     my $placementinfo = $1;
     my %changed;
     printdebug "checking origs needed placement '$placementinfo'...\n";
     my $placementinfo = $1;
     my %changed;
     printdebug "checking origs needed placement '$placementinfo'...\n";
@@ -1926,7 +2013,7 @@ sub changes_update_origs_from_dsc ($$$$) {
        printdebug "origs $file is_orig\n";
        my $have = archive_query('file_in_archive', $file);
        if (!defined $have) {
        printdebug "origs $file is_orig\n";
        my $have = archive_query('file_in_archive', $file);
        if (!defined $have) {
-           print STDERR <<END;
+           print STDERR __ <<END;
 archive does not support .orig check; hope you used --ch:--sa/-sd if needed
 END
            return;
 archive does not support .orig check; hope you used --ch:--sa/-sd if needed
 END
            return;
@@ -1943,25 +2030,27 @@ END
                $_ = $dsc->{$fname};
                next unless defined;
                m/^(\w+) .* \Q$file\E$/m or
                $_ = $dsc->{$fname};
                next unless defined;
                m/^(\w+) .* \Q$file\E$/m or
-                   fail ".dsc $fname missing entry for $file";
+                   fail f_ ".dsc %s missing entry for %s", $fname, $file;
                if ($h->{$archivefield} eq $1) {
                    $same++;
                } else {
                if ($h->{$archivefield} eq $1) {
                    $same++;
                } else {
-                   push @differ,
- "$archivefield: $h->{$archivefield} (archive) != $1 (local .dsc)";
+                   push @differ, f_
+                       "%s: %s (archive) != %s (local .dsc)",
+                       $archivefield, $h->{$archivefield}, $1;
                }
            }
                }
            }
-           die "$file ".Dumper($h)." ?!" if $same && @differ;
+           confess "$file ".Dumper($h)." ?!" if $same && @differ;
            $found_same++
                if $same;
            $found_same++
                if $same;
-           push @found_differ, "archive $h->{filename}: ".join "; ", @differ
+           push @found_differ,
+               f_ "archive %s: %s", $h->{filename}, join "; ", @differ
                if @differ;
        }
        printdebug "origs $file f.same=$found_same".
            " #f._differ=$#found_differ\n";
        if (@found_differ && !$found_same) {
            fail join "\n",
                if @differ;
        }
        printdebug "origs $file f.same=$found_same".
            " #f._differ=$#found_differ\n";
        if (@found_differ && !$found_same) {
            fail join "\n",
-               "archive contains $file with different checksum",
+               (f_ "archive contains %s with different checksum", $file),
                @found_differ;
        }
        # Now we edit the changes file to add or remove it
                @found_differ;
        }
        # Now we edit the changes file to add or remove it
@@ -1979,7 +2068,7 @@ END
                $dsc_data =~ m/^(.* \Q$file\E$)$/m or die "$dsc_data $file ?";
                my $extra = $1;
                $extra =~ s/ \d+ /$&$placementinfo /
                $dsc_data =~ m/^(.* \Q$file\E$)$/m or die "$dsc_data $file ?";
                my $extra = $1;
                $extra =~ s/ \d+ /$&$placementinfo /
-                   or die "$fname $extra >$dsc_data< ?"
+                   or confess "$fname $extra >$dsc_data< ?"
                    if $fname eq 'Files';
                $changes->{$fname} .= "\n". $extra;
                $changed{$file} = "added";
                    if $fname eq 'Files';
                $changes->{$fname} .= "\n". $extra;
                $changed{$file} = "added";
@@ -1988,7 +2077,7 @@ END
     }
     if (%changed) {
        foreach my $file (keys %changed) {
     }
     if (%changed) {
        foreach my $file (keys %changed) {
-           progress sprintf
+           progress f_
                "edited .changes for archive .orig contents: %s %s",
                $changed{$file}, $file;
        }
                "edited .changes for archive .orig contents: %s %s",
                $changed{$file}, $file;
        }
@@ -1997,10 +2086,11 @@ END
        if (act_local()) {
            rename $chtmp,$changesfile or die "$changesfile $!";
        } else {
        if (act_local()) {
            rename $chtmp,$changesfile or die "$changesfile $!";
        } else {
-           progress "[new .changes left in $changesfile]";
+           progress f_ "[new .changes left in %s]", $changesfile;
        }
     } else {
        }
     } else {
-       progress "$changesfile already has appropriate .orig(s) (if any)";
+       progress f_ "%s already has appropriate .orig(s) (if any)",
+                   $changesfile;
     }
 }
 
     }
 }
 
@@ -2009,28 +2099,6 @@ sub make_commit ($) {
     return cmdoutput @git, qw(hash-object -w -t commit), $file;
 }
 
     return cmdoutput @git, qw(hash-object -w -t commit), $file;
 }
 
-sub make_commit_text ($) {
-    my ($text) = @_;
-    my ($out, $in);
-    my @cmd = (@git, qw(hash-object -w -t commit --stdin));
-    debugcmd "|",@cmd;
-    print Dumper($text) if $debuglevel > 1;
-    my $child = open2($out, $in, @cmd) or die $!;
-    my $h;
-    eval {
-       print $in $text or die $!;
-       close $in or die $!;
-       $h = <$out>;
-       $h =~ m/^\w+$/ or die;
-       $h = $&;
-       printdebug "=> $h\n";
-    };
-    close $out;
-    waitpid $child, 0 == $child or die "$child $!";
-    $? and failedcmd @cmd;
-    return $h;
-}
-
 sub clogp_authline ($) {
     my ($clogp) = @_;
     my $author = getfield $clogp, 'Maintainer';
 sub clogp_authline ($) {
     my ($clogp) = @_;
     my $author = getfield $clogp, 'Maintainer';
@@ -2045,8 +2113,9 @@ sub clogp_authline ($) {
     my $date = cmdoutput qw(date), '+%s %z', qw(-d), getfield($clogp,'Date');
     my $authline = "$author $date";
     $authline =~ m/$git_authline_re/o or
     my $date = cmdoutput qw(date), '+%s %z', qw(-d), getfield($clogp,'Date');
     my $authline = "$author $date";
     $authline =~ m/$git_authline_re/o or
-       fail "unexpected commit author line format \`$authline'".
-       " (was generated from changelog Maintainer field)";
+       fail f_ "unexpected commit author line format \`%s'".
+               " (was generated from changelog Maintainer field)",
+               $authline;
     return ($1,$2,$3) if wantarray;
     return $authline;
 }
     return ($1,$2,$3) if wantarray;
     return $authline;
 }
@@ -2059,14 +2128,14 @@ sub vendor_patches_distro ($$) {
     printdebug "checking for vendor-specific $series ($what)\n";
 
     if (!open SERIES, "<", $series) {
     printdebug "checking for vendor-specific $series ($what)\n";
 
     if (!open SERIES, "<", $series) {
-       die "$series $!" unless $!==ENOENT;
+       confess "$series $!" unless $!==ENOENT;
        return;
     }
     while (<SERIES>) {
        next unless m/\S/;
        next if m/^\s+\#/;
 
        return;
     }
     while (<SERIES>) {
        next unless m/\S/;
        next if m/^\s+\#/;
 
-       print STDERR <<END;
+       print STDERR __ <<END;
 
 Unfortunately, this source package uses a feature of dpkg-source where
 the same source package unpacks to different source code on different
 
 Unfortunately, this source package uses a feature of dpkg-source where
 the same source package unpacks to different source code on different
@@ -2079,8 +2148,9 @@ different packages, if different distros are supposed to have
 different code).
 
 END
 different code).
 
 END
-       fail "Found active distro-specific series file for".
-           " $checkdistro ($what): $series, cannot continue";
+       fail f_ "Found active distro-specific series file for".
+               " %s (%s): %s, cannot continue",
+               $checkdistro, $what, $series;
     }
     die "$series $!" if SERIES->error;
     close SERIES;
     }
     die "$series $!" if SERIES->error;
     close SERIES;
@@ -2109,11 +2179,11 @@ sub check_for_vendor_patches () {
     use Dpkg::Vendor;
     vendor_patches_distro($ENV{DEB_VENDOR}, "DEB_VENDOR");
     vendor_patches_distro(Dpkg::Vendor::get_current_vendor(),
     use Dpkg::Vendor;
     vendor_patches_distro($ENV{DEB_VENDOR}, "DEB_VENDOR");
     vendor_patches_distro(Dpkg::Vendor::get_current_vendor(),
-                        "Dpkg::Vendor \`current vendor'");
+                         __ "Dpkg::Vendor \`current vendor'");
     vendor_patches_distro(access_basedistro(),
     vendor_patches_distro(access_basedistro(),
-                         "(base) distro being accessed");
+                         __ "(base) distro being accessed");
     vendor_patches_distro(access_nomdistro(),
     vendor_patches_distro(access_nomdistro(),
-                         "(nominal) distro being accessed");
+                         __ "(nominal) distro being accessed");
 }
 
 sub generate_commits_from_dsc () {
 }
 
 sub generate_commits_from_dsc () {
@@ -2134,12 +2204,12 @@ sub generate_commits_from_dsc () {
            printdebug "linked (using ...,fetch).\n";
        } elsif ((printdebug "($!) "),
                 $! != ENOENT) {
            printdebug "linked (using ...,fetch).\n";
        } elsif ((printdebug "($!) "),
                 $! != ENOENT) {
-           fail "accessing $buildproductsdir/$f,fetch: $!";
+           fail f_ "accessing %s: %s", "$buildproductsdir/$f,fetch", $!;
        } elsif (link_ltarget $upper_f, $f) {
            printdebug "linked.\n";
        } elsif ((printdebug "($!) "),
                 $! != ENOENT) {
        } elsif (link_ltarget $upper_f, $f) {
            printdebug "linked.\n";
        } elsif ((printdebug "($!) "),
                 $! != ENOENT) {
-           fail "accessing $buildproductsdir/$f: $!";
+           fail f_ "accessing %s: %s", "$buildproductsdir/$f", $!;
        } else {
            printdebug "absent.\n";
        }
        } else {
            printdebug "absent.\n";
        }
@@ -2154,14 +2224,14 @@ sub generate_commits_from_dsc () {
            printdebug "linked.\n";
        } elsif ((printdebug "($!) "),
                 $! != EEXIST) {
            printdebug "linked.\n";
        } elsif ((printdebug "($!) "),
                 $! != EEXIST) {
-           fail "saving $buildproductsdir/$f: $!";
+           fail f_ "saving %s: %s", "$buildproductsdir/$f", $!;
        } elsif (!$refetched) {
            printdebug "no need.\n";
        } elsif (link $f, "$upper_f,fetch") {
            printdebug "linked (using ...,fetch).\n";
        } elsif ((printdebug "($!) "),
                 $! != EEXIST) {
        } elsif (!$refetched) {
            printdebug "no need.\n";
        } elsif (link $f, "$upper_f,fetch") {
            printdebug "linked (using ...,fetch).\n";
        } elsif ((printdebug "($!) "),
                 $! != EEXIST) {
-           fail "saving $buildproductsdir/$f,fetch: $!";
+           fail f_ "saving %s: %s", "$buildproductsdir/$f,fetch", $!;
        } else {
            printdebug "cannot.\n";
        }
        } else {
            printdebug "cannot.\n";
        }
@@ -2227,7 +2297,7 @@ sub generate_commits_from_dsc () {
            chdir "_unpack-tar" or die $!;
            open STDIN, "<&", $input or die $!;
            exec @tarcmd;
            chdir "_unpack-tar" or die $!;
            open STDIN, "<&", $input or die $!;
            exec @tarcmd;
-           die "dgit (child): exec $tarcmd[0]: $!";
+           die f_ "dgit (child): exec %s: %s", $tarcmd[0], $!;
        }
        $!=0; (waitpid $tar_pid, 0) == $tar_pid or die $!;
        !$? or failedcmd @tarcmd;
        }
        $!=0; (waitpid $tar_pid, 0) == $tar_pid or die $!;
        !$? or failedcmd @tarcmd;
@@ -2291,7 +2361,7 @@ sub generate_commits_from_dsc () {
     push @cmd, qw(-x --), $dscfn;
     runcmd @cmd;
 
     push @cmd, qw(-x --), $dscfn;
     runcmd @cmd;
 
-    my ($tree,$dir) = mktree_in_ud_from_only_subdir("source package");
+    my ($tree,$dir) = mktree_in_ud_from_only_subdir(__ "source package");
     if (madformat $dsc->{format}) { 
        check_for_vendor_patches();
     }
     if (madformat $dsc->{format}) { 
        check_for_vendor_patches();
     }
@@ -2309,7 +2379,7 @@ sub generate_commits_from_dsc () {
     my $r1clogp;
 
     printdebug "import clog search...\n";
     my $r1clogp;
 
     printdebug "import clog search...\n";
-    parsechangelog_loop \@clogcmd, "package changelog", sub {
+    parsechangelog_loop \@clogcmd, (__ "package changelog"), sub {
        my ($thisstanza, $desc) = @_;
        no warnings qw(exiting);
 
        my ($thisstanza, $desc) = @_;
        no warnings qw(exiting);
 
@@ -2349,7 +2419,7 @@ sub generate_commits_from_dsc () {
        printdebug "import clog $r1clogp->{version} becomes r1\n";
     };
 
        printdebug "import clog $r1clogp->{version} becomes r1\n";
     };
 
-    $clogp or fail "package changelog has no entries!";
+    $clogp or fail __ "package changelog has no entries!";
 
     my $authline = clogp_authline $clogp;
     my $changes = getfield $clogp, 'Changes';
 
     my $authline = clogp_authline $clogp;
     my $changes = getfield $clogp, 'Changes';
@@ -2368,12 +2438,13 @@ sub generate_commits_from_dsc () {
        foreach my $tt (@tartrees) {
            printdebug "import tartree $tt->{F} $tt->{Tree}\n";
 
        foreach my $tt (@tartrees) {
            printdebug "import tartree $tt->{F} $tt->{Tree}\n";
 
+           my $mbody = f_ "Import %s", $tt->{F};
            $tt->{Commit} = make_commit_text($tt->{Orig} ? <<END_O : <<END_T);
 tree $tt->{Tree}
 author $r1authline
 committer $r1authline
 
            $tt->{Commit} = make_commit_text($tt->{Orig} ? <<END_O : <<END_T);
 tree $tt->{Tree}
 author $r1authline
 committer $r1authline
 
-Import $tt->{F}
+$mbody
 
 [dgit import orig $tt->{F}]
 END_O
 
 [dgit import orig $tt->{F}]
 END_O
@@ -2381,7 +2452,7 @@ tree $tt->{Tree}
 author $authline
 committer $authline
 
 author $authline
 committer $authline
 
-Import $tt->{F}
+$mbody
 
 [dgit import tarball $package $cversion $tt->{F}]
 END_T
 
 [dgit import tarball $package $cversion $tt->{F}]
 END_T
@@ -2448,7 +2519,7 @@ END
                chomp $@;
                progress "warning: $@";
                $path = "$absurdity:$path";
                chomp $@;
                progress "warning: $@";
                $path = "$absurdity:$path";
-               progress "$us: trying slow absurd-git-apply...";
+               progress f_ "%s: trying slow absurd-git-apply...", $us;
                rename "../../gbp-pq-output","../../gbp-pq-output.0"
                    or $!==ENOENT
                    or die $!;
                rename "../../gbp-pq-output","../../gbp-pq-output.0"
                    or $!==ENOENT
                    or die $!;
@@ -2467,19 +2538,19 @@ END
                    'exec >/dev/null 2>>../../gbp-pq-output', @showcmd;
                debugcmd "+",@realcmd;
                if (system @realcmd) {
                    'exec >/dev/null 2>>../../gbp-pq-output', @showcmd;
                debugcmd "+",@realcmd;
                if (system @realcmd) {
-                   die +(shellquote @showcmd).
-                       " failed: ".
-                       failedcmd_waitstatus()."\n";
+                   die f_ "%s failed: %s\n",
+                       +(shellquote @showcmd),
+                       failedcmd_waitstatus();
                }
 
                my $gapplied = git_rev_parse('HEAD');
                my $gappliedtree = cmdoutput @git, qw(rev-parse HEAD:);
                $gappliedtree eq $dappliedtree or
                }
 
                my $gapplied = git_rev_parse('HEAD');
                my $gappliedtree = cmdoutput @git, qw(rev-parse HEAD:);
                $gappliedtree eq $dappliedtree or
-                   fail <<END;
+                   fail f_ <<END, $gapplied, $gappliedtree, $dappliedtree;
 gbp-pq import and dpkg-source disagree!
 gbp-pq import and dpkg-source disagree!
- gbp-pq import gave commit $gapplied
- gbp-pq import gave tree $gappliedtree
- dpkg-source --before-build gave tree $dappliedtree
+ gbp-pq import gave commit %s
+ gbp-pq import gave tree %s
+ dpkg-source --before-build gave tree %s
 END
                $rawimport_hash = $gapplied;
            };
 END
                $rawimport_hash = $gapplied;
            };
@@ -2491,11 +2562,11 @@ END
        }
     }
 
        }
     }
 
-    progress "synthesised git commit from .dsc $cversion";
+    progress f_ "synthesised git commit from .dsc %s", $cversion;
 
     my $rawimport_mergeinput = {
         Commit => $rawimport_hash,
 
     my $rawimport_mergeinput = {
         Commit => $rawimport_hash,
-        Info => "Import of source package",
+        Info => __ "Import of source package",
     };
     my @output = ($rawimport_mergeinput);
 
     };
     my @output = ($rawimport_mergeinput);
 
@@ -2506,16 +2577,18 @@ END
            version_compare($oversion, $cversion);
        if ($vcmp < 0) {
            @output = ($rawimport_mergeinput, $lastpush_mergeinput,
            version_compare($oversion, $cversion);
        if ($vcmp < 0) {
            @output = ($rawimport_mergeinput, $lastpush_mergeinput,
-               { Message => <<END, ReverseParents => 1 });
-Record $package ($cversion) in archive suite $csuite
+               { ReverseParents => 1,
+                 Message => (f_ <<END, $package, $cversion, $csuite) });
+Record %s (%s) in archive suite %s
 END
        } elsif ($vcmp > 0) {
 END
        } elsif ($vcmp > 0) {
-           print STDERR <<END or die $!;
+           print STDERR f_ <<END, $cversion, $oversion,
 
 
-Version actually in archive:   $cversion (older)
-Last version pushed with dgit: $oversion (newer or same)
-$later_warning_msg
+Version actually in archive:   %s (older)
+Last version pushed with dgit: %s (newer or same)
+%s
 END
 END
+               __ $later_warning_msg or die $!;
             @output = $lastpush_mergeinput;
         } else {
            # Same version.  Use what's in the server git branch,
             @output = $lastpush_mergeinput;
         } else {
            # Same version.  Use what's in the server git branch,
@@ -2552,15 +2625,15 @@ sub complete_file_from_dsc ($$;$) {
 
     if (stat_exists $tf) {
        if ($checkhash->()) {
 
     if (stat_exists $tf) {
        if ($checkhash->()) {
-           progress "using existing $f";
+           progress f_ "using existing %s", $f;
            return 1;
        }
        if (!$refetched) {
            return 1;
        }
        if (!$refetched) {
-           fail "file $f has hash $got but .dsc".
-               " demands hash $fi->{Hash} ".
-               "(perhaps you should delete this file?)";
+           fail f_ "file %s has hash %s but .dsc demands hash %s".
+                   " (perhaps you should delete this file?)",
+                   $f, $got, $fi->{Hash};
        }
        }
-       progress "need to fetch correct version of $f";
+       progress f_ "need to fetch correct version of %s", $f;
        unlink $tf or die "$tf $!";
        $$refetched = 1;
     } else {
        unlink $tf or die "$tf $!";
        $$refetched = 1;
     } else {
@@ -2576,9 +2649,9 @@ sub complete_file_from_dsc ($$;$) {
     return 0 if !act_local();
 
     $checkhash->() or
     return 0 if !act_local();
 
     $checkhash->() or
-       fail "file $f has hash $got but .dsc".
-           " demands hash $fi->{Hash} ".
-           "(got wrong file from archive!)";
+       fail f_ "file %s has hash %s but .dsc demands hash %s".
+               " (got wrong file from archive!)",
+               $f, $got, $fi->{Hash};
 
     return 1;
 }
 
     return 1;
 }
@@ -2671,7 +2744,7 @@ sub git_lrfetch_sane {
     for (;;) {
        printdebug "git_lrfetch_sane iteration $fetch_iteration\n";
         if (++$fetch_iteration > 10) {
     for (;;) {
        printdebug "git_lrfetch_sane iteration $fetch_iteration\n";
         if (++$fetch_iteration > 10) {
-           fail "too many iterations trying to get sane fetch!";
+           fail __ "too many iterations trying to get sane fetch!";
        }
 
        my @look = map { "refs/$_" } @specs;
        }
 
        my @look = map { "refs/$_" } @specs;
@@ -2685,8 +2758,8 @@ sub git_lrfetch_sane {
            m/^(\w+)\s+(\S+)\n/ or die "ls-remote $_ ?";
            my ($objid,$rrefname) = ($1,$2);
            if (!$wanted_rref->($rrefname)) {
            m/^(\w+)\s+(\S+)\n/ or die "ls-remote $_ ?";
            my ($objid,$rrefname) = ($1,$2);
            if (!$wanted_rref->($rrefname)) {
-               print STDERR <<END;
-warning: git ls-remote @look reported $rrefname; this is silly, ignoring it.
+               print STDERR f_ <<END, "@look", $rrefname;
+warning: git ls-remote %s reported %s; this is silly, ignoring it.
 END
                next;
            }
 END
                next;
            }
@@ -2729,8 +2802,8 @@ END
 git-fetch @fspecs created $lrefname which git ls-remote @look didn't list.
 END
                } else {
 git-fetch @fspecs created $lrefname which git ls-remote @look didn't list.
 END
                } else {
-                   print STDERR <<END
-warning: git fetch @fspecs created $lrefname; this is silly, deleting it.
+                   print STDERR f_ <<END, "@fspecs", $lrefname
+warning: git fetch %s created %s; this is silly, deleting it.
 END
                }
                runcmd_ordryrun_local @git, qw(update-ref -d), $lrefname;
 END
                }
                runcmd_ordryrun_local @git, qw(update-ref -d), $lrefname;
@@ -2744,14 +2817,14 @@ END
            my $want = $wantr{$rrefname};
            next if $got eq $want;
            if (!defined $objgot{$want}) {
            my $want = $wantr{$rrefname};
            next if $got eq $want;
            if (!defined $objgot{$want}) {
-               fail <<END unless act_local();
+               fail __ <<END 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.
 END
 --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.
 END
-               print STDERR <<END;
-warning: git ls-remote suggests we want $lrefname
-warning:  and it should refer to $want
+               print STDERR f_ <<END, $lrefname, $want;
+warning: git ls-remote suggests we want %s
+warning:  and it should refer to %s
 warning:  but git fetch didn't fetch that object to any relevant ref.
 warning:  This may be due to a race with someone updating the server.
 warning:  Will try again...
 warning:  but git fetch didn't fetch that object to any relevant ref.
 warning:  This may be due to a race with someone updating the server.
 warning:  Will try again...
@@ -3115,13 +3188,14 @@ sub fetch_from_archive () {
        if (!$lastpush_hash || $dsc_hash eq $lastpush_hash) {
            @mergeinputs = $dsc_mergeinput
        } elsif (is_fast_fwd($dsc_hash,$lastpush_hash)) {
        if (!$lastpush_hash || $dsc_hash eq $lastpush_hash) {
            @mergeinputs = $dsc_mergeinput
        } elsif (is_fast_fwd($dsc_hash,$lastpush_hash)) {
-           print STDERR <<END or die $!;
+           print STDERR f_ <<END, $dsc_hash, $lastpush_hash,
 
 Git commit in archive is behind the last version allegedly pushed/uploaded.
 
 Git commit in archive is behind the last version allegedly pushed/uploaded.
-Commit referred to by archive: $dsc_hash
-Last version pushed with dgit: $lastpush_hash
-$later_warning_msg
+Commit referred to by archive: %s
+Last version pushed with dgit: %s
+%s
 END
 END
+               __ $later_warning_msg or die $!;
            @mergeinputs = ($lastpush_mergeinput);
        } else {
            # Archive has .dsc which is not a descendant of the last dgit
            @mergeinputs = ($lastpush_mergeinput);
        } else {
            # Archive has .dsc which is not a descendant of the last dgit
@@ -3151,11 +3225,12 @@ END
     } elsif ($lastpush_hash) {
        # only in git, not in the archive yet
        @mergeinputs = ($lastpush_mergeinput);
     } elsif ($lastpush_hash) {
        # only in git, not in the archive yet
        @mergeinputs = ($lastpush_mergeinput);
-       print STDERR <<END or die $!;
+       print STDERR f_ <<END,
 
 Package not found in the archive, but has allegedly been pushed using dgit.
 
 Package not found in the archive, but has allegedly been pushed using dgit.
-$later_warning_msg
+%s
 END
 END
+           __ $later_warning_msg or die $!;
     } else {
        printdebug "nothing found!\n";
        if (defined $skew_warning_vsn) {
     } else {
        printdebug "nothing found!\n";
        if (defined $skew_warning_vsn) {
@@ -3204,10 +3279,7 @@ END
        # here we go, then:
        my $tree_commit = $mergeinputs[0]{Commit};
 
        # here we go, then:
        my $tree_commit = $mergeinputs[0]{Commit};
 
-       my $tree = cmdoutput @git, qw(cat-file commit), $tree_commit;
-       $tree =~ m/\n\n/;  $tree = $`;
-       $tree =~ m/^tree (\w+)$/m or die "$dsc_hash tree ?";
-       $tree = $1;
+       my $tree = get_tree_of_commit $tree_commit;;
 
        # We use the changelog author of the package in question the
        # author of this pseudo-merge.  This is (roughly) correct if
 
        # We use the changelog author of the package in question the
        # author of this pseudo-merge.  This is (roughly) correct if
@@ -3475,7 +3547,7 @@ END
 
 
 sub multisuite_suite_child ($$$) {
 
 
 sub multisuite_suite_child ($$$) {
-    my ($tsuite, $merginputs, $fn) = @_;
+    my ($tsuite, $mergeinputs, $fn) = @_;
     # in child, sets things up, calls $fn->(), and returns undef
     # in parent, returns canonical suite name for $tsuite
     my $canonsuitefh = IO::File::new_tmpfile;
     # in child, sets things up, calls $fn->(), and returns undef
     # in parent, returns canonical suite name for $tsuite
     my $canonsuitefh = IO::File::new_tmpfile;
@@ -3502,7 +3574,7 @@ sub multisuite_suite_child ($$$) {
        return $csuite;
     }
     printdebug "multisuite $tsuite ok (canon=$csuite)\n";
        return $csuite;
     }
     printdebug "multisuite $tsuite ok (canon=$csuite)\n";
-    push @$merginputs, {
+    push @$mergeinputs, {
         Ref => lrref,
         Info => $csuite,
     };
         Ref => lrref,
         Info => $csuite,
     };
@@ -3546,7 +3618,6 @@ sub fork_for_multisuite ($) {
             fetch_one();
            finish 0;
        });
             fetch_one();
            finish 0;
        });
-       # xxx collecte the ref here
 
        $csubsuite =~ s/^\Q$cbasesuite\E-/-/;
        push @csuites, $csubsuite;
 
        $csubsuite =~ s/^\Q$cbasesuite\E-/-/;
        push @csuites, $csubsuite;
@@ -4276,6 +4347,15 @@ END
     my $actualhead = git_rev_parse('HEAD');
 
     if (branch_is_gdr_unstitched_ff($symref, $actualhead, $archive_hash)) {
     my $actualhead = git_rev_parse('HEAD');
 
     if (branch_is_gdr_unstitched_ff($symref, $actualhead, $archive_hash)) {
+       if (quiltmode_splitbrain()) {
+           my ($ffq_prev, $gdrlast) = branch_gdr_info($symref, $actualhead);
+           fail <<END;
+Branch is managed by git-debrebase ($ffq_prev
+exists), but quilt mode ($quilt_mode) implies a split view.
+Pass the right --quilt option or adjust your git config.
+Or, maybe, run git-debrebase forget-was-ever-debrebase.
+END
+       }
        runcmd_ordryrun_local @git_debrebase, 'stitch';
        $actualhead = git_rev_parse('HEAD');
     }
        runcmd_ordryrun_local @git_debrebase, 'stitch';
        $actualhead = git_rev_parse('HEAD');
     }
@@ -4517,9 +4597,8 @@ END
     supplementary_message(<<'END');
 Push failed, while obtaining signatures on the .changes and .dsc.
 If it was just that the signature failed, you may try again by using
     supplementary_message(<<'END');
 Push failed, while obtaining signatures on the .changes and .dsc.
 If it was just that the signature failed, you may try again by using
-debsign by hand to sign the changes
-   $changesfile
-and then dput to complete the upload.
+debsign by hand to sign the changes file (see the command dgit tried,
+above), and then dput that changes file to complete the upload.
 If you need to change the package, you must use a new version number.
 END
     if ($we_are_responder) {
 If you need to change the package, you must use a new version number.
 END
     if ($we_are_responder) {
@@ -5269,29 +5348,7 @@ END
     my $dgitview = git_rev_parse 'HEAD';
 
     changedir $maindir;
     my $dgitview = git_rev_parse 'HEAD';
 
     changedir $maindir;
-    # When we no longer need to support squeeze, use --create-reflog
-    # instead of this:
-    ensuredir "$maindir_gitcommon/logs/refs/dgit-intern";
-    my $makelogfh = new IO::File "$maindir_gitcommon/logs/refs/$splitbraincache", '>>'
-      or die $!;
-
-    my $oldcache = git_get_ref "refs/$splitbraincache";
-    if ($oldcache eq $dgitview) {
-       my $tree = cmdoutput qw(git rev-parse), "$dgitview:";
-       # git update-ref doesn't always update, in this case.  *sigh*
-       my $dummy = make_commit_text <<END;
-tree $tree
-parent $dgitview
-author Dgit <dgit\@example.com> 1000000000 +0000
-committer Dgit <dgit\@example.com> 1000000000 +0000
-
-Dummy commit - do not use
-END
-       runcmd @git, qw(update-ref -m), "dgit $our_version - dummy",
-           "refs/$splitbraincache", $dummy;
-    }
-    runcmd @git, qw(update-ref -m), $cachekey, "refs/$splitbraincache",
-       $dgitview;
+    reflog_cache_insert "refs/$splitbraincache", $cachekey, $dgitview;
 
     changedir "$playground/work";
 
 
     changedir "$playground/work";
 
@@ -5431,13 +5488,20 @@ sub quiltify ($$$$) {
        };
        if ($quilt_mode eq 'linear') {
            print STDERR "\n$us: error: quilt fixup cannot be linear.  Stopped at:\n";
        };
        if ($quilt_mode eq 'linear') {
            print STDERR "\n$us: error: quilt fixup cannot be linear.  Stopped at:\n";
+           my $all_gdr = !!@nots;
            foreach my $notp (@nots) {
                print STDERR "$us:  ", $reportnot->($notp), "\n";
            foreach my $notp (@nots) {
                print STDERR "$us:  ", $reportnot->($notp), "\n";
+               $all_gdr &&= $notp->{Child} &&
+                   (git_cat_file $notp->{Child}{Commit}, 'commit')
+                   =~ m{^\[git-debrebase(?! split[: ]).*\]$}m;
            }
            }
-           print STDERR "$us: $_\n" foreach @$failsuggestion;
+           print STDERR "\n";
+           $failsuggestion =
+               [ grep { $_->[0] ne 'quilt-mode' } @$failsuggestion ]
+               if $all_gdr;
+           print STDERR "$us: $_->[1]\n" foreach @$failsuggestion;
            fail
            fail
- "quilt history linearisation failed.  Search \`quilt fixup' in dgit(7).\n".
- "Use dpkg-source --commit by hand; or, --quilt=smash for one ugly patch";
+ "quilt history linearisation failed.  Search \`quilt fixup' in dgit(7).\n";
        } elsif ($quilt_mode eq 'smash') {
        } elsif ($quilt_mode eq 'auto') {
            progress "quilt fixup cannot be linear, smashing...";
        } elsif ($quilt_mode eq 'smash') {
        } elsif ($quilt_mode eq 'auto') {
            progress "quilt fixup cannot be linear, smashing...";
@@ -5591,7 +5655,7 @@ END
 
     if ($quilt_mode eq 'linear'
        && !$fopts->{'single-debian-patch'}
 
     if ($quilt_mode eq 'linear'
        && !$fopts->{'single-debian-patch'}
-       && branch_is_gdr($symref, $headref)) {
+       && branch_is_gdr($headref)) {
        # This is much faster.  It also makes patches that gdr
        # likes better for future updates without laundering.
        #
        # This is much faster.  It also makes patches that gdr
        # likes better for future updates without laundering.
        #
@@ -5738,6 +5802,31 @@ END
     close $fakedsc or die $!;
 }
 
     close $fakedsc or die $!;
 }
 
+sub quilt_fakedsc2unapplied ($$) {
+    my ($headref, $upstreamversion) = @_;
+    # must be run in the playground
+    # quilt_make_fake_dsc must have been called
+
+    runcmd qw(sh -ec),
+        'exec dpkg-source --no-check --skip-patches -x fake.dsc >/dev/null';
+
+    my $fakexdir= $package.'-'.(stripepoch $upstreamversion);
+    rename $fakexdir, "fake" or die "$fakexdir $!";
+
+    changedir 'fake';
+
+    remove_stray_gits("source package");
+    mktree_in_ud_here();
+
+    rmtree '.pc';
+
+    rmtree 'debian'; # git checkout commitish paths does not delete!
+    runcmd @git, qw(checkout -f), $headref, qw(-- debian);
+    my $unapplied=git_add_write_tree();
+    printdebug "fake orig tree object $unapplied\n";
+    return $unapplied;
+}    
+
 sub quilt_check_splitbrain_cache ($$) {
     my ($headref, $upstreamversion) = @_;
     # Called only if we are in (potentially) split brain mode.
 sub quilt_check_splitbrain_cache ($$) {
     my ($headref, $upstreamversion) = @_;
     # Called only if we are in (potentially) split brain mode.
@@ -5769,26 +5858,12 @@ sub quilt_check_splitbrain_cache ($$) {
     push @cachekey, $srcshash->hexdigest();
     $splitbrain_cachekey = "@cachekey";
 
     push @cachekey, $srcshash->hexdigest();
     $splitbrain_cachekey = "@cachekey";
 
-    my @cmd = (@git, qw(log -g), '--pretty=format:%H %gs',
-              $splitbraincache);
     printdebug "splitbrain cachekey $splitbrain_cachekey\n";
     printdebug "splitbrain cachekey $splitbrain_cachekey\n";
-    debugcmd "|(probably)",@cmd;
-    my $child = open GC, "-|";  defined $child or die $!;
-    if (!$child) {
-       chdir $maindir or die $!;
-       if (!stat "$maindir_gitcommon/logs/refs/$splitbraincache") {
-           $! == ENOENT or die $!;
-           printdebug ">(no reflog)\n";
-           finish 0;
-       }
-       exec @cmd; die $!;
-    }
-    while (<GC>) {
-       chomp;
-       printdebug ">| ", $_, "\n" if $debuglevel > 1;
-       next unless m/^(\w+) (\S.*\S)$/ && $2 eq $splitbrain_cachekey;
-           
-       my $cachehit = $1;
+
+    my $cachehit = reflog_cache_lookup
+       "refs/$splitbraincache", $splitbrain_cachekey;
+
+    if ($cachehit) {
        unpack_playtree_mkwork($headref);
        my $saved = maybe_split_brain_save $headref, $cachehit, "cache-hit";
        if ($cachehit ne $headref) {
        unpack_playtree_mkwork($headref);
        my $saved = maybe_split_brain_save $headref, $cachehit, "cache-hit";
        if ($cachehit ne $headref) {
@@ -5800,8 +5875,6 @@ sub quilt_check_splitbrain_cache ($$) {
        progress "dgit view: found cached, no changes required";
        return ($headref, $splitbrain_cachekey);
     }
        progress "dgit view: found cached, no changes required";
        return ($headref, $splitbrain_cachekey);
     }
-    die $! if GC->error;
-    failedcmd unless close GC;
 
     printdebug "splitbrain cache miss\n";
     return (undef, $splitbrain_cachekey);
 
     printdebug "splitbrain cache miss\n";
     return (undef, $splitbrain_cachekey);
@@ -5891,24 +5964,7 @@ sub quilt_fixup_multipatch ($$$) {
            quilt_check_splitbrain_cache($headref, $upstreamversion);
        return if $cachehit;
     }
            quilt_check_splitbrain_cache($headref, $upstreamversion);
        return if $cachehit;
     }
-
-    runcmd qw(sh -ec),
-        'exec dpkg-source --no-check --skip-patches -x fake.dsc >/dev/null';
-
-    my $fakexdir= $package.'-'.(stripepoch $upstreamversion);
-    rename $fakexdir, "fake" or die "$fakexdir $!";
-
-    changedir 'fake';
-
-    remove_stray_gits("source package");
-    mktree_in_ud_here();
-
-    rmtree '.pc';
-
-    rmtree 'debian'; # git checkout commitish paths does not delete!
-    runcmd @git, qw(checkout -f), $headref, qw(-- debian);
-    my $unapplied=git_add_write_tree();
-    printdebug "fake orig tree object $unapplied\n";
+    my $unapplied=quilt_fakedsc2unapplied($headref, $upstreamversion);
 
     ensuredir '.pc';
 
 
     ensuredir '.pc';
 
@@ -5986,12 +6042,21 @@ END
 
     my @failsuggestion;
     if (!($diffbits->{O2H} & $diffbits->{O2A})) {
 
     my @failsuggestion;
     if (!($diffbits->{O2H} & $diffbits->{O2A})) {
-        push @failsuggestion, "This might be a patches-unapplied branch.";
-    }  elsif (!($diffbits->{H2A} & $diffbits->{O2A})) {
-        push @failsuggestion, "This might be a patches-applied branch.";
+        push @failsuggestion, [ 'unapplied',
+                              "This might be a patches-unapplied branch." ];
+    } elsif (!($diffbits->{H2A} & $diffbits->{O2A})) {
+        push @failsuggestion, [ 'applied',
+                               "This might be a patches-applied branch." ];
     }
     }
-    push @failsuggestion, "Maybe you need to specify one of".
-        " --[quilt=]gbp --[quilt=]dpm --quilt=unapplied ?";
+    push @failsuggestion, [ 'quilt-mode',
+ "Maybe you need one of --[quilt=]gbp --[quilt=]dpm --quilt=unapplied ?" ];
+
+    push @failsuggestion, [ 'gitattrs',
+ "Warning: Tree has .gitattributes.  See GITATTRIBUTES in dgit(7)." ]
+       if stat_exists '.gitattributes';
+
+    push @failsuggestion, [ 'origs',
+ "Maybe orig tarball(s) are not identical to git representation?" ];
 
     if (quiltmode_splitbrain()) {
        quiltify_splitbrain($clogp, $unapplied, $headref, $oldtiptree,
 
     if (quiltmode_splitbrain()) {
        quiltify_splitbrain($clogp, $unapplied, $headref, $oldtiptree,
@@ -6198,16 +6263,27 @@ sub massage_dbp_args ($;$) {
     my $dmode = '-F';
     foreach my $l ($cmd, $xargs) {
        next unless $l;
     my $dmode = '-F';
     foreach my $l ($cmd, $xargs) {
        next unless $l;
-       @$l = grep { !(m/^-[SgGFABb]$/s and $dmode=$_) } @$l;
+       @$l = grep { !(m/^-[SgGFABb]$|^--build=/s and $dmode=$_) } @$l;
     }
     push @$cmd, '-nc';
 #print STDERR "MASS1 ",Dumper($cmd, $xargs, $dmode);
     my $r = WANTSRC_BUILDER;
     printdebug "massage split $dmode.\n";
     }
     push @$cmd, '-nc';
 #print STDERR "MASS1 ",Dumper($cmd, $xargs, $dmode);
     my $r = WANTSRC_BUILDER;
     printdebug "massage split $dmode.\n";
-    $r = $dmode =~ m/[S]/  ?  WANTSRC_SOURCE :
-      $dmode =~ y/gGF/ABb/ ?  WANTSRC_SOURCE | WANTSRC_BUILDER :
-      $dmode =~ m/[ABb]/   ?                   WANTSRC_BUILDER :
-      die "$dmode ?";
+    if ($dmode =~ s/^--build=//) {
+       $r = 0;
+       my @d = split /,/, $dmode;
+       $r |= WANTSRC_SOURCE  if grep { s/^full$/binary/ } @d;
+       $r |= WANTSRC_SOURCE  if grep { s/^source$// } @d;
+       $r |= WANTSRC_BUILDER if grep { m/./ } @d;
+       fail "Wanted to build nothing!" unless $r;
+       $dmode = '--build='. join ',', grep m/./, @d;
+    } else {
+       $r =
+         $dmode =~ m/[S]/     ?  WANTSRC_SOURCE :
+         $dmode =~ y/gGF/ABb/ ?  WANTSRC_SOURCE | WANTSRC_BUILDER :
+         $dmode =~ m/[ABb]/   ?                   WANTSRC_BUILDER :
+         die "$dmode ?";
+    }
     printdebug "massage done $r $dmode.\n";
     push @$cmd, $dmode;
 #print STDERR "MASS2 ",Dumper($cmd, $xargs, $r);
     printdebug "massage done $r $dmode.\n";
     push @$cmd, $dmode;
 #print STDERR "MASS2 ",Dumper($cmd, $xargs, $r);
@@ -6296,9 +6372,10 @@ sub postbuild_mergechanges_vanilla ($) {
 sub cmd_build {
     build_prep_early();
     $buildproductsdir eq '..' or print STDERR <<END;
 sub cmd_build {
     build_prep_early();
     $buildproductsdir eq '..' or print STDERR <<END;
-$us: warning: build-products-dir set, but not supported by dgit build
-$us: warning: things may go wrong or files may go to the wrong place
+$us: warning: build-products-dir set, but not supported by dpkg-buildpackage
+$us: warning: build-products-dir will be ignored; files will go to ..
 END
 END
+    $buildproductsdir = '..';
     my @dbp = (@dpkgbuildpackage, qw(-us -uc), changesopts_initial(), @ARGV);
     my $wantsrc = massage_dbp_args \@dbp;
     build_prep($wantsrc);
     my @dbp = (@dpkgbuildpackage, qw(-us -uc), changesopts_initial(), @ARGV);
     my $wantsrc = massage_dbp_args \@dbp;
     build_prep($wantsrc);
@@ -6550,6 +6627,24 @@ sub cmd_quilt_fixup {
     build_maybe_quilt_fixup();
 }
 
     build_maybe_quilt_fixup();
 }
 
+sub cmd_print_unapplied_treeish {
+    badusage "incorrect arguments to dgit print-unapplied-treeish" if @ARGV;
+    my $headref = git_rev_parse('HEAD');
+    my $clogp = commit_getclogp $headref;
+    $package = getfield $clogp, 'Source';
+    $version = getfield $clogp, 'Version';
+    $isuite = getfield $clogp, 'Distribution';
+    $csuite = $isuite; # we want this to be offline!
+    notpushing();
+
+    prep_ud();
+    changedir $playground;
+    my $uv = upstreamversion $version;
+    quilt_make_fake_dsc($uv);
+    my $u = quilt_fakedsc2unapplied($headref, $uv);
+    print $u, "\n" or die $!;
+}
+
 sub import_dsc_result {
     my ($dstref, $newhash, $what_log, $what_msg) = @_;
     my @cmd = (git_update_ref_cmd $what_log, $dstref, $newhash);
 sub import_dsc_result {
     my ($dstref, $newhash, $what_log, $what_msg) = @_;
     my @cmd = (git_update_ref_cmd $what_log, $dstref, $newhash);
@@ -6953,7 +7048,10 @@ sub parseopts () {
            } elsif (m/^--delayed=(\d+)$/s) {
                push @ropts, $_;
                push @dput, $_;
            } elsif (m/^--delayed=(\d+)$/s) {
                push @ropts, $_;
                push @dput, $_;
-           } elsif (my ($k,$v) = m/^--(dgit-view)-save=(.+)$/s) {
+           } elsif (my ($k,$v) =
+                    m/^--save-(dgit-view)=(.+)$/s ||
+                    m/^--(dgit-view)-save=(.+)$/s
+                    ) {
                push @ropts, $_;
                $v =~ s#^(?!refs/)#refs/heads/#;
                $internal_object_save{$k} = $v;
                push @ropts, $_;
                $v =~ s#^(?!refs/)#refs/heads/#;
                $internal_object_save{$k} = $v;
@@ -7129,7 +7227,7 @@ sub parseopts_late_defaults () {
        $$vr = $v;
     }
 
        $$vr = $v;
     }
 
-    fail "dgit: --include-dirty is not supported in split view quilt mode"
+    fail __ "dgit: --include-dirty is not supported in split view quilt mode"
        if $split_brain && $includedirty;
 
     if (!defined $cleanmode) {
        if $split_brain && $includedirty;
 
     if (!defined $cleanmode) {
@@ -7147,6 +7245,9 @@ sub parseopts_late_defaults () {
     $bpd_glob =~ s#[][\\{}*?~]#\\$&#g;
 }
 
     $bpd_glob =~ s#[][\\{}*?~]#\\$&#g;
 }
 
+setlocale(LC_MESSAGES, "");
+textdomain("dgit");
+
 if ($ENV{$fakeeditorenv}) {
     git_slurp_config();
     quilt_fixup_editor();
 if ($ENV{$fakeeditorenv}) {
     git_slurp_config();
     quilt_fixup_editor();
@@ -7159,7 +7260,7 @@ print STDERR "DRY RUN ONLY\n" if $dryrun_level > 1;
 print STDERR "DAMP RUN - WILL MAKE LOCAL (UNSIGNED) CHANGES\n"
     if $dryrun_level == 1;
 if (!@ARGV) {
 print STDERR "DAMP RUN - WILL MAKE LOCAL (UNSIGNED) CHANGES\n"
     if $dryrun_level == 1;
 if (!@ARGV) {
-    print STDERR $helpmsg or die $!;
+    print STDERR __ $helpmsg or die $!;
     finish 8;
 }
 $cmd = $subcommand = shift @ARGV;
     finish 8;
 }
 $cmd = $subcommand = shift @ARGV;