chiark / gitweb /
changelog: start 9.10
[dgit.git] / git-debrebase
index e6d75b7efa3dd9c98ae5fd2ac3ad6813f180df67..06f56d872abe5bcf98a966ae61bf2d2080a7a49a 100755 (executable)
@@ -3,7 +3,8 @@
 # Script helping make fast-forwarding histories while still rebasing
 # upstream deltas when working on Debian packaging
 #
-# Copyright (C)2017,2018 Ian Jackson
+# Copyright (C)2017-2019 Ian Jackson
+# Copyright (C)2019      Niko Tyni
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -47,7 +48,7 @@ usages:
   git-debrebase [<options>] prepush [--prose=...]
   git-debrebase [<options>] quick|conclude
   git-debrebase [<options>] new-upstream <new-version> [<details ...>]
-  git-debrebase [<options>] convert-from-gbp [<upstream-commitish>]
+  git-debrebase [<options>] convert-from-* ...
   ...
 See git-debrebase(1), git-debrebase(5), dgit-maint-debrebase(7) (in dgit).
 END
@@ -120,7 +121,6 @@ our $playprefix = 'debrebase';
 our $rd;
 our $workarea;
 
-our @git = qw(git);
 our @dgit = qw(dgit);
 
 sub in_workarea ($) {
@@ -144,10 +144,10 @@ sub run_ref_updates_now ($$) {
 
     my @upd_cmd = (git_update_ref_cmd "debrebase: $mrest", qw(--stdin));
     debugcmd '>|', @upd_cmd;
-    open U, "|-", @upd_cmd or confess $!;
+    open U, "|-", @upd_cmd or confess "$!";
     foreach (@$updates) {
        printdebug ">= ", $_, "\n";
-       print U $_, "\n" or confess $!;
+       print U $_, "\n" or confess "$!";
     }
     printdebug ">\$\n";
     close U or failedcmd @upd_cmd;
@@ -355,44 +355,6 @@ sub calculate_committer_authline () {
     return $&;
 }
 
-sub rm_subdir_cached ($) {
-    my ($subdir) = @_;
-    runcmd @git, qw(rm --quiet -rf --cached --ignore-unmatch), $subdir;
-}
-
-sub read_tree_subdir ($$) {
-    my ($subdir, $new_tree_object) = @_;
-    rm_subdir_cached $subdir;
-    runcmd @git, qw(read-tree), "--prefix=$subdir/", $new_tree_object;
-}
-
-sub read_tree_debian ($) {
-    my ($treeish) = @_;
-    read_tree_subdir 'debian', "$treeish:debian";
-    rm_subdir_cached 'debian/patches';
-}
-
-sub read_tree_upstream ($;$$) {
-    my ($treeish, $keep_patches, $tree_with_debian) = @_;
-    # if $tree_with_debian is supplied, will use that for debian/
-    # otherwise will save and restore it.
-    my $debian =
-       $tree_with_debian ? "$tree_with_debian:debian"
-       : cmdoutput @git, qw(write-tree --prefix=debian/);
-    runcmd @git, qw(read-tree), $treeish;
-    read_tree_subdir 'debian', $debian;
-    rm_subdir_cached 'debian/patches' unless $keep_patches;
-};
-
-sub make_commit ($$) {
-    my ($parents, $message_paras) = @_;
-    my $tree = cmdoutput @git, qw(write-tree);
-    my @cmd = (@git, qw(commit-tree), $tree);
-    push @cmd, qw(-p), $_ foreach @$parents;
-    push @cmd, qw(-m), $_ foreach @$message_paras;
-    return cmdoutput @cmd;
-}
-
 our @snag_force_opts;
 sub snag ($$;@) {
     my ($tag,$msg) = @_; # ignores extra args, for benefit of keycommits
@@ -611,7 +573,7 @@ sub merge_series ($$$;@) {
            if ($any) {
                open S, $seriesfile or confess "$seriesfile $!";
                while (my $patch = <S>) {
-                   chomp $patch or confess $!;
+                   chomp $patch or confess "$!";
                    $prereq{$patch} //= {};
                    foreach my $earlier (@earlier) {
                        $prereq{$patch}{$earlier}{$s}++ and confess;
@@ -708,7 +670,7 @@ sub merge_series ($$$;@) {
            };
        };
 
-       open NS, '>', $seriesfile or confess $!;
+       open NS, '>', $seriesfile or confess "$!";
 
        while (keys %prereq) {
            my $best;
@@ -723,7 +685,7 @@ sub merge_series ($$$;@) {
                $best = $try;
            }
            printdebug "merge_series series next $best\n";
-           print NS "$best\n" or confess $!;
+           print NS "$best\n" or confess "$!";
            delete $prereq{$best};
            foreach my $gp (values %prereq) {
                delete $gp->{$best};
@@ -765,10 +727,10 @@ sub merge_series_patchqueue_convert ($$$) {
            my $tree = cmdoutput @git, qw(write-tree);
            $commit =~ s{^parent (\S+)$}{parent $build}m or confess;
            $commit =~ s{^tree (\S+)$}{tree $tree}m      or confess;
-           open C, ">", "../mcommit" or confess $!;
-           print C $commit or confess $!;
-           close C or confess $!;
-           $build = cmdoutput @git, qw(hash-object -w -t commit ../mcommit);
+           open C, ">", "../mcommit" or confess "$!";
+           print C $commit or confess "$!";
+           close C or confess "$!";
+           $build = hash_commit '../mcommit';
        }
        $result = $build;
        mwrecknote($wrecknotes, 'merged-result', $result);
@@ -1586,7 +1548,7 @@ sub walk ($;$$$) {
     confess __ "internal error" unless $build eq (pop @processed)->{CommitId};
 
     in_workarea sub {
-       mkdir $rd or $!==EEXIST or confess $!;
+       mkdir $rd or $!==EEXIST or confess "$!";
        my $current_method;
        my $want_debian = $build;
        my $want_upstream = $build;
@@ -1696,9 +1658,9 @@ sub walk ($;$$$) {
                        or confess "$ch ?";
                }
                my $cf = "$rd/m$rewriting";
-               open CD, ">", $cf or confess $!;
-               print CD $ch, "\n", $cl->{Msg} or confess $!;
-               close CD or confess $!;
+               open CD, ">", $cf or confess "$!";
+               print CD $ch, "\n", $cl->{Msg} or confess "$!";
+               close CD or confess "$!";
                my @cmd = (@git, qw(hash-object));
                push @cmd, qw(-w) if $rewriting;
                push @cmd, qw(-t commit), $cf;
@@ -1796,7 +1758,7 @@ sub cmd_launder_v0 () {
 
 sub defaultcmd_rebase () {
     push @ARGV, @{ $opt_defaultcmd_interactive // [] };
-    my ($tip,$breakwater) = do_launder_head 'launder for rebase';
+    my ($tip,$breakwater) = do_launder_head __ 'launder for rebase';
     runcmd @git, qw(rebase), @ARGV, $breakwater if @ARGV;
 }
 
@@ -1811,7 +1773,7 @@ sub cmd_analyse () {
        $old = git_rev_parse 'HEAD';
     }
     my ($dummy,$breakwater) = walk $old, 1,*STDOUT;
-    STDOUT->error and confess $!;
+    STDOUT->error and confess "$!";
 }
 
 sub ffq_check ($;$$) {
@@ -1828,7 +1790,7 @@ sub ffq_check ($;$$) {
     # normally $currentval should be HEAD
     my ($currentval, $ff, $notff) =@_;
 
-    $ff //= sub { print $_[0] or confess $!; };
+    $ff //= sub { print $_[0] or confess "$!"; };
     $notff //= \&snag;
 
     my ($status, $message, $current, $ffq_prev, $gdrlast)
@@ -1994,36 +1956,6 @@ sub do_stitch ($;$) {
     stitch($dangling_head, $ffq_prev, $gdrlast, $ffq_prev_commitish, $prose);
 }
 
-sub upstream_commitish_search ($$) {
-    my ($upstream_version, $tried) = @_;
-    # todo: at some point maybe use git-deborig to do this
-    foreach my $tagpfx ('', 'v', 'upstream/') {
-       my $tag = $tagpfx.(dep14_version_mangle $upstream_version);
-       my $new_upstream = git_get_ref "refs/tags/$tag";
-       push @$tried, $tag;
-       return $new_upstream if length $new_upstream;
-    }
-}
-
-sub resolve_upstream_version ($$) {
-    my ($new_upstream, $upstream_version) = @_;
-
-    if (!defined $new_upstream) {
-       my @tried;
-       $new_upstream = upstream_commitish_search $upstream_version, \@tried;
-       if (!length $new_upstream) {
-           fail f_
-               "Could not determine appropriate upstream commitish.\n".
-               " (Tried these tags: %s)\n".
-               " Check version, and specify upstream commitish explicitly.",
-               "@tried";
-       }
-    }
-    $new_upstream = git_rev_parse $new_upstream;
-
-    return $new_upstream;
-}
-
 sub cmd_new_upstream () {
     # automatically and unconditionally launders before rebasing
     # if rebase --abort is used, laundering has still been done
@@ -2043,7 +1975,8 @@ sub cmd_new_upstream () {
 
     my $new_upstream = shift @ARGV;
     my $new_upstream_version = upstreamversion  $new_version;
-    $new_upstream =
+    my $new_upstream_used;
+    ($new_upstream, $new_upstream_used) =
        resolve_upstream_version $new_upstream, $new_upstream_version;
 
     record_ffq_auto();
@@ -2257,7 +2190,7 @@ sub cmd_record_ffq_prev () {
     badusage "no arguments allowed" if @ARGV;
     my ($status, $msg) = record_ffq_prev_deferred();
     if ($status eq 'exists' && $opt_noop_ok) {
-       print __ "Previous head already recorded\n" or confess $!;
+       print __ "Previous head already recorded\n" or confess "$!";
     } elsif ($status eq 'deferred') {
        run_deferred_updates 'record-ffq-prev';
     } else {
@@ -2268,13 +2201,13 @@ sub cmd_record_ffq_prev () {
 sub cmd_anchor () {
     badusage __ "no arguments allowed" if @ARGV;
     my ($anchor, $bw) = keycommits +(git_rev_parse 'HEAD'), 0,0;
-    print "$anchor\n" or confess $!;
+    print "$anchor\n" or confess "$!";
 }
 
 sub cmd_breakwater () {
     badusage __ "no arguments allowed" if @ARGV;
     my ($anchor, $bw) = keycommits +(git_rev_parse 'HEAD'), 0,0;
-    print "$bw\n" or confess $!;
+    print "$bw\n" or confess "$!";
 }
 
 sub cmd_status () {
@@ -2300,22 +2233,22 @@ sub cmd_status () {
        $newest //= $oldest;
     };
     my ($anchor, $bw) = keycommits +(git_rev_parse 'HEAD'),
-       sub { $note->(1, 'branch contains furniture (not laundered)', @_); },
-       sub { $note->(2, 'branch is unlaundered', @_); },
-       sub { $note->(3, 'branch needs laundering', @_); },
-       sub { $note->(4, 'branch not in git-debrebase form', @_); };
+       sub { $note->(1, __ 'branch contains furniture (not laundered)',@_); },
+       sub { $note->(2, __ 'branch is unlaundered', @_); },
+       sub { $note->(3, __ 'branch needs laundering', @_); },
+       sub { $note->(4, __ 'branch not in git-debrebase form', @_); };
 
     my $prcommitinfo = sub {
        my ($cid) = @_;
-       flush STDOUT or confess $!;
+       flush STDOUT or confess "$!";
        runcmd @git, qw(--no-pager log -n1),
            '--pretty=format:    %h %s%n',
            $cid;
     };
 
-    print "current branch contents, in git-debrebase terms:\n";
+    print __ "current branch contents, in git-debrebase terms:\n";
     if (!$oldest->{Badness}) {
-       print "  branch is laundered\n";
+       print __ "  branch is laundered\n";
     } else {
        print "  $oldest->{OurMsg}\n";
        my $printed = '';
@@ -2331,44 +2264,44 @@ sub cmd_status () {
     my $prab = sub {
        my ($cid, $what) = @_;
        if (!defined $cid) {
-           print "  $what is not well-defined\n";
+           print f_ "  %s is not well-defined\n", $what;
        } else {
            print "  $what\n";
            $prcommitinfo->($cid);
        }
     };
-    print "key git-debrebase commits:\n";
-    $prab->($anchor, 'anchor');
-    $prab->($bw, 'breakwater');
+    print __ "key git-debrebase commits:\n";
+    $prab->($anchor, __ 'anchor');
+    $prab->($bw, __ 'breakwater');
 
     my ($ffqstatus, $ffq_msg, $current, $ffq_prev, $gdrlast) =
        ffq_prev_branchinfo();
 
-    print "branch and ref status, in git-debrebase terms:\n";
+    print __ "branch and ref status, in git-debrebase terms:\n";
     if ($ffq_msg) {
        print "  $ffq_msg\n";
     } else {
        $ffq_prev = git_get_ref $ffq_prev;
        $gdrlast = git_get_ref $gdrlast;
        if ($ffq_prev) {
-           print "  unstitched; previous tip was:\n";
+           print __ "  unstitched; previous tip was:\n";
            $prcommitinfo->($ffq_prev);
        } elsif (!$gdrlast) {
-           print "  stitched? (no record of git-debrebase work)\n";
+           print __ "  stitched? (no record of git-debrebase work)\n";
        } elsif (is_fast_fwd $gdrlast, 'HEAD') {
-           print "  stitched\n";
+           print __ "  stitched\n";
        } else {
-           print "  not git-debrebase (diverged since last stitch)\n"
+           print __ "  not git-debrebase (diverged since last stitch)\n"
        }
     }
-    print "you are currently rebasing\n" if currently_rebasing();
+    print __ "you are currently rebasing\n" if currently_rebasing();
 }
 
 sub cmd_stitch () {
     my $prose = 'stitch';
     getoptions("stitch",
               'prose=s', \$prose);
-    badusage "no arguments allowed" if @ARGV;
+    badusage __ "no arguments allowed" if @ARGV;
     do_stitch $prose, 0;
 }
 sub cmd_prepush () {
@@ -2377,21 +2310,21 @@ sub cmd_prepush () {
 }
 
 sub cmd_quick () {
-    badusage "no arguments allowed" if @ARGV;
-    do_launder_head 'launder for git-debrebase quick';
+    badusage __ "no arguments allowed" if @ARGV;
+    do_launder_head __ 'launder for git-debrebase quick';
     do_stitch 'quick';
 }
 
 sub cmd_conclude () {
     my ($ffq_prev, $gdrlast, $ffq_prev_commitish) = ffq_prev_info();
     if (!$ffq_prev_commitish) {
-       fail "No ongoing git-debrebase session." unless $opt_noop_ok;
+       fail __ "No ongoing git-debrebase session." unless $opt_noop_ok;
        return;
     }
     my $dangling_head = get_head();
     
     badusage "no arguments allowed" if @ARGV;
-    do_launder_head 'launder for git-debrebase quick';
+    do_launder_head __ 'launder for git-debrebase quick';
     do_stitch 'quick';
 }
 
@@ -2401,7 +2334,7 @@ sub cmd_scrap () {
        push @deferred_updates, 'verify HEAD HEAD';
        # noop, but stops us complaining that scrap was a noop
     }
-    badusage "no arguments allowed" if @ARGV;
+    badusage __ "no arguments allowed" if @ARGV;
     my ($ffq_prev, $gdrlast, $ffq_prev_commitish) = ffq_prev_info();
     my $scrapping_head;
     if ($ffq_prev_commitish) {
@@ -2415,7 +2348,7 @@ sub cmd_scrap () {
            "delete $merge_cache_ref";
     }
     if (!@deferred_updates) {
-       fail "No ongoing git-debrebase session." unless $opt_noop_ok;
+       fail __ "No ongoing git-debrebase session." unless $opt_noop_ok;
        finish 0;
     }
     snags_maybe_bail();
@@ -2454,7 +2387,7 @@ sub make_patches ($) {
            rm_subdir_cached 'debian/patches';
        }
        $out = make_commit [$head], [
-            'Commit patch queue (exported by git-debrebase)',
+            (__ 'Commit patch queue (exported by git-debrebase)'),
             '[git-debrebase make-patches: export and commit patches]',
         ];
     };
@@ -2465,22 +2398,23 @@ sub cmd_make_patches () {
     my $opt_quiet_would_amend;
     getoptions("make-patches",
               'quiet-would-amend!', \$opt_quiet_would_amend);
-    badusage "no arguments allowed" if @ARGV;
+    badusage __ "no arguments allowed" if @ARGV;
     bail_if_rebasing();
     my $old_head = get_head();
     my $new = make_patches $old_head;
     my $d = get_differs $old_head, $new;
     if ($d == 0) {
-       fail "No (more) patches to export." unless $opt_noop_ok;
+       fail __ "No (more) patches to export." unless $opt_noop_ok;
        return;
     } elsif ($d == D_PAT_ADD) {
        snags_maybe_bail();
        update_head_checkout $old_head, $new, 'make-patches';
     } else {
-       print STDERR failmsg
+       print STDERR failmsg f_
            "Patch export produced patch amendments".
-           " (abandoned output commit $new).".
-           "  Try laundering first."
+           " (abandoned output commit %s).".
+           "  Try laundering first.",
+           $new
            unless $opt_quiet_would_amend;
        finish 7;
     }
@@ -2496,18 +2430,20 @@ sub check_series_has_all_patches ($) {
     our $comments_snagged;
     foreach my $f (grep /\S/, grep {!m/^\s\#/} split /\n/, $series) {
        if ($f =~ m/^\s*\#/) {
-           snag 'series-comments',
-               "$seriesfn contains comments, which will be discarded"
+           snag 'series-comments', f_
+               "%s contains comments, which will be discarded",
+               $seriesfn
                unless $comments_snagged++;
            next;
        }
-       fail "patch $f repeated in $seriesfn !" if $series{$f}++;
+       fail f_ "patch %s repeated in %s !", $f, $seriesfn if $series{$f}++;
     }
     foreach my $patchfile (get_tree "$head:debian/patches", 1,1) {
        my ($f,$i) = @$patchfile;
        next if $series{$f};
        next if $f eq 'series';
-       snag 'unused-patches', "Unused patch file $f will be discarded";
+       snag 'unused-patches', f_
+           "Unused patch file %s will be discarded", $f;
     }
 }
 
@@ -2515,11 +2451,11 @@ sub begin_convert_from () {
     my $head = get_head();
     my ($ffqs, $ffqm, $symref, $ffq_prev, $gdrlast) = ffq_prev_branchinfo();
 
-    fail "ffq-prev exists, this is already managed by git-debrebase!"
+    fail __ "ffq-prev exists, this is already managed by git-debrebase!"
        if $ffq_prev && git_get_ref $ffq_prev;
 
     my $gdrlast_obj = $gdrlast && git_get_ref $gdrlast;
-    snag 'already-converted',
+    snag 'already-converted', __
        "ahead of debrebase-last, this is already managed by git-debrebase!"
        if $gdrlast_obj && is_fast_fwd $gdrlast_obj, $head;
     return ($head, { LastRef => $gdrlast, LastObj => $gdrlast_obj });
@@ -2534,18 +2470,19 @@ sub complete_convert_from ($$$$) {
     update_head_checkout $old_head, $new_head, $mrest;
 }
 
+sub cmd_convert_from_unapplied () { cmd_convert_from_gbp(); }
 sub cmd_convert_from_gbp () {
-    badusage "want only 1 optional argument, the upstream git commitish"
+    badusage __ "want only 1 optional argument, the upstream git commitish"
        unless @ARGV<=1;
 
     my $clogp = parsechangelog();
     my $version = $clogp->{'Version'}
-       // die "missing Version from changelog\n";
+       // fail __ "missing Version from changelog\n";
 
     my ($upstream_spec) = @ARGV;
 
     my $upstream_version = upstreamversion $version;
-    my $upstream =
+    my ($upstream, $upstream_used) =
        resolve_upstream_version($upstream_spec, $upstream_version);
 
     my ($old_head, $gdrlastinfo) = begin_convert_from();
@@ -2555,30 +2492,32 @@ sub cmd_convert_from_gbp () {
        runcmd @git, qw(--no-pager diff --stat),
            $upstream, $old_head,
            qw( -- :!/debian :/);
-       fail <<END;
-upstream ($upstream_spec) and HEAD are not
+       fail f_ <<END, $upstream_used, $upstream;
+upstream (%s) and HEAD are not
 identical in upstream files.  See diffstat above, or run
-  git diff $upstream_spec HEAD -- :!/debian :/
+  git diff %s HEAD -- :!/debian :/
 END
     }
 
     if (!is_fast_fwd $upstream, $old_head) {
        snag 'upstream-not-ancestor',
-           "upstream ($upstream) is not an ancestor of HEAD";
+           f_ "upstream (%s) is not an ancestor of HEAD", $upstream;
     } else {
        my $wrong = cmdoutput
            (@git, qw(rev-list --ancestry-path), "$upstream..HEAD",
             qw(-- :/ :!/debian));
        if (length $wrong) {
-           snag 'unexpected-upstream-changes',
-               "history between upstream ($upstream) and HEAD contains direct changes to upstream files - are you sure this is a gbp (patches-unapplied) branch?";
-           print STDERR "list expected changes with:  git log --stat --ancestry-path $upstream_spec..HEAD -- :/ ':!/debian'\n";
+           snag 'unexpected-upstream-changes', f_
+               "history between upstream (%s) and HEAD contains direct changes to upstream files - are you sure this is a gbp (patches-unapplied) branch?",
+               $upstream_used;
+           print STDERR f_ "list expected changes with:  %s\n", 
+ "git log --stat --ancestry-path $upstream..HEAD -- :/ ':!/debian'";
        }
     }
 
     if ((git_cat_file "$upstream:debian")[0] ne 'missing') {
        snag 'upstream-has-debian',
-           "upstream ($upstream) contains debian/ directory";
+           f_ "upstream (%s) contains debian/ directory", $upstream;
     }
 
     check_series_has_all_patches $old_head;
@@ -2596,29 +2535,30 @@ END
            $suite = $stz->{Distribution};
            last;
        };
-       die "neither of the first two changelog entries are released\n"
+       die __ "neither of the first two changelog entries are released\n"
            unless defined $lvsn;
        print "last finished-looking changelog entry: ($lvsn) $suite\n";
        my $mtag_pat = debiantag_maintview $lvsn, '*';
        my $mtag = cmdoutput @git, qw(describe --always --abbrev=0 --match),
            $mtag_pat;
-       die "could not find suitable maintainer view tag $mtag_pat\n"
+       die f_ "could not find suitable maintainer view tag %s\n", $mtag_pat
            unless $mtag =~ m{/};
        is_fast_fwd $mtag, 'HEAD' or
-           die "HEAD is not FF from maintainer tag $mtag!";
+           die f_ "HEAD is not FF from maintainer tag %s!", $mtag;
        my $dtag = "archive/$mtag";
        git_get_ref "refs/tags/$dtag" or
-           die "dgit view tag $dtag not found\n";
+           die f_ "dgit view tag %s not found\n", $dtag;
        is_fast_fwd $mtag, $dtag or
-           die "dgit view tag $dtag is not FF from maintainer tag $mtag\n";
-       print "will stitch in dgit view, $dtag\n";
+           die f_ "dgit view tag %s is not FF from maintainer tag %s\n",
+                  $dtag, $mtag;
+       print f_ "will stitch in dgit view, %s\n", $dtag;
        git_rev_parse $dtag;
     };
     if (!$previous_dgit_view) {
        $@ =~ s/^\n+//;
        chomp $@;
-       print STDERR <<END;
-Cannot confirm dgit view: $@
+       print STDERR f_ <<END, "$@";
+Cannot confirm dgit view: %s
 Failed to stitch in dgit view (see messages above).
 dgit --overwrite will be needed on the first dgit push after conversion.
 END
@@ -2663,8 +2603,8 @@ END
     };
 
     complete_convert_from $old_head, $work, $gdrlastinfo, 'convert-from-gbp';
-    print <<END or confess $!;
-$us: converted from patched-unapplied (gbp) branch format, OK
+    print f_ <<END, $us or confess "$!";
+%s: converted from patched-unapplied (gbp) branch format, OK
 END
 }
 
@@ -2692,10 +2632,10 @@ sub cmd_convert_to_gbp () {
     }
     snags_maybe_bail();
     update_head_checkout $head, $out, "convert to gbp (v0)";
-    print <<END or confess $!;
-$us: converted to git-buildpackage branch format
-$us: WARNING: do not now run "git-debrebase" any more
-$us: WARNING: doing so would drop all upstream patches!
+    print f_ <<END, $us,$us,$us or confess "$!";
+%s: converted to git-buildpackage branch format
+%s: WARNING: do not now run "git-debrebase" any more
+%s: WARNING: doing so would drop all upstream patches!
 END
 }
 
@@ -2714,7 +2654,7 @@ sub cmd_convert_from_dgit_view () {
               'origs!', \$do_origs,
               'tags!', \$do_tags,
               'always-convert-anyway!', \$always);
-    fail "takes 1 optional argument, the upstream commitish" if @ARGV>1;
+    fail __ "takes 1 optional argument, the upstream commitish" if @ARGV>1;
 
     my @upstreams;
 
@@ -2722,7 +2662,7 @@ sub cmd_convert_from_dgit_view () {
        my $spec = shift @ARGV;
        my $commit = git_rev_parse "$spec^{commit}";
        push @upstreams, { Commit => $commit,
-                          Source => "$ARGV[0], from command line",
+                          Source => (f_ "%s, from command line", $spec),
                           Only => 1,
                         };
     }
@@ -2735,12 +2675,13 @@ sub cmd_convert_from_dgit_view () {
        keycommits $head, sub{}, sub{}, $trouble, $trouble;
        printdebug "troubles=$troubles\n";
        if (!$troubles) {
-           print STDERR <<END;
-$us: Branch already seems to be in git-debrebase format!
-$us: --always-convert-anyway would do the conversion operation anyway
-$us: but is probably a bad idea.  Probably, you wanted to do nothing.
+           print STDERR f_ <<END, $us,$us,$us;
+%s: Branch already seems to be in git-debrebase format!
+%s: --always-convert-anyway would do the conversion operation anyway
+%s: but is probably a bad idea.  Probably, you wanted to do nothing.
 END
-           fail "Branch already in git-debrebase format." unless $opt_noop_ok;
+           fail __ "Branch already in git-debrebase format."
+               unless $opt_noop_ok;
            finish 0;
        }
     }
@@ -2750,19 +2691,20 @@ END
     snags_maybe_bail_early();
 
     my $version = upstreamversion $clogp->{Version};
-    print STDERR "Considering possible commits corresponding to upstream:\n";
+    print STDERR __
+       "Considering possible commits corresponding to upstream:\n";
 
     if (!@upstreams) {
        if ($do_tags) {
            my @tried;
            my $ups_tag = upstream_commitish_search $version, \@tried;
            if ($ups_tag) {
-               my $this = "git tag $tried[-1]";
+               my $this = f_ "git tag %s", $tried[-1];
                push @upstreams, { Commit => $ups_tag,
                                   Source => $this,
                                 };
            } else {
-               printf STDERR
+               print STDERR f_
                    " git tag: no suitable tag found (tried %s)\n",
                    "@tried";
            }
@@ -2772,11 +2714,12 @@ END
            # we do a quick check to see if there are plausible origs
            my $something=0;
            if (!opendir BPD, $bpd) {
-               die "opendir build-products-dir $bpd: $!" unless $!==ENOENT;
+               die f_ "opendir build-products-dir %s: %s", $bpd, $!
+                   unless $!==ENOENT;
            } else {
                while ($!=0, my $f = readdir BPD) {
                    next unless is_orig_file_of_p_v $f, $p, $version;
-                   printf STDERR
+                   print STDERR f_
                        " orig: found what looks like a .orig, %s\n",
                        "$bpd/$f";
                    $something=1;
@@ -2807,7 +2750,7 @@ END
                                     };
                }
            } else {
-               printf STDERR
+               print STDERR f_
                    " orig: no suitable origs found (looked for %s in %s)\n",
                    "${p}_".(stripeoch $version)."...", $bpd;
            }
@@ -2816,7 +2759,8 @@ END
 
     my $some_patches = stat_exists 'debian/patches/series';
 
-    print STDERR "Evaluating possible commits corresponding to upstream:\n";
+    print STDERR __
+       "Evaluating possible commits corresponding to upstream:\n";
 
     my $result;
     foreach my $u (@upstreams) {
@@ -2852,7 +2796,7 @@ END
                }
                my $r = system @gbp_cmd;
                if ($r) {
-                   printf STDERR
+                   print STDERR f_
                        " %s: couldn't apply patches: gbp pq %s",
                        $u->{Source}, waitstatusmsg();
                    return;
@@ -2861,8 +2805,9 @@ END
            my $work = git_rev_parse qw(HEAD);
            my $diffout = cmdoutput @git, qw(diff-tree --stat HEAD), $work;
            if (length $diffout) {
-               print STDERR
-                   " $u->{Source}: applying patches gives different tree\n";
+               print STDERR f_
+                   " %s: applying patches gives different tree\n",
+                   $u->{Source};
                print STDERR $diffout if $diagnose;
                return;
            }
@@ -2874,24 +2819,26 @@ END
     }
 
     if (!$result) {
-       fail <<END;
+       fail __ <<END;
 Could not find or construct a suitable upstream commit.
 Rerun adding --diagnose after convert-from-dgit-view, or pass a
 upstream commmit explicitly or provide suitable origs.
 END
     }
 
-    printf STDERR "Yes, will base new branch on %s\n", $result->{Source};
+    print STDERR f_ "Yes, will base new branch on %s\n", $result->{Source};
 
     complete_convert_from $head, $result->{Result}, $gdrlastinfo,
        'convert-from-dgit-view';
 }
 
 sub cmd_forget_was_ever_debrebase () {
-    badusage "forget-was-ever-debrebase takes no further arguments" if @ARGV;
+    badusage __ "forget-was-ever-debrebase takes no further arguments"
+       if @ARGV;
     my ($ffqstatus, $ffq_msg, $current, $ffq_prev, $gdrlast) =
        ffq_prev_branchinfo();
-    fail "Not suitable for recording git-debrebaseness anyway: $ffq_msg"
+    fail f_ "Not suitable for recording git-debrebaseness anyway: %s",
+           $ffq_msg
        if defined $ffq_msg;
     push @deferred_updates, "delete $ffq_prev";
     push @deferred_updates, "delete $gdrlast";
@@ -2992,7 +2939,7 @@ setlocale(LC_MESSAGES, "");
 textdomain("git-debrebase");
 
 getoptions_main
-          ("bad options\n",
+          (__ "bad options\n",
           "D+" => \$debuglevel,
           'noop-ok', => \$opt_noop_ok,
           'f=s' => \@snag_force_opts,
@@ -3012,14 +2959,13 @@ getoptions_main
               push @$opt_defaultcmd_interactive, @ARGV;
               @ARGV=();
           },
-          'help' => sub { print __ $usage_message or confess $!; finish 0; },
+          'help' => sub { print __ $usage_message or confess "$!"; finish 0; },
           );
 
 initdebug('git-debrebase ');
 enabledebug if $debuglevel;
 
-my $toplevel = cmdoutput @git, qw(rev-parse --show-toplevel);
-chdir $toplevel or fail "chdir toplevel $toplevel: $!\n";
+changedir_git_toplevel();
 
 $rd = fresh_playground "$playprefix/misc";