chiark / gitweb /
i18n: git-debrebase: Change a lot of die to confess
[dgit.git] / git-debrebase
index 4bfe0952518214203c5e3ac397b27c53ceb93354..39f79cd5f92cd7aa637e1e2313931e9306922fe0 100755 (executable)
@@ -21,6 +21,7 @@
 END { $? = $Debian::Dgit::ExitStatus::desired // -1; };
 use Debian::Dgit::GDR;
 use Debian::Dgit::ExitStatus;
+use Debian::Dgit::I18n;
 
 use strict;
 
@@ -30,6 +31,7 @@ setup_sigwarn();
 use Memoize;
 use Carp;
 use POSIX;
+use Locale::gettext;
 use Data::Dumper;
 use Getopt::Long qw(:config posix_default gnu_compat bundling);
 use Dpkg::Version;
@@ -103,7 +105,7 @@ sub dd ($) {
 sub get_commit ($) {
     my ($objid) = @_;
     my $data = (git_cat_file $objid, 'commit');
-    $data =~ m/(?<=\n)\n/ or die "$objid ($data) ?";
+    $data =~ m/(?<=\n)\n/ or confess "$objid ($data) ?";
     return ($`,$');
 }
 
@@ -142,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 die $!;
+    open U, "|-", @upd_cmd or confess $!;
     foreach (@$updates) {
        printdebug ">= ", $_, "\n";
-       print U $_, "\n" or die $!;
+       print U $_, "\n" or confess $!;
     }
     printdebug ">\$\n";
     close U or failedcmd @upd_cmd;
@@ -196,10 +198,12 @@ sub get_tree ($;$$) {
        return () if $type eq 'missing';
     }
 
+    $recurse = !!$recurse;
+
     confess "get_tree needs object not $x ?" unless $x =~ m{^[0-9a-f]+\:};
 
     our (@get_tree_memo, %get_tree_memo);
-    my $memo = $get_tree_memo{$x};
+    my $memo = $get_tree_memo{$recurse,$x};
     return @$memo if $memo;
 
     local $Debian::Dgit::debugcmd_when_debuglevel = 3;
@@ -216,7 +220,7 @@ sub get_tree ($;$$) {
        push @l, [ $n, $i ];
        confess "$x need $last < $n ?" unless $last lt $n;
     }
-    $get_tree_memo{$x} = \@l;
+    $get_tree_memo{$recurse,$x} = \@l;
     push @get_tree_memo, $x;
     if (@get_tree_memo > 10) {
        delete $get_tree_memo{ shift @get_tree_memo };
@@ -225,15 +229,18 @@ sub get_tree ($;$$) {
 }
 
 sub trees_diff_walk ($$$;$) {
-    # trees_diff_walk [$all,] $x, $y, sub {... }
-    # calls sub->($name, $ix, $iy) for each difference (with $all, each name)
+    # trees_diff_walk [{..opts...},] $x, $y, sub {... }
+    # calls sub->($name, $ix, $iy) for each difference
     # $x and $y are as for get_tree
     # where $name, $ix, $iy are $name and $info from get_tree
-    my $all = shift @_ if @_>=4;
+    # opts are   all       call even for names same in both
+    #            recurse   call even for names same in both
+    my $opts = shift @_ if @_>=4;
     my ($x,$y,$call) = @_;
+    my $all = $opts->{all};
     return if !$all and $x eq $y;
-    my @x = get_tree $x;
-    my @y = get_tree $y;
+    my @x = get_tree $x, 0, $opts->{recurse};
+    my @y = get_tree $y, 0, $opts->{recurse};
     printdebug "trees_diff_walk(..$x,$y..) ".Dumper(\@x,\@y)
        if $debuglevel >= 3;
     while (@x || @y) {
@@ -305,13 +312,16 @@ sub get_differs ($$) {
 
            my $xp = $ix && "$xd/patches";
            my $yp = $iy && "$yd/patches";
-           trees_diff_walk $xp, $yp, sub {
+           trees_diff_walk { recurse=>1 }, $xp, $yp, sub {
                my ($n,$ix,$iy) = @_;
 
                # analyse difference in debian/patches
 
                my $ok;
-               if ($n !~ m/\.series$/s && !$ix && $plain->($iy)) {
+               if ($n =~ m{/$}s) {
+                   # we are recursing; directories may appear and disappear
+                   $ok = 1;
+               } elsif ($n !~ m/\.series$/s && !$ix && $plain->($iy)) {
                    $ok = 1;
                } elsif ($n eq 'series' && $plain->($ix) && $plain->($iy)) {
                    my $x_s = (git_cat_file "$xp/series", 'blob');
@@ -486,6 +496,7 @@ sub gbp_pq_export ($$$) {
     # must be run in a workarea.  $bname and patch-queue/$bname
     # ought not to exist.  Leaves you on patch-queue/$bname with
     # the patches staged but not committed.
+    # returns 1 if there were any patches
     printdebug "gbp_pq_export $bname $base $tip\n";
     runcmd @git, qw(checkout -q -b), $bname, $base;
     runcmd @git, qw(checkout -q -b), "patch-queue/$bname", $tip;
@@ -495,7 +506,9 @@ sub gbp_pq_export ($$$) {
        { local ($!,$?); copy('../gbp-pq-err', \*STDERR); }
        failedcmd @gbp_cmd;
     }
-    runcmd @git, qw(add -f debian/patches) if stat_exists 'debian/patches';
+    return 0 unless stat_exists 'debian/patches';
+    runcmd @git, qw(add -f debian/patches);
+    return 1;
 }
 
 
@@ -592,22 +605,21 @@ sub merge_series ($$$;@) {
        playtree_setup();
        foreach my $q ($base_q, reverse @input_qs) {
            my $s = $q->{MR}{S};
-           gbp_pq_export "p-$s", $q->{SeriesBase}, $q->{SeriesTip};
+           my $any = gbp_pq_export "p-$s", $q->{SeriesBase}, $q->{SeriesTip};
            my @earlier;
-           if (open S, $seriesfile) {
+           if ($any) {
+               open S, $seriesfile or confess "$seriesfile $!";
                while (my $patch = <S>) {
-                   chomp $patch or die $!;
+                   chomp $patch or confess $!;
                    $prereq{$patch} //= {};
                    foreach my $earlier (@earlier) {
-                       $prereq{$patch}{$earlier}{$s}++ and die;
+                       $prereq{$patch}{$earlier}{$s}++ and confess;
                    }
                    push @earlier, $patch;
-                   stat "debian/patches/$patch" or die "$patch ?";
+                   stat "debian/patches/$patch" or confess "$patch ?";
                }
-               S->error and die "$seriesfile $!";
+               S->error and confess "$seriesfile $!";
                close S;
-           } else {
-               die "$seriesfile $!" unless $!==ENOENT;
            }
            read_tree_upstream $newbase, 1;
            my $pec = make_commit [ grep { defined } $base_q->{MR}{PEC} ], [
@@ -685,7 +697,7 @@ sub merge_series ($$$;@) {
        my $authordate = sub {
            my ($f) = @_;
            $authordate{$f} //= do {
-               open PF, "<", "debian/patches/$f" or die "$f $!";
+               open PF, "<", "debian/patches/$f" or confess "$f $!";
                while (<PF>) {
                    return $nodate if m/^$/;
                    last if s{^Date: }{};
@@ -695,7 +707,7 @@ sub merge_series ($$$;@) {
            };
        };
 
-       open NS, '>', $seriesfile or die $!;
+       open NS, '>', $seriesfile or confess $!;
 
        while (keys %prereq) {
            my $best;
@@ -710,7 +722,7 @@ sub merge_series ($$$;@) {
                $best = $try;
            }
            printdebug "merge_series series next $best\n";
-           print NS "$best\n" or die $!;
+           print NS "$best\n" or confess $!;
            delete $prereq{$best};
            foreach my $gp (values %prereq) {
                delete $gp->{$best};
@@ -752,9 +764,9 @@ 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 die $!;
-           print C $commit or die $!;
-           close C or die $!;
+           open C, ">", "../mcommit" or confess $!;
+           print C $commit or confess $!;
+           close C or confess $!;
            $build = cmdoutput @git, qw(hash-object -w -t commit ../mcommit);
        }
        $result = $build;
@@ -825,11 +837,11 @@ sub parsecommit ($;$) {
     #    $p_ref, if provided, must be [] and is used as a base for Parents
 
     $p_ref //= [];
-    die if @$p_ref;
+    confess if @$p_ref;
 
     my ($h,$m) = get_commit $objid;
 
-    my ($t) = $h =~ m/^tree (\w+)$/m or die $objid;
+    my ($t) = $h =~ m/^tree (\w+)$/m or confess $objid;
     my (@ph) = $h =~ m/^parent (\w+)$/mg;
 
     my $r = {
@@ -999,7 +1011,8 @@ sub classify ($) {
     if (@p == 2 && @identical == 2) {
        my $get_t = sub {
            my ($ph,$pm) = get_commit $_[0]{CommitId};
-           $ph =~ m/^committer .* (\d+) [-+]\d+$/m or die "$_->{CommitId} ?";
+           $ph =~ m/^committer .* (\d+) [-+]\d+$/m
+               or confess "$_->{CommitId} ?";
            $1;
        };
        my @bytime = @p;
@@ -1070,9 +1083,8 @@ sub keycommits ($;$$$$$) {
     my ($head, $furniture, $unclean, $trouble, $fatal, $claimed_bw) = @_;
     # => ($anchor, $breakwater)
 
-    # $unclean->("unclean-$tagsfx", $msg, $cl)
     # $furniture->("unclean-$tagsfx", $msg, $cl)
-    # $dgitimport->("unclean-$tagsfx", $msg, $cl))
+    # $unclean->("unclean-$tagsfx", $msg, $cl)
     #   is callled for each situation or commit that
     #   wouldn't be found in a laundered branch
     # $furniture is for furniture commits such as might be found on an
@@ -1193,8 +1205,8 @@ sub walk ($;$$$) {
        my ($prose, $info) = @_;
        my $ms = $cl->{Msg};
        chomp $ms;
-       $info //= '';
-       $ms .= "\n\n[git-debrebase$info: $prose]\n";
+       confess unless defined $info;
+       $ms .= "\n\n[git-debrebase $info: $prose]\n";
        return (Msg => $ms);
     };
     my $rewrite_from_here = sub {
@@ -1275,7 +1287,7 @@ sub walk ($;$$$) {
        } elsif ($ty eq 'Mixed') {
            my $queue = sub {
                my ($q, $wh) = @_;
-               my $cls = { %$cl, $xmsg->("split mixed commit: $wh part") };
+               my $cls = { %$cl, $xmsg->("mixed commit: $wh part",'split') };
                push @$q, $cls;
            };
            $queue->(\@brw_cl, "debian");
@@ -1328,12 +1340,12 @@ sub walk ($;$$$) {
                push @brw_cl, {
                    %$cl,
                    SpecialMethod => 'DgitImportDebianUpdate',
-                    $xmsg->("convert dgit import: debian changes")
+                    $xmsg->("debian changes", 'convert dgit import')
                }, {
                    %$cl,
                    SpecialMethod => 'DgitImportUpstreamUpdate',
                     $xmsg->("convert dgit import: upstream update",
-                           " anchor")
+                           "anchor")
                };
                $prline->(" Import");
                $rewrite_from_here->(\@brw_cl);
@@ -1349,7 +1361,7 @@ sub walk ($;$$$) {
                $cl->{Why} = "bare dgit dsc import";
                return $bomb->();
            }
-           die "$ty ?";
+           confess "$ty ?";
        } elsif ($ty eq 'MergedBreakwaters') {
            $last_anchor = mergedbreakwaters_anchor $cl;
            $build_start->(' MergedBreakwaters', $cur);
@@ -1526,7 +1538,7 @@ sub walk ($;$$$) {
                 %$cl,
                 SpecialMethod => 'MergeCreateMergedBreakwaters',
                 $xmsg->('constructed from vanilla merge',
-                       ' merged-breakwater'),
+                       'merged-breakwater'),
             };
            push @upp_cl, {
                 %$cl,
@@ -1560,7 +1572,7 @@ sub walk ($;$$$) {
     confess "internal error" unless $build eq (pop @processed)->{CommitId};
 
     in_workarea sub {
-       mkdir $rd or $!==EEXIST or die $!;
+       mkdir $rd or $!==EEXIST or confess $!;
        my $current_method;
        my $want_debian = $build;
        my $want_upstream = $build;
@@ -1670,9 +1682,9 @@ sub walk ($;$$$) {
                        or confess "$ch ?";
                }
                my $cf = "$rd/m$rewriting";
-               open CD, ">", $cf or die $!;
-               print CD $ch, "\n", $cl->{Msg} or die $!;
-               close CD or die $!;
+               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;
@@ -1702,7 +1714,7 @@ sub walk ($;$$$) {
     };
 
     my $final_check = get_differs $build, $input;
-    die sprintf "internal error %#x %s %s", $final_check, $input, $build
+    confess sprintf "internal error %#x %s %s", $final_check, $input, $build
        if $final_check & ~D_PAT_ADD;
 
     my @r = ($build, $breakwater, $last_anchor);
@@ -1785,7 +1797,7 @@ sub cmd_analyse () {
        $old = git_rev_parse 'HEAD';
     }
     my ($dummy,$breakwater) = walk $old, 1,*STDOUT;
-    STDOUT->error and die $!;
+    STDOUT->error and confess $!;
 }
 
 sub ffq_check ($;$$) {
@@ -1802,7 +1814,7 @@ sub ffq_check ($;$$) {
     # normally $currentval should be HEAD
     my ($currentval, $ff, $notff) =@_;
 
-    $ff //= sub { print $_[0] or die $!; };
+    $ff //= sub { print $_[0] or confess $!; };
     $notff //= \&snag;
 
     my ($status, $message, $current, $ffq_prev, $gdrlast)
@@ -2041,7 +2053,7 @@ sub cmd_new_upstream () {
             unless @ARGV && $ARGV[0] !~ m{^-};
 
        my $c = git_rev_parse shift @ARGV;
-       die unless $n =~ m/^$extra_orig_namepart_re$/;
+       confess unless $n =~ m/^$extra_orig_namepart_re$/;
        $newpiece->($n, New => $c);
     }
 
@@ -2055,7 +2067,7 @@ sub cmd_new_upstream () {
     my $old_upstream;
     if (!$old_anchor_cl->{OrigParents}) {
        snag 'anchor-treated',
          'old anchor is recognised due to --anchor, cannot check upstream';
__ 'old anchor is recognised due to --anchor, cannot check upstream';
     } else {
        $old_upstream = parsecommit
            $old_anchor_cl->{OrigParents}[0]{CommitId};
@@ -2161,36 +2173,45 @@ sub cmd_new_upstream () {
  "[git-debrebase anchor: new upstream $new_upstream_version, merge]",
             ];
 
-       my $clogsignoff = cmdoutput qw(git show),
-           '--pretty=format:%an <%ae>  %aD',
-           $new_bw;
-
        # Now we have to add a changelog stanza so the Debian version
-       # is right.
-       die if unlink "debian";
-       die $! unless $!==ENOENT or $!==ENOTEMPTY;
-       unlink "debian/changelog" or $!==ENOENT or die $!;
-        mkdir "debian" or die $!;
-       open CN, ">", "debian/changelog" or die $!;
-       my $oldclog = git_cat_file ":debian/changelog";
-       $oldclog =~ m/^($package_re) \(\S+\) / or
-           fail "cannot parse old changelog to get package name";
-       my $p = $1;
-       print CN <<END, $oldclog or die $!;
-$p ($new_version) UNRELEASED; urgency=medium
-
-  * Update to new upstream version $new_upstream_version.
-
- -- $clogsignoff
-
-END
-       close CN or die $!;
+       # is right.  We use debchange to do this.  Invoking debchange
+       # here is a bit fiddly because it has a lot of optional
+       # exciting behaviours, some of which will break stuff, and
+       # some of which won't work in a playtree.
+
+       # Make debchange use git's idea of the user's identity.
+       # That way, if the user never uses debchange et al, configuring
+       # git is enough.
+       my $usetup = sub {
+           my ($e, $k) = @_;
+           my $v = cfg $k, 1;
+           defined $v or return;
+           $ENV{$e} = $v;
+       };
+       $usetup->('DEBEMAIL',    'user.email');
+       $usetup->('DEBFULLNAME', 'user.name');
+
+sleep 2;
+
+       my @dch = (qw(debchange
+                     --allow-lower-version .*
+                     --no-auto-nmu
+                     --preserve
+                     --vendor=Unknown-Vendor
+                     --changelog debian/changelog
+                     --check-dirname-level 0
+                     --release-heuristic=changelog
+                     -v), $new_version,
+                  "Update to new upstream version $new_upstream_version.");
+
+       runcmd @git, qw(checkout -q debian/changelog);
+       runcmd @dch;
        runcmd @git, qw(update-index --add --replace), 'debian/changelog';
 
        # Now we have the final new breakwater branch in the index
         $new_bw = make_commit [ $new_bw ],
             [ "Update changelog for new upstream $new_upstream_version",
-              "[git-debrebase: new upstream $new_upstream_version, changelog]",
+              "[git-debrebase changelog: new upstream $new_upstream_version]",
             ];
     };
 
@@ -2212,7 +2233,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 die $!;
+       print "Previous head already recorded\n" or confess $!;
     } elsif ($status eq 'deferred') {
        run_deferred_updates 'record-ffq-prev';
     } else {
@@ -2223,13 +2244,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 "$bw\n" or die $!;
+    print "$bw\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 die $!;
+    print "$bw\n" or confess $!;
 }
 
 sub cmd_status () {
@@ -2262,7 +2283,7 @@ sub cmd_status () {
 
     my $prcommitinfo = sub {
        my ($cid) = @_;
-       flush STDOUT or die $!;
+       flush STDOUT or confess $!;
        runcmd @git, qw(--no-pager log -n1),
            '--pretty=format:    %h %s%n',
            $cid;
@@ -2387,23 +2408,30 @@ sub make_patches_staged ($) {
     # laundered.
     my ($secret_head, $secret_bw, $last_anchor) = walk $head;
     fresh_workarea();
+    my $any;
     in_workarea sub {
-       gbp_pq_export 'bw', $secret_bw, $secret_head;
+       $any = gbp_pq_export 'bw', $secret_bw, $secret_head;
     };
+    return $any;
 }
 
 sub make_patches ($) {
     my ($head) = @_;
     keycommits $head, 0, \&snag;
-    make_patches_staged $head;
+    my $any = make_patches_staged $head;
     my $out;
     in_workarea sub {
-       my $ptree = cmdoutput @git, qw(write-tree --prefix=debian/patches/);
+       my $ptree = !$any ? undef :
+           cmdoutput @git, qw(write-tree --prefix=debian/patches/);
        runcmd @git, qw(read-tree), $head;
-       read_tree_subdir 'debian/patches', $ptree;
+       if ($ptree) {
+           read_tree_subdir 'debian/patches', $ptree;
+       } else {
+           rm_subdir_cached 'debian/patches';
+       }
        $out = make_commit [$head], [
             'Commit patch queue (exported by git-debrebase)',
-            '[git-debrebase: export and commit patches]',
+            '[git-debrebase make-patches: export and commit patches]',
         ];
     };
     return $out;
@@ -2441,7 +2469,14 @@ sub check_series_has_all_patches ($) {
        [qw(blob missing)];
     $series //= '';
     my %series;
+    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"
+               unless $comments_snagged++;
+           next;
+       }
        fail "patch $f repeated in $seriesfn !" if $series{$f}++;
     }
     foreach my $patchfile (get_tree "$head:debian/patches", 1,1) {
@@ -2604,7 +2639,7 @@ END
     };
 
     complete_convert_from $old_head, $work, $gdrlastinfo, 'convert-from-gbp';
-    print <<END or die $!;
+    print <<END or confess $!;
 git-debrebase: converted from patched-unapplied (gbp) branch format, OK
 END
 }
@@ -2613,22 +2648,27 @@ sub cmd_convert_to_gbp () {
     badusage "no arguments allowed" if @ARGV;
     my $head = get_head();
     my (undef, undef, undef, $ffq, $gdrlast) = ffq_prev_branchinfo();
-    keycommits $head, 0;
-    my $out;
-    make_patches_staged $head;
-    in_workarea sub {
-       $out = make_commit ['HEAD'], [
-            'Commit patch queue (converted from git-debrebase format)',
-            '[git-debrebase convert-to-gbp: commit patches]',
-        ];
-    };
+    my ($anchor, $breakwater) = keycommits $head, 0;
+    my $out = $breakwater;
+    my $any = make_patches_staged $head;
+    if ($any) {
+       in_workarea sub {
+           $out = make_commit [$out], [
+               'Commit patch queue (converted from git-debrebase format)',
+               '[git-debrebase convert-to-gbp: commit patches]',
+           ];
+       };
+    } else {
+       # in this case, it can be fast forward
+       $out = $head;
+    }
     if (defined $ffq) {
        push @deferred_updates, "delete $ffq";
        push @deferred_updates, "delete $gdrlast";
     }
     snags_maybe_bail();
     update_head_checkout $head, $out, "convert to gbp (v0)";
-    print <<END or die $!;
+    print <<END or confess $!;
 git-debrebase: converted to git-buildpackage branch format
 git-debrebase: WARNING: do not now run "git-debrebase" any more
 git-debrebase: WARNING: doing so would drop all upstream patches!
@@ -2718,7 +2758,7 @@ END
                    $something=1;
                    last;
                }
-               die "read $bpd: $!" if $!;
+               confess "read $bpd: $!" if $!;
                closedir BPD;
            }
            if ($something) {
@@ -2735,7 +2775,7 @@ Import effective orig tree for upstream version $version
 END
 This includes the contents of the .orig(s), minus any debian/ directory.
 
-[git-debrebase import-from-dgit-view upstream-import-convert: $version]
+[git-debrebase convert-from-dgit-view upstream-import-convert: $version]
 END
                                                    ];
                    push @upstreams, { Commit => $ups_synth,
@@ -2769,7 +2809,7 @@ END
  'git-debrebase convert-from-dgit-view: drop upstream changes from breakwater',
  "Drop upstream changes, and delete debian/patches, as part of converting\n".
  "to git-debrebase format.  Upstream changes will appear as commits.",
- '[git-debrebase convert-from-dgit-view: drop patches from tree]'
+ '[git-debrebase convert-from-dgit-view drop-patches]'
                                           ];
            }
            $work = make_commit [ $work, $u->{Commit} ], [
@@ -2823,6 +2863,18 @@ END
        'convert-from-dgit-view';
 }
 
+sub cmd_forget_was_ever_debrebase () {
+    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"
+       if defined $ffq_msg;
+    push @deferred_updates, "delete $ffq_prev";
+    push @deferred_updates, "delete $gdrlast";
+    snags_maybe_bail();
+    run_deferred_updates "forget-was-ever-debrebase";
+}
+
 sub cmd_record_resolved_merge () {
     badusage "record-resolved-merge takes no further arguments" if @ARGV;
     # MERGE-TODO needs documentation
@@ -2912,6 +2964,9 @@ sub cmd_downstream_rebase_launder_v0 () {
     }
 }
 
+setlocale(LC_MESSAGES, "");
+textdomain("git-debrebase");
+
 getoptions_main
           ("bad options\n",
           "D+" => \$debuglevel,
@@ -2923,9 +2978,9 @@ getoptions_main
           'experimental-merge-resolution!', \$opt_merges,
           '-i:s' => sub {
               my ($opt,$val) = @_;
-              badusage "git-debrebase: no cuddling to -i for git-rebase"
+              badusage __ "git-debrebase: no cuddling to -i for git-rebase"
                   if length $val;
-              die if $opt_defaultcmd_interactive; # should not happen
+              confess if $opt_defaultcmd_interactive; # should not happen
               $opt_defaultcmd_interactive = [ qw(-i) ];
               # This access to @ARGV is excessive familiarity with
               # Getopt::Long, but there isn't another sensible
@@ -2954,7 +3009,7 @@ if (!@ARGV || $opt_defaultcmd_interactive || $ARGV[0] =~ m{^-}) {
     $cmdfn =~ y/-/_/;
     $cmdfn = ${*::}{"cmd_$cmdfn"};
 
-    $cmdfn or badusage "unknown git-debrebase sub-operation $cmd";
+    $cmdfn or badusage f_ "unknown git-debrebase sub-operation %s", $cmd;
     $cmdfn->();
 }