chiark / gitweb /
git-debrebase: bugfixes
[dgit.git] / git-debrebase
index b57e42833cee4a5562d2cf8510d62620ad6c44e0..88118aa42c6decdc923647bc7dcd241a03fbec40 100755 (executable)
@@ -116,7 +116,7 @@ sub dd ($) {
 
 sub get_commit ($) {
     my ($objid) = @_;
-    my $data = git_cat_file $objid, 'commit';
+    my $data = (git_cat_file $objid, 'commit');
     $data =~ m/(?<=\n)\n/ or die "$objid ($data) ?";
     return ($`,$');
 }
@@ -162,7 +162,7 @@ sub get_differs ($$) {
        my @cmd = (@git, qw(diff-tree -z --no-renames));
        push @cmd, @$opts;
        push @cmd, "$_:" foreach $x, $y;
-       push @cmd, @$limits;
+       push @cmd, '--', @$limits;
        my $diffs = cmdoutput @cmd;
        foreach (split /\0/, $diffs) { $fn->(); }
     };
@@ -200,8 +200,8 @@ sub get_differs ($$) {
            if ($mode eq 'A' && !m/\.series$/s) {
                $ok = 1;
            } elsif ($mode eq 'M' && $_ eq 'series') {
-               my $x_s = git_cat_file "$x:debian/patches/series", 'blob';
-               my $y_s = git_cat_file "$y:debian/patches/series", 'blob';
+               my $x_s = (git_cat_file "$x:debian/patches/series", 'blob');
+               my $y_s = (git_cat_file "$y:debian/patches/series", 'blob');
                chomp $x_s;  $x_s .= "\n";
                $ok = $x_s eq substr($y_s, 0, length $x_s);
            } else {
@@ -252,6 +252,26 @@ sub make_commit ($$) {
     return cmdoutput @cmd;
 }
 
+our $fproblems;
+sub fproblem ($) {
+    my ($msg) = @_;
+    $fproblems++;
+    print STDERR "git-debrebase: safety catch tripped: $msg\n";
+}
+sub fproblems_maybe_bail () {
+    if ($fproblems) {
+       if ($opt_force) {
+           printf STDERR
+               "safety catch trips (%d) overriden by --force\n",
+               $fproblems;
+       } else {
+           fail sprintf
+               "safety catch trips (%d) (you could --force)",
+               $fproblems;
+       }
+    }
+}
+
 # classify returns an info hash like this
 #   CommitId => $objid
 #   Hdr => # commit headers, including 1 final newline
@@ -750,9 +770,8 @@ sub update_head ($$$) {
 
 sub update_head_checkout ($$$) {
     my ($old, $new, $mrest) = @_;
-    my $symref = git_get_symref();
-    runcmd @git, qw(checkout), $new, qw(.);
     update_head $old, $new, $mrest;
+    runcmd @git, qw(reset --hard);
 }
 
 sub update_head_postlaunder ($$$) {
@@ -853,18 +872,11 @@ sub cmd_new_upstream_v0 () {
     my $old_upstream = parsecommit
         $old_upstream_update_cl->{OrigParents}[0]{CommitId};
 
-    my $problems = 0;
-    my $problem = sub {
-       my ($msg) = @_;
-       $problems++;
-       print STDERR "preflight check failed: $msg\n";
-    };
-
     $piece->('', Old => $old_upstream->{CommitId});
 
     if ($old_upstream->{Msg} =~ m{^\[git-debrebase }m) {
        if ($old_upstream->{Msg} =~
- m{^\[git-debrebase new-upstream combine \.((?: $extra_orig_namepart_re)+)\]}
+ m{^\[git-debrebase (?:\w*-)?upstream combine \.((?: $extra_orig_namepart_re)+)\]}
           ) {
            my @oldpieces = ('', split / /, $1);
            my $parentix = -1 + scalar @{ $old_upstream->{Parents} };
@@ -873,35 +885,25 @@ sub cmd_new_upstream_v0 () {
                $piece->($n, Old => $old_upstream->{CommitId}.'^'.$parentix);
            }
        } else {
-           $problem->("previous upstream $old_upstream->{CommitId} is from".
-                 " git-debrebase but not a \`new-upstream combine' commit");
+           fproblem "previous upstream $old_upstream->{CommitId} is from".
+                 " git-debrebase but not an \`upstream combine' commit";
        }
     }
 
     foreach my $pc (values %pieces) {
        if (!$pc->{Old}) {
-           $problem->("introducing upstream piece \`$pc->{Name}'");
+           fproblem "introducing upstream piece \`$pc->{Name}'";
        } elsif (!$pc->{New}) {
-           $problem->("dropping upstream piece \`$pc->{Name}'");
+           fproblem "dropping upstream piece \`$pc->{Name}'";
        } elsif (!is_fast_fwd $pc->{Old}, $pc->{New}) {
-           $problem->("not fast forward: $pc->{Name} $pc->{Old}..$pc->{New}");
+           fproblem "not fast forward: $pc->{Name} $pc->{Old}..$pc->{New}";
        }
     }
 
     printdebug "%pieces = ", (dd \%pieces), "\n";
     printdebug "\@newpieces = ", (dd \@newpieces), "\n";
 
-    if ($problems) {
-       if ($opt_force) {
-           printf STDERR
-               "preflight check failures (%d) overriden by --force\n",
-               $problems;
-       } else {
-           fail sprintf
-               "preflight check failures (%d) (you could --force)",
-               $problems;
-       }
-    }
+    fproblems_maybe_bail();
 
     my $new_bw;
 
@@ -909,7 +911,7 @@ sub cmd_new_upstream_v0 () {
     in_workarea sub {
        my @upstream_merge_parents;
 
-       if (!$problems) {
+       if (!$fproblems) {
            push @upstream_merge_parents, $old_upstream->{CommitId};
        }
 
@@ -987,6 +989,71 @@ END
     # now it's for the user to sort out
 }
 
+sub cmd_gbp2debrebase () {
+    badusage "needs 1 optional argument, the upstream" unless @ARGV<=1;
+    my ($upstream_spec) = @ARGV;
+    $upstream_spec //= 'refs/heads/upstream';
+    my $upstream = git_rev_parse $upstream_spec;
+    my $old_head = get_head();
+
+    my $upsdiff = get_differs $upstream, $old_head;
+    if ($upsdiff & D_UPS) {
+       runcmd @git, qw(--no-pager diff),
+           $upstream, $old_head,
+           qw( -- :!/debian :/);
+ fail "upstream ($upstream_spec) and HEAD are not identical in upstream files";
+    }
+
+    if (!is_fast_fwd $upstream, $old_head) {
+       fproblem "upstream ($upstream) is not an ancestor of HEAD";
+    } else {
+       my $wrong = cmdoutput
+           (@git, qw(rev-list --ancestry-path), "$upstream..HEAD",
+            qw(-- :/ :!/debian));
+       if (length $wrong) {
+           fproblem "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";
+       }
+    }
+
+    if ((git_cat_file "$upstream:debian")[0] ne 'missing') {
+       fproblem "upstream ($upstream) contains debian/ directory";
+    }
+
+    fproblems_maybe_bail();
+
+    my $work;
+
+    fresh_workarea();
+    in_workarea sub {
+       runcmd @git, qw(checkout -q -b gdr-internal), $old_head;
+       # make a branch out of the patch queue - we'll want this in a mo
+       runcmd qw(gbp pq import);
+       # strip the patches out
+       runcmd @git, qw(checkout -q gdr-internal~0);
+       rm_subdir_cached 'debian/patches';
+       $work = make_commit ['HEAD'], [
+ 'git-debrebase import: drop patch queue',
+ 'Delete debian/patches, as part of converting to git-debrebase format.',
+ '[git-debrebase gbp2debrebase drop-patches]'
+                             ];
+       # make the breakwater pseudomerge
+       # the tree is already exactly right
+       $work = make_commit [$work, $upstream], [
+ 'git-debrebase import: declare upstream',
+ 'First breakwater merge.',
+ '[git-debrebase declare-upstream breakwater]'
+                             ];
+
+       # 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();
+    };
+
+    update_head_checkout $old_head, $work, 'gbp2debrebase';
+}
+
 sub cmd_downstream_rebase_launder_v0 () {
     badusage "needs 1 argument, the baseline" unless @ARGV==1;
     my ($base) = @ARGV;