chiark / gitweb /
Split brain: some work on integration into the rest of dgit
[dgit.git] / dgit
diff --git a/dgit b/dgit
index b3071b71347d9dec2ae3c14d09e56e02cc92f07a..4d31cd606b568e4b7df586de3b802ada2a9a909d 100755 (executable)
--- a/dgit
+++ b/dgit
@@ -71,6 +71,7 @@ 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 $splitbraincache = 'dgit-intern/quilt-cache';
 
 our (@git) = qw(git);
 our (@dget) = qw(dget);
@@ -199,6 +200,10 @@ sub deliberately_not_fast_forward () {
     }
 }
 
+sub quiltmode_splitbrain () {
+    $quilt_mode =~ m/gbp|dpm|unapplied/;
+}
+
 #---------- remote protocol support, common ----------
 
 # remote push initiator/responder protocol:
@@ -1983,10 +1988,14 @@ END
 
     my $format = getfield $dsc, 'Format';
     printdebug "format $format\n";
+
     if (madformat($format)) {
        # user might have not used dgit build, so maybe do this now:
        commit_quilty_patch();
     }
+
+    die 'xxx fast forward (should not depend on quilt mode, but will always be needed if we did $split_brain)' if $split_brain;
+
     check_not_dirty();
     changedir $ud;
     progress "checking that $dscfn corresponds to HEAD";
@@ -2515,12 +2524,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/
     # 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;
@@ -2529,7 +2540,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;
@@ -2552,8 +2565,9 @@ sub quiltify_splitbrain_needed () {
     }
 }
 
-sub quiltify_splitbrain ($$) {
-    my ($clogp, $diffbits) = @_;
+sub quiltify_splitbrain ($$$$$$) {
+    my ($clogp, $unapplied, $headref, $diffbits,
+       $editedignores, $cachekey) = @_;
     if ($quilt_mode !~ m/gbp|dpm/) {
        # treat .gitignore just like any other upstream file
        $diffbits = { %$diffbits };
@@ -2571,11 +2585,15 @@ sub quiltify_splitbrain ($$) {
        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 -q 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: $!";
@@ -2588,13 +2606,33 @@ 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
-    die 'xxx gitignore';
-       
-    }
-    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)';
+        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";
+    }
+
+    my $dgitview = git_rev_parse 'refs/heads/dgit-view';
+
+    changedir '../../../..';
+    ensuredir ".git/logs/refs/dgit-intern";
+    my $makelogfh = new IO::File ".git/logs/refs/$splitbraincache", '>>'
+      or die $!;
+    runcmd @git, qw(update-ref -m), $cachekey, "refs/$splitbraincache",
+       $dgitview;
+
+    changedir '.git/dgit/unpack/work';
 }
 
 sub quiltify ($$$$) {
@@ -2830,6 +2868,8 @@ sub build_maybe_quilt_fixup () {
        quilt_fixup_multipatch($clogp, $headref, $upstreamversion);
     }
 
+    die 'bug' if $split_brain && !$need_split_build_invocation;
+
     changedir '../../../..';
     runcmd_ordryrun_local
         @git, qw(pull --ff-only -q .git/dgit/unpack/work master);
@@ -3006,6 +3046,63 @@ END
     $dscaddfile->($debtar);
     close $fakedsc or die $!;
 
+    my $splitbrain_cachekey;
+    if (quiltmode_splitbrain()) {
+       # we look in the reflog of dgit-intern/quilt-cache
+       # we look for an entry whose message is the key for the cache lookup
+       my @cachekey = (qw(dgit), $our_version);
+       push @cachekey, $upstreamversion;
+       push @cachekey, $headref;
+
+       push @cachekey, hashfile('fake.dsc');
+
+       my $srcshash = Digest::SHA->new(256);
+       my %sfs = ( %INC, '$0(dgit)' => $0 );
+       foreach my $sfk (sort keys %sfs) {
+           $srcshash->add($sfk,"  ");
+           $srcshash->add(hashfile($sfs{$sfk}));
+           $srcshash->add("\n");
+       }
+       push @cachekey, $srcshash->hexdigest();
+       $splitbrain_cachekey = "@cachekey";
+
+       my @cmd = (@git, qw(reflog), '--pretty=format:%H %gs',
+                  $splitbraincache);
+       printdebug "splitbrain cachekey $splitbrain_cachekey\n";
+       debugcmd "|(probably)",@cmd;
+       my $child = open GC, "-|";  defined $child or die $!;
+       if (!$child) {
+           chdir '../../..' or die $!;
+           if (!stat ".git/logs/refs/$splitbraincache") {
+               $! == ENOENT or die $!;
+               printdebug ">(no reflog)\n";
+               exit 0;
+           }
+           exec @cmd; die $!;
+       }
+       while (<GC>) {
+           chomp;
+           printdebug ">| ", $_, "\n" if $debuglevel > 1;
+           next unless m/^(\w+) (\S.*\S)$/ && $2 eq $splitbrain_cachekey;
+           
+           my $cachehit = $1;
+           quilt_fixup_mkwork($headref);
+           if ($cachehit ne $headref) {
+               progress "quilt fixup ($quilt_mode mode) found cached tree";
+               runcmd @git, qw(checkout -q -b dgit-view), $cachehit;
+               $split_brain = 1;
+               return;
+           }
+           progress "quilt fixup ($quilt_mode mode)".
+             " found cached indication that no changes needed";
+           return;
+       }
+       die $! if GC->error;
+       failedcmd unless close GC;
+
+       printdebug "splitbrain cache miss\n";
+    }
+
     runcmd qw(sh -ec),
         'exec dpkg-source --no-check --skip-patches -x fake.dsc >/dev/null';
 
@@ -3052,11 +3149,12 @@ END
     # 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),
+        H2O => quiltify_trees_differ($headref,  $unapplied, 1,\%editedignores),
         H2A => quiltify_trees_differ($headref,  $oldtiptree,1),
         O2A => quiltify_trees_differ($unapplied,$oldtiptree,1),
     };
