chiark / gitweb /
Split brain: Actually get gbp pq output back onto dgit-view branch
[dgit.git] / dgit
diff --git a/dgit b/dgit
index 0c8183dc2d614abe2cd27f91188d205752ad1bcd..0812f32ae2e48338a2686ffae70bfcb5050bc580 100755 (executable)
--- a/dgit
+++ b/dgit
@@ -61,7 +61,7 @@ our $cleanmode;
 our $changes_since_version;
 our $rmchanges;
 our $quilt_mode;
-our $quilt_modes_re = 'linear|smash|auto|nofix|nocheck';
+our $quilt_modes_re = 'linear|smash|auto|nofix|nocheck|gbp|unapplied';
 our $we_are_responder;
 our $initiator_tempdir;
 
@@ -70,6 +70,8 @@ our %format_ok = map { $_=>1 } ("1.0","3.0 (native)","3.0 (quilt)");
 our $suite_re = '[-+.0-9a-z]+';
 our $cleanmode_re = 'dpkg-source(?:-d)?|git|git-ff|check|none';
 
+our $git_authline_re = '^([^<>]+) \<(\S+)\> (\d+ [-+]\d+)$';
+
 our (@git) = qw(git);
 our (@dget) = qw(dget);
 our (@curl) = qw(curl -f);
@@ -83,6 +85,7 @@ our (@dpkgbuildpackage) = qw(dpkg-buildpackage -i\.git/ -I.git);
 our (@dpkgsource) = qw(dpkg-source -i\.git/ -I.git);
 our (@dpkggenchanges) = qw(dpkg-genchanges);
 our (@mergechanges) = qw(mergechanges -f);
+our (@gbppq) = qw(gbp-pq);
 our (@changesopts) = ('');
 
 our %opts_opt_map = ('dget' => \@dget, # accept for compatibility
@@ -114,6 +117,7 @@ autoflush STDOUT 1;
 
 our $supplementary_message = '';
 our $need_split_build_invocation = 0;
+our $split_brain = 0;
 
 END {
     local ($@, $?);
@@ -1314,9 +1318,10 @@ sub clogp_authline ($) {
     $author =~ s#,.*##ms;
     my $date = cmdoutput qw(date), '+%s %z', qw(-d), getfield($clogp,'Date');
     my $authline = "$author $date";
-    $authline =~ m/^[^<>]+ \<\S+\> \d+ [-+]\d+$/ or
+    $authline =~ m/$git_authline_re/o or
        fail "unexpected commit author line format \`$authline'".
        " (was generated from changelog Maintainer field)";
+    return ($1,$2,$3) if wantarray;
     return $authline;
 }
 
@@ -2510,11 +2515,14 @@ END
     }
 }
 
