chiark / gitweb /
exit status: Fix dgit-badcommit-fixup to use Debian::Dgit::ExitStatus
[dgit.git] / git-debrebase
index fcca5b1..bf9af61 100755 (executable)
@@ -18,6 +18,9 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+END { $? = $Debian::Dgit::ExitStatus::desired // -1; };
+use Debian::Dgit::ExitStatus;
+
 use strict;
 
 use Debian::Dgit qw(:DEFAULT :playground);
@@ -39,7 +42,7 @@ our $us = qw(git-debrebase);
 sub badusage ($) {
     my ($m) = @_;
     print STDERR "bad usage: $m\n";
-    exit 12;
+    finish 12;
 }
 
 sub cfg ($;$) {
@@ -99,12 +102,18 @@ sub fresh_workarea () {
     in_workarea sub { playtree_setup };
 }
 
+our $snags_forced;
+our $snags_tripped;
+our $snags_checked;
 our @deferred_updates;
 our @deferred_update_messages;
 
 sub run_deferred_updates ($) {
     my ($mrest) = @_;
 
+    confess 'dangerous internal error' if
+       !$snags_checked || $snags_tripped || $snags_forced;
+
     my @upd_cmd = (@git, qw(update-ref --stdin -m), "debrebase: $mrest");
     debugcmd '>|', @upd_cmd;
     open U, "|-", @upd_cmd or die $!;
@@ -227,8 +236,6 @@ sub make_commit ($$) {
 }
 
 our @snag_force_opts;
-our $snags_forced;
-our $snags_tripped;
 sub snag ($$) {
     my ($tag,$msg) = @_;
     if (grep { $_ eq $tag } @snag_force_opts) {
@@ -241,16 +248,19 @@ sub snag ($$) {
 }
 
 sub snags_maybe_bail () {
+    $snags_checked++;
     if ($snags_forced) {
        printf STDERR
            "%s: snags: %d overriden by individual -f options\n",
            $us, $snags_forced;
+       $snags_forced=0;
     }
     if ($snags_tripped) {
        if ($opt_force) {
            printf STDERR
                "%s: snags: %d overriden by global --force\n",
                $us, $snags_tripped;
+           $snags_tripped=0;
        } else {
            fail sprintf
   "%s: snags: %d blockers (you could -f<tag>, or --force)",
@@ -396,6 +406,15 @@ sub classify ($) {
        # BreakwaterStart commits are also anchors in the terminology
        # of git-debrebase(5), but they are untagged (and always
        # manually generated).
+       #
+       # We cannot not tolerate any tagged linear commit (ie,
+       # BreakwaterStart commits tagged `[anchor:') because such a
+       # thing could result from an erroneous linearising raw git
+       # rebase of a merge anchor.  That would represent a corruption
+       # of the branch. and we want to detect and reject the results
+       # of such corruption before it makes it out anywhere.  If we
+       # reject it here then we avoid making the pseudomerge which
+       # would be needed to push it.
 
        my $badanchor = sub { $unknown->("git-debrebase \`anchor' but @_"); };
        @p == 2 or return $badanchor->("has other than two parents");
@@ -551,7 +570,7 @@ sub keycommits ($;$$$) {
            $breakwater = undef;
        } elsif ($ty eq 'Mixed') {
            $x->($unclean, 'mixed',
-                'found mixed upstream/packaging commit ($head)');
+                "found mixed upstream/packaging commit ($head)");
            $clogonly = undef;
            $breakwater = undef;
        } elsif ($ty eq 'Pseudomerge' or
@@ -892,6 +911,7 @@ sub do_launder_head ($) {
     my $old = get_head();
     record_ffq_auto();
     my ($tip,$breakwater) = walk $old;
+    snags_maybe_bail();
     update_head_postlaunder $old, $tip, $reflogmsg;
     return ($tip,$breakwater);
 }
@@ -1074,8 +1094,8 @@ sub stitch ($$$$$) {
     update_head $old_head, $new_head, "stitch: $prose";
 }
 
-sub do_stitch ($) {
-    my ($prose) = @_;
+sub do_stitch ($;$) {
+    my ($prose, $unclean) = @_;
 
     my ($ffq_prev, $gdrlast, $ffq_prev_commitish) = ffq_prev_info();
     if (!$ffq_prev_commitish) {
@@ -1084,7 +1104,8 @@ sub do_stitch ($) {
     }
     my $dangling_head = get_head();
 
-    keycommits $dangling_head, \&snag, \&snag, \&snag;
+    keycommits $dangling_head, $unclean,$unclean,$unclean;
+    snags_maybe_bail();
 
     stitch($dangling_head, $ffq_prev, $gdrlast, $ffq_prev_commitish, $prose);
 }
@@ -1307,13 +1328,79 @@ sub cmd_stitch () {
     my $prose = 'stitch';
     GetOptions('prose=s', \$prose) or die badusage("bad options to stitch");
     badusage "no arguments allowed" if @ARGV;
-    do_stitch($prose);
+    do_stitch $prose, 0;
+}
+sub cmd_prepush () { cmd_stitch(); }
+
+sub cmd_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;
+       return;
+    }
+    my $dangling_head = get_head();
+    
+    badusage "no arguments allowed" if @ARGV;
+    do_launder_head 'launder for git-debrebase quick';
+    do_stitch 'quick';
+}
+
+sub make_patches_staged ($) {
+    my ($head) = @_;
+    # Produces the patches that would result from $head if it were
+    # laundered.
+    my ($secret_head, $secret_bw, $last_anchor) = walk $head;
+    fresh_workarea();
+    in_workarea sub {
+       runcmd @git, qw(checkout -q -b bw), $secret_bw;
+       runcmd @git, qw(checkout -q -b patch-queue/bw), $secret_head;
+       runcmd qw(gbp pq export);
+       runcmd @git, qw(add debian/patches);
+    };
+}
+
+sub make_patches ($) {
+    my ($head) = @_;
+    keycommits $head, 0, \&snag;
+    make_patches_staged $head;
+    my $out;
+    in_workarea sub {
+       my $ptree = cmdoutput @git, qw(write-tree --prefix=debian/patches/);
+       runcmd @git, qw(read-tree), $head;
+       read_tree_subdir 'debian/patches', $ptree;
+       $out = make_commit [$head], [
+            'Commit patch queue (exported by git-debrebase)',
+            '[git-debrebase: export and commit patches]',
+        ];
+    };
+    my $d = get_differs $head, $out;
+    if ($d == 0) {
+       return undef; # nothing to do
+    } elsif ($d == D_PAT_ADD) {
+       return $out; # OK
+    } else {
+       fail "Patch export produced patch amendments".
+           " (abandoned output commit $out).".
+           "  Try laundering first.";
+    }
+}
+
+sub cmd_make_patches () {
     badusage "no arguments allowed" if @ARGV;
-    do_launder_head 'launder for conclude';
-    do_stitch 'conclude';
+    my $old_head = get_head();
+    my $new = make_patches $old_head;
+    snags_maybe_bail();
+    if (!$new) {
+       fail "No (more) patches to export." unless $opt_noop_ok;
+       return;
+    }
+    update_head_checkout $old_head, $new, 'make-patches';
 }
 
 sub cmd_convert_from_gbp () {
@@ -1389,14 +1476,10 @@ sub cmd_convert_to_gbp () {
     badusage "no arguments allowed" if @ARGV;
     my $head = get_head();
     my (undef, undef, undef, $ffq, $gdrlast) = ffq_prev_branchinfo();
-    my ($anchor, $bw) = keycommits $head, 0;
-    fresh_workarea();
+    keycommits $head, 0;
     my $out;
+    make_patches_staged $head;
     in_workarea sub {
-       runcmd @git, qw(checkout -q -b bw), $bw;
-       runcmd @git, qw(checkout -q -b patch-queue/bw), $head;
-       runcmd qw(gbp pq export);
-       runcmd @git, qw(add debian/patches);
        $out = make_commit ['HEAD'], [
             'Commit patch queue (converted from git-debrebase format)',
             '[git-debrebase convert-to-gbp: commit patches]',
@@ -1406,6 +1489,7 @@ sub cmd_convert_to_gbp () {
        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 $!;
 git-debrebase: converted to git-buildpackage branch format
@@ -1501,3 +1585,5 @@ if (!@ARGV || $opt_defaultcmd_interactive || $ARGV[0] =~ m{^-}) {
     $cmdfn or badusage "unknown git-debrebase sub-operation $cmd";
     $cmdfn->();
 }
+
+finish 0;