@@ -3084,8 +3182,10 @@ END
     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, $diffbits);
+    if (quiltmode_splitbrain()) {
+       quiltify_splitbrain($clogp, $unapplied, $headref,
+                            $diffbits, \%editedignores,
+                           $splitbrain_cachekey);
        return;
     }
 
@@ -3125,10 +3225,14 @@ sub quilt_fixup_editor () {
 
 #----- other building -----
 
-our $suppress_clean;
+our $clean_using_builder;
+# ^ tree is to be cleaned by dpkg-source's builtin idea that it should
+#   clean the tree before building (perhaps invoked indirectly by
+#   whatever we are using to run the build), rather than separately
+#   and explicitly by us.
 
 sub clean_tree () {
-    return if $suppress_clean;
+    return if $clean_using_builder;
     if ($cleanmode eq 'dpkg-source') {
        runcmd_ordryrun_local @dpkgbuildpackage, qw(-T clean);
     } elsif ($cleanmode eq 'dpkg-source-d') {
@@ -3233,7 +3337,7 @@ sub massage_dbp_args ($;$) {
     debugcmd '#massaging#', @$cmd if $debuglevel>1;
 #print STDERR "MASS0 ",Dumper($cmd, $xargs, $need_split_build_invocation);
     if ($cleanmode eq 'dpkg-source' && !$need_split_build_invocation) {
-       $suppress_clean = 1;
+       $clean_using_builder = 1;
        return 0;
     }
     # -nc has the side effect of specifying -b if nothing else specified
@@ -3293,7 +3397,7 @@ sub cmd_gbp_build {
     if ($wantsrc > 0) {
        build_source();
     } else {
-       if (!$suppress_clean) {
+       if (!$clean_using_builder) {
            push @cmd, '--git-cleaner=true';
        }
        build_prep();
@@ -3313,7 +3417,7 @@ sub cmd_git_build { cmd_gbp_build(); } # compatibility with <= 1.0
 sub build_source {
     if ($cleanmode =~ m/^dpkg-source/) {
        # dpkg-source will clean, so we shouldn't
-       $suppress_clean = 1;
+       $clean_using_builder = 1;
     }
     build_prep();
     $sourcechanges = changespat $version,'source';
@@ -3674,6 +3778,8 @@ if (!defined $quilt_mode) {
     $quilt_mode = $1;
 }
 
+$need_split_build_invocation ||= quiltmode_splitbrain();
+
 if (!defined $cleanmode) {
     local $access_forpush;
     $cleanmode = access_cfg('clean-mode', 'RETURN-UNDEF');