chiark / gitweb /
git-debrebase: slight message improvements etc.
[dgit.git] / git-debrebase
index 726c7be8a75d7b3d13feefb78e1d5d7a3c50cbc2..5adbb8fecdfcc702b5822b1ea7e118942369bbd4 100755 (executable)
 #    git-debrebase [<options> --] [<git-rebase options...>]
 #    git-debrebase [<options>] analyse
 #    git-debrebase [<options>] launder         # prints breakwater tip etc.
+#    git-debrebase [<options>] stitch [--prose=<for commit message>]
 #    git-debrebase [<options>] downstream-rebase-launder-v0  # experimental
 #
-#    git-debrebase [<options>] gbp2debrebase-v0 \
-#             <upstream>
+#    git-debrebase [<options>] convert-from-gbp <upstream-git-rev>
 
 # problems / outstanding questions:
 #
@@ -100,7 +100,7 @@ sub badusage ($) {
 
 sub cfg ($;$) {
     my ($k, $optional) = @_;
-    $/ = "\0";
+    local $/ = "\0";
     my @cmd = qw(git config -z);
     push @cmd, qw(--get-all) if wantarray;
     push @cmd, $k;
@@ -441,7 +441,7 @@ sub classify ($) {
        my @overwritten = grep { $_->{Differs} } @p;
        confess "internal error $objid ?" unless @overwritten==1;
        return $classify->(qw(Pseudomerge),
-                          Overwritten => $overwritten[0],
+                          Overwritten => [ $overwritten[0] ],
                           Contributor => $identical[0]);
     }
     if (@p == 2 && @identical == 2) {
@@ -452,7 +452,7 @@ sub classify ($) {
        } @p;
        return $classify->(qw(Pseudomerge),
                           SubType => qw(Ambiguous),
-                          Overwritten => $bytime[0],
+                          Overwritten => [ $bytime[0] ],
                           Contributor => $bytime[1]);
     }
     foreach my $p (@p) {
@@ -626,7 +626,8 @@ sub walk ($;$$) {
                # suite intended by the non-dgit NMUer, and later
                # pseudomerges may represent in-archive copies.
                my $ovwrs = $pm->{Overwritten};
-               printf $report " PM=%s \@Overwr:%d", $pm, (scalar @$ovwrs)
+               printf $report " PM=%s \@Overwr:%d",
+                   $pm->{CommitId}, (scalar @$ovwrs)
                    if $report;
                if (@$ovwrs != 1) {
                     printdebug "*** WALK BOMB DgitImportUnpatched\n";
@@ -649,26 +650,15 @@ sub walk ($;$$) {
                    %$cl,
                    SpecialMethod => 'DgitImportDebianUpdate',
                     $xmsg->("convert dgit import: debian changes")
+               }, {
+                   %$cl,
+                   SpecialMethod => 'DgitImportUpstreamUpdate',
+                    $xmsg->("convert dgit import: upstream update",
+                           " breakwater")
                };
-               my $differs = (get_differs $ovwr, $cl->{Tree});
-               printf $report " Differs=%#x", $differs if $report;
-               if ($differs & D_UPS) {
-                   printf $report " D_UPS" if $report;
-                   # This will also trigger if a non-dgit git-based NMU
-                   # deleted .gitignore (which is a thing that some of
-                   # the existing git tools do if the user doesn't
-                   # somehow tell them not to).  Ah well.
-                   push @brw_cl, {
-                       %$cl,
-                       SpecialMethod => 'DgitImportUpstreamUpdate',
-                       $xmsg->("convert dgit import: upstream changes",
-                               " breakwater")
-                   };
-               }
                $prline->(" Import");
                $rewrite_from_here->();
                $upp_limit //= $#upp_cl; # further, deeper, patches discarded
-                die 'BUG $upp_limit is not used anywhere?';
                $cur = $ovwr;
                next;
            } else {
@@ -710,6 +700,8 @@ sub walk ($;$$) {
        runcmd @git, qw(read-tree), $treeish;
        $read_tree_debian->($build);
     };
+
+    $#upp_cl = $upp_limit if defined $upp_limit;
  
     my $committer_authline = calculate_committer_authline();
 
@@ -745,8 +737,10 @@ sub walk ($;$$) {
                next;
            } elsif ($method eq 'DgitImportDebianUpdate') {
                $read_tree_debian->($cltree);
-               rm_subdir_cached qw(debian/patches);
            } elsif ($method eq 'DgitImportUpstreamUpdate') {
+               confess unless $rewriting;
+               my $differs = (get_differs $build, $cltree);
+               next unless $differs & D_UPS;
                $read_tree_upstream->($cltree);
                push @parents, map { $_->{CommitId} } @{ $cl->{OrigParents} };
            } else {
@@ -762,7 +756,7 @@ sub walk ($;$$) {
            my $newtree = cmdoutput @git, qw(write-tree);
            my $ch = $cl->{Hdr};
            $ch =~ s{^tree .*}{tree $newtree}m or confess "$ch ?";
-           $ch =~ s{^parent .*\n}{}m;
+           $ch =~ s{^parent .*\n}{}mg;
            $ch =~ s{(?=^author)}{
                join '', map { "parent $_\n" } @parents
            }me or confess "$ch ?";
@@ -795,7 +789,10 @@ sub walk ($;$$) {
     return @r
 }
 
-sub get_head () { return git_rev_parse qw(HEAD); }
+sub get_head () {
+    git_check_unmodified();
+    return git_rev_parse qw(HEAD);
+}
 
 sub update_head ($$$) {
     my ($old, $new, $mrest) = @_;
@@ -829,9 +826,14 @@ sub cmd_launder () {
 
 sub defaultcmd_rebase () {
     my $old = get_head();
+    my ($status, $message) = record_ffq_prev();
+    if ($status eq 'written' || $status eq 'exists') {
+    } else {
+       fproblem $status, "could not record ffq-prev: $message";
+       fproblems_maybe_bail();
+    }
     my ($tip,$breakwater) = walk $old;
     update_head_postlaunder $old, $tip, 'launder for rebase';
-    @ARGV = qw(-i) unless @ARGV; # make configurable
     runcmd @git, qw(rebase), @ARGV, $breakwater;
 }
 
@@ -842,12 +844,26 @@ sub cmd_analyse () {
     if (defined $old) {
        $old = git_rev_parse $old;
     } else {
-       $old = get_head();
+       $old = git_rev_parse 'HEAD';
     }
     my ($dummy,$breakwater) = walk $old, 1,*STDOUT;
     STDOUT->error and die $!;
 }
 
+sub ffq_prev_branchinfo () {
+    # => ('status', "message", [$current, $ffq_prev])
+    # 'status' may be
+    #    branch         message is undef
+    #    weird-symref   } no $current,
+    #    notbranch      }  no $ffq_prev
+    my $current = git_get_symref();
+    return ('detached', 'detached HEAD') unless defined $current;
+    return ('weird-symref', 'HEAD symref is not to refs/')
+       unless $current =~ m{^refs/};
+    my $ffq_prev = "refs/$ffq_refprefix/$'";
+    return ('branch', undef, $current, $ffq_prev);
+}
+
 sub record_ffq_prev () {
     # => ('status', "message")
     # 'status' may be
@@ -859,22 +875,19 @@ sub record_ffq_prev () {
     # if not ff from some branch we should be ff from, is an fproblem
     # if "written", will have printed something about that to stdout,
     #   and also some messages about ff checks
-    my $current = git_get_symref();
-    return ('detached', 'detached HEAD') unless defined $current;
-    return ('weird-symref', 'HEAD symref is not to refs/')
-       unless $current =~ m{^refs/};
-    my $ffq_prev = "refs/$ffq_refprefix/$'";
+    my ($status, $message, $current, $ffq_prev) = ffq_prev_branchinfo();
+    return ($status, $message) unless $status eq 'branch';
 
     my $currentval = get_head();
 
     my $exists = git_get_ref $ffq_prev;
-    return ('exists',"$ffq_prev already exists") if defined $exists;
+    return ('exists',"$ffq_prev already exists") if $exists;
 
     return ('not-branch', 'HEAD symref is not to refs/heads/')
        unless $current =~ m{^refs/heads/};
     my $branch = $';
 
-    my @check_specs = split /\;/, (cfg "branch.$branch.ffq-ffrefs") // '*';
+    my @check_specs = split /\;/, (cfg "branch.$branch.ffq-ffrefs",1) // '*';
     my %checked;
 
     my $check = sub {
@@ -901,7 +914,7 @@ sub record_ffq_prev () {
        }
     };
 
-    my $merge = cfg "branch.$branch.merge";
+    my $merge = cfg "branch.$branch.merge",1;
     if (defined $merge && $merge =~ m{^refs/heads/}) {
        my $rhs = $';
        my $check_remote = sub {
@@ -909,17 +922,19 @@ sub record_ffq_prev () {
            return unless defined $remote;
            $check->("refs/remotes/$remote/$rhs", $desc);
        };
-       $check_remote->((cfg "branch.$branch.remote"),
+       $check_remote->((cfg "branch.$branch.remote",1),
                        'remote fetch/merge branch');
-       $check_remote->((cfg "branch.$branch.pushRemote") //
-                       (cfg "branch.$branch.pushDefault"),
+       $check_remote->((cfg "branch.$branch.pushRemote",1) //
+                       (cfg "branch.$branch.pushDefault",1),
                        'remote push branch');
     }
     if ($branch =~ m{^dgit/}) {
        $check->("remotes/dgit/$branch", 'remote dgit branch');
+    } elsif ($branch =~ m{^master$}) {
+       $check->("remotes/dgit/dgit/sid", 'remote dgit branch for sid');
     }
 
-    fproblems_maybe_fail();
+    fproblems_maybe_bail();
     runcmd @git, qw(update-ref -m), "record current head for preservation",
        $ffq_prev, $currentval, $git_null_obj;
     print "Recorded current head for preservation\n" or die $!;
@@ -927,7 +942,6 @@ sub record_ffq_prev () {
 }
 
 sub cmd_new_upstream_v0 () {
-    # tree should be clean and this is not checked
     # automatically and unconditionally launders before rebasing
     # if rebase --abort is used, laundering has still been done
 
@@ -1116,8 +1130,41 @@ sub cmd_record_ffq_prev () {
     }
 }
 
-sub cmd_gbp2debrebase () {
-    badusage "needs 1 optional argument, the upstream" unless @ARGV<=1;
+sub cmd_stitch () {
+    my $prose = '';
+    GetOptions('prose=s', \$prose) or die badusage("bad options to stitch");
+    badusage "no arguments allowed" if @ARGV;
+    my ($status, $message, $current, $ffq_prev) = ffq_prev_branchinfo();
+    if ($status ne 'branch') {
+       fproblem $status, "could not check ffq-prev: $message";
+       fproblems_maybe_bail();
+    }
+    my $prev = $ffq_prev && git_get_ref $ffq_prev;
+    if (!$prev) {
+       fail "No ffq-prev to stitch." unless $opt_noop_ok;
+    }
+    fresh_workarea();
+    my $old_head = get_head();
+    my $new_head = make_commit [ $old_head, $ffq_prev ], [
+       'Declare fast forward / record previous work',
+        "[git-debrebase pseudomerge: stitch$prose]",
+    ];
+    my @upd_cmd = (@git, qw(update-ref --stdin));
+    debugcmd '>|', @upd_cmd;
+    open U, "|-", @upd_cmd or die $!;
+    my $u = <<END;
+update HEAD $new_head $old_head
+delete $ffq_prev $prev
+END
+    printdebug ">= ", $_, "\n" foreach split /\n/, $u;
+    print U $u;
+    printdebug ">\$\n";
+    close U or failedcmd @upd_cmd;
+}
+
+sub cmd_convert_from_gbp () {
+    badusage "needs 1 optional argument, the upstream git rev"
+       unless @ARGV<=1;
     my ($upstream_spec) = @ARGV;
     $upstream_spec //= 'refs/heads/upstream';
     my $upstream = git_rev_parse $upstream_spec;
@@ -1163,9 +1210,9 @@ sub cmd_gbp2debrebase () {
        runcmd @git, qw(checkout -q gdr-internal~0);
        rm_subdir_cached 'debian/patches';
        $work = make_commit ['HEAD'], [
- 'git-debrebase import: drop patch queue',
+ 'git-debrebase convert-from-gbp: drop patches from tree',
  'Delete debian/patches, as part of converting to git-debrebase format.',
- '[git-debrebase: gbp2debrebase, drop patches]'
+ '[git-debrebase convert-from-gbp: drop patches from tree]'
                              ];
        # make the breakwater pseudomerge
        # the tree is already exactly right
@@ -1178,10 +1225,10 @@ sub cmd_gbp2debrebase () {
        # rebase the patch queue onto the new breakwater
        runcmd @git, qw(reset --quiet --hard patch-queue/gdr-internal);
        runcmd @git, qw(rebase --quiet --onto), $work, qw(gdr-internal);
-       $work = get_head();
+       $work = git_rev_parse 'HEAD';
     };
 
-    update_head_checkout $old_head, $work, 'gbp2debrebase';
+    update_head_checkout $old_head, $work, 'convert-from-gbp';
 }
 
 sub cmd_downstream_rebase_launder_v0 () {