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);
}
}
+sub quiltmode_splitbrain () {
+ $quilt_mode =~ m/gbp|dpm|unapplied/;
+}
+
#---------- remote protocol support, common ----------
# remote push initiator/responder protocol:
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";
}
}
-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 };
progress "creating patches-applied version using gbp-pq";
open STDOUT, ">/dev/null" or die $!;
runcmd shell_cmd 'exec >/dev/null', @gbppq, qw(import);
- runcmd @git, qw(update-ref dgit-view HEAD);
- runcmd @git, qw(checkout dgit-view);
+ # 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
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 ($$$$) {
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);
$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';
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;
}
#----- 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') {
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
#print STDERR "MASS1 ",Dumper($cmd, $xargs, $dmode);
my $r = 0;
if ($need_split_build_invocation) {
+ printdebug "massage split $dmode.\n";
$r = $dmode =~ m/[S]/ ? +2 :
$dmode =~ y/gGF/ABb/ ? +1 :
$dmode =~ m/[ABb]/ ? 0 :
die "$dmode ?";
}
+ printdebug "massage done $r $dmode.\n";
push @$cmd, $dmode;
#print STDERR "MASS2 ",Dumper($cmd, $xargs, $r);
return $r;
if ($wantsrc > 0) {
build_source();
} else {
- if (!$suppress_clean) {
+ if (!$clean_using_builder) {
push @cmd, '--git-cleaner=true';
}
build_prep();
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';
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;
- changedir "..";
- runcmd_ordryrun_local @dpkgsource, qw(-b --), $leafdir;
- changedir $pwd;
+ my @cmd = (@dpkgsource, qw(-b --));
+ if ($split_brain) {
+ changedir $ud;
+ runcmd_ordryrun_local @cmd, "work";
+ my @udfiles = <${package}_*>;
+ changedir "../../..";
+ foreach my $f (@udfiles) {
+ printdebug "source copy, found $f\n";
+ next unless
+ $f eq $dscfn or
+ ($f =~ m/\.debian\.tar(?:\.\w+)$/ &&
+ $f eq srcfn($version, $&));
+ printdebug "source copy, found $f - renaming\n";
+ rename "$ud/$f", "../$f" or $!==ENOENT
+ or fail "put in place new source file ($f): $!";
+ }
+ } else {
+ my $pwd = must_getcwd();
+ my $leafdir = basename $pwd;
+ changedir "..";
+ runcmd_ordryrun_local @cmd, $leafdir;
+ changedir $pwd;
+ }
runcmd_ordryrun_local qw(sh -ec),
'exec >$1; shift; exec "$@"','x',
"../$sourcechanges",
$quilt_mode = $1;
}
+$need_split_build_invocation ||= quiltmode_splitbrain();
+
if (!defined $cleanmode) {
local $access_forpush;
$cleanmode = access_cfg('clean-mode', 'RETURN-UNDEF');