X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=dgit.git;a=blobdiff_plain;f=dgit;h=455b375e96e13a7c0f790514e8169fe75ab39490;hp=b3071b71347d9dec2ae3c14d09e56e02cc92f07a;hb=411fe55e96c646557ec19614888658a6cea693b4;hpb=5e8aa6d1695b5eb47a928d1d546605bdf924c494 diff --git a/dgit b/dgit index b3071b71..455b375e 100755 --- 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); @@ -85,7 +86,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 (@gbp) = qw(gbp); our (@changesopts) = (''); our %opts_opt_map = ('dget' => \@dget, # accept for compatibility @@ -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 }; @@ -2564,18 +2578,33 @@ sub quiltify_splitbrain ($$) { 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->{H2O} & 01)) { + my $msg = + "--quilt=$quilt_mode specified, implying patches-unapplied git tree\n". + " but git tree differs from orig in upstream files."; + if (!stat_exists "debian/patches") { + $msg .= + "\n ... debian/patches is missing; perhaps this is a patch queue branch?"; + } + fail $msg; + } if ($quilt_mode =~ m/gbp|unapplied/ && - ($diffbits->{O2A} & 01) && # some patches - !($diffbits->{H2O} & 01)) { # but HEAD is like orig + ($diffbits->{O2A} & 01)) { # some patches quiltify_splitbrain_needed(); - progress "creating patches-applied version using gbp-pq"; + progress "creating patches-applied version using gbp pq"; open STDOUT, ">/dev/null" or die $!; - runcmd shell_cmd 'exec >/dev/null', @gbppq, qw(import); + runcmd shell_cmd 'exec >/dev/null', @gbp, qw(pq 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 +2617,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 +2879,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 +3057,64 @@ 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, $quilt_mode; + 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 () { + 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 +3161,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 +3194,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 +3237,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 +3349,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 @@ -3252,11 +3368,13 @@ sub massage_dbp_args ($;$) { #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; @@ -3293,7 +3411,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(); @@ -3311,9 +3429,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'; @@ -3322,18 +3449,36 @@ 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; - 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", @@ -3674,6 +3819,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');