chiark / gitweb /
Split build: Split clean from source build in split build mode
[dgit.git] / dgit
diff --git a/dgit b/dgit
index 4af8f34cd2b21ab71b08529c9e0aaf2b6efe8db6..9763bbfd6c99fce35d768559fec144416f1dc710 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";
@@ -2556,8 +2565,9 @@ sub quiltify_splitbrain_needed () {
     }
 }
 
-sub quiltify_splitbrain ($$$$$) {
-    my ($clogp, $unapplied, $headref, $diffbits, $editedignores) = @_;
+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 };
@@ -2575,6 +2585,9 @@ 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
@@ -2610,8 +2623,16 @@ END
         commit_admin "Commit patch to update .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)';
+    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 ($$$$) {
@@ -2847,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);
@@ -3023,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';
 
@@ -3102,9 +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/) {
+    if (quiltmode_splitbrain()) {
        quiltify_splitbrain($clogp, $unapplied, $headref,
-                            $diffbits, \%editedignores);
+                            $diffbits, \%editedignores,
+                           $splitbrain_cachekey);
        return;
     }
 
@@ -3144,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') {
@@ -3252,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
@@ -3312,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();
@@ -3330,9 +3415,18 @@ sub cmd_gbp_build {
 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;
+    my $our_cleanmode = $cleanmode;
+    if ($need_split_build_invocation) {
+       # Pretend that clean is being done some other way.  This
+       # forces us not to try to use dpkg-buildpackage to clean and
+       # build source all in one go; and instead we run dpkg-source
+       # (and build_prep() will do the clean since $clean_using_builder
+       # is false).
+       $our_cleanmode = 'ELSEWHERE';
+    }
+    if ($our_cleanmode =~ m/^dpkg-source/) {
+       # dpkg-source invocation (below) will clean, so build_prep shouldn't
+       $clean_using_builder = 1;
     }
     build_prep();
     $sourcechanges = changespat $version,'source';
@@ -3341,12 +3435,12 @@ sub build_source {
            or fail "remove $sourcechanges: $!";
     }
     $dscfn = dscfn($version);
-    if ($cleanmode eq 'dpkg-source') {
+    if ($our_cleanmode eq 'dpkg-source') {
        runcmd_ordryrun_local @dpkgbuildpackage, qw(-us -uc -S),
-                              changesopts();
-    } elsif ($cleanmode eq 'dpkg-source-d') {
+           changesopts();
+    } elsif ($our_cleanmode eq 'dpkg-source-d') {
        runcmd_ordryrun_local @dpkgbuildpackage, qw(-us -uc -S -d),
-                              changesopts();
+           changesopts();
     } else {
        my $pwd = must_getcwd();
        my $leafdir = basename $pwd;
@@ -3693,6 +3787,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');