-sub quiltify_trees_differ ($$;$) {
-    my ($x,$y,$finegrained) = @_;
+sub quiltify_trees_differ ($$;$$) {
+    my ($x,$y,$finegrained,$ignorenamesr) = @_;
     # returns true iff the two tree objects differ other than in debian/
-    # returns bitmas 01 - differ in upstream files except .gitignore
-    #                02 - differ in .gitignore
+    # with $finegrained,
+    # returns bitmask 01 - differ in upstream files except .gitignore
+    #                 02 - differ in .gitignore
+    # if $ignorenamesr is defined, $ingorenamesr->{$fn}
+    #  is set for each modified .gitignore filename $fn
     local $/=undef;
     my @cmd = (@git, qw(diff-tree --name-only -z));
     push @cmd, qw(-r) if $finegrained;
@@ -2523,7 +2531,9 @@ sub quiltify_trees_differ ($$;$) {
     my $r = 0;
     foreach my $f (split /\0/, $diffs) {
        next if $f =~ m#^debian(?:/.*)?$#s;
-       $r |= ($f =~ m#^(?:.*/)?.gitignore$#s) ? 02 : 01;
+       my $isignore = $f =~ m#^(?:.*/)?.gitignore$#s;
+       $r |= $isignore ? 02 : 01;
+       $ignorenamesr->{$f}=1 if $ignorenamesr && $isignore;
     }
     printdebug "quiltify_trees_differ $x $y => $r\n";
     return $r;
@@ -2536,17 +2546,79 @@ sub quiltify_tree_sentinelfiles ($) {
         qw(-- debian/rules debian/control);
     $r =~ s/\n/,/g;
     return $r;
-}
+                                }
+
+sub quiltify_splitbrain_needed () {
+    if (!$split_brain) {
+       progress "creating dgit view";
+       runcmd @git, qw(checkout -q -b dgit-view);
+       $split_brain = 1;
+    }
+}
+
+sub quiltify_splitbrain ($$$$$) {
+    my ($clogp, $unapplied, $headref, $diffbits, $editedignores) = @_;
+    if ($quilt_mode !~ m/gbp|dpm/) {
+       # treat .gitignore just like any other upstream file
+       $diffbits = { %$diffbits };
+       $_ = !!$_ foreach values %$diffbits;
+    }
+    # We would like any commits we generate to be reproducible
+    my @authline = clogp_authline($clogp);
+    local $ENV{GIT_COMMITTER_NAME} =  $authline[0];
+    local $ENV{GIT_COMMITTER_EMAIL} = $authline[1];
+    local $ENV{GIT_COMMITTER_DATE} =  $authline[2];
+    if ($quilt_mode =~ m/gbp|unapplied/ &&
+       ($diffbits->{O2A} & 01) && # some patches
+       !($diffbits->{H2O} & 01)) { # but HEAD is like orig
+       quiltify_splitbrain_needed();
+       progress "creating patches-applied version using gbp-pq";
+       open STDOUT, ">/dev/null" or die $!;
+       runcmd shell_cmd 'exec >/dev/null', @gbppq, qw(import);
+       # gbp-pq import creates a fresh branch; push back to dgit-view
+       runcmd @git, qw(update-ref refs/heads/dgit-view HEAD);
+       runcmd @git, qw(checkout dgit-view);
+    }
+    if (($diffbits->{H2O} & 02) && # user has modified .gitignore
+       !($diffbits->{O2A} & 02)) { # patches do not change .gitignore
+       quiltify_splitbrain_needed();
+       progress "creating patch to represent .gitignore changes";
+        ensuredir "debian/patches";
+       my $gipatch = "debian/patches/auto-gitignore";
+       open GIPATCH, ">>", "$gipatch" or die "$gipatch: $!";
+       stat GIPATCH or die "$gipatch: $!";
+       fail "$gipatch already exists; but want to create it".
+           " to record .gitignore changes" if (stat _)[7];
+       print GIPATCH <<END or die "$gipatch: $!";
+Subject: Update .gitignore from Debian packaging branch
+
+The Debian packaging git branch contains these updates to the upstream
+.gitignore file(s).  This patch is autogenerated, to provide these
+updates to users of the official Debian archive view of the package.
+
+[dgit version $our_version]
+---
+END
+        close GIPATCH or die "$gipatch: $!";
+        runcmd shell_cmd "exec >>$gipatch", @git, qw(diff),
+            $unapplied, $headref, "--", sort keys %$editedignores;
+        open SERIES, "+>>", "debian/patches/series" or die $!;
+        defined seek SERIES, -1, 2 or $!==EINVAL or die $!;
+        my $newline;
+        defined read SERIES, $newline, 1 or die $!;
+       print SERIES "\n" or die $! unless $newline eq "\n";
+       print SERIES "auto-gitignore\n" or die $!;
+       close SERIES or die  $!;
+        runcmd @git, qw(add -- debian/patches/series), $gipatch;
+        commit_admin "Commit patch to update .gitignore";
+    }
 
-sub quilt_could_gbp ($$$) {
-    my ($userhead,$unapplied,$applied) = @_;
-    return
-       !(quiltify_trees_differ($userhead,$unapplied,1) & 01) &&
-       (quiltify_trees_differ($userhead,$applied,1) & 01);
+    die 'xxx memoisation via git-reflog';
+    die 'xxx fast forward (should not depend on quilt mode, but will always be needed if we did $split_brain)';
 }
 
 sub quiltify ($$$$) {
-    my ($clogp,$target,$unapplied,$oldtiptree) = @_;
+    my ($clogp,$target,$oldtiptree,$failsuggestion) = @_;
 
     # Quilt patchification algorithm
     #
@@ -2684,12 +2756,7 @@ sub quiltify ($$$$) {
            foreach my $notp (@nots) {
                print STDERR "$us:  ", $reportnot->($notp), "\n";
            }
-           if (quilt_could_gbp($target,$unapplied,$oldtiptree)) {
-               print STDERR <<END;
-$us: Tree looks like a patches-unapplied git branch.
-$us: Maybe you forgot --quilt=gbp (or --quilt=apply) ?
-END
-           }
+           print STDERR "$us: $_\n" foreach @$failsuggestion;
            fail "quilt fixup naive history linearisation failed.\n".
  "Use dpkg-source --commit by hand; or, --quilt=smash for one ugly patch";
        } elsif ($quilt_mode eq 'smash') {
@@ -2998,9 +3065,53 @@ END
     rmtree '.pc';
     runcmd @git, qw(add -Af .);
     my $oldtiptree=git_write_tree();
+    printdebug "fake o+d/p tree object $unapplied\n";
     changedir '../work';
 
-    quiltify($clogp,$headref,$unapplied,$oldtiptree);
+
+    # We calculate some guesswork now about what kind of tree this might
+    # be.  This is mostly for error reporting.
+
+    my %editedignores;
+    my $diffbits = {
+        # H = user's HEAD
+        # O = orig, without patches applied
+        # A = "applied", ie orig with H's debian/patches applied
+        H2O => quiltify_trees_differ($headref,  $unapplied, 1,\%editedignores),
+        H2A => quiltify_trees_differ($headref,  $oldtiptree,1),
+        O2A => quiltify_trees_differ($unapplied,$oldtiptree,1),
+    };
+
+    my @dl;
+    foreach my $b (qw(01 02)) {
+        foreach my $v (qw(H2O O2A H2A)) {
+            push @dl, ($diffbits->{$v} & $b) ? '##' : '==';
+        }
+    }
+    printdebug "differences \@dl @dl.\n";
+
+    progress sprintf
+"$us: quilt differences: src:  %s orig %s     gitignores:  %s orig %s\n".
+"$us: quilt differences:      HEAD %s o+d/p               HEAD %s o+d/p",
+                             $dl[0], $dl[1],              $dl[3], $dl[4],
+                                 $dl[2],                     $dl[5];
+
+    my @failsuggestion;
+    if (!($diffbits->{H2O} & $diffbits->{O2A})) {
+        push @failsuggestion, "This might be a patches-unapplied branch.";
+    }  elsif (!($diffbits->{H2A} & $diffbits->{O2A})) {
+        push @failsuggestion, "This might be a patches-applied branch.";
+    }
+    push @failsuggestion, "Maybe you need to specify one of".
+        " --quilt=gbp --quilt=dpm --quilt=unapplied ?";
+
+    if ($quilt_mode =~ m/gbp|dpm|unapplied/) {
+       quiltify_splitbrain($clogp, $unapplied, $headref,
+                            $diffbits, \%editedignores);
+       return;
+    }
+
+    quiltify($clogp,$headref,$oldtiptree,\@failsuggestion);
 
     if (!open P, '>>', ".pc/applied-patches") {
        $!==&ENOENT or die $!;