X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=dgit.git;a=blobdiff_plain;f=dgit;h=11f473ca8bf7c56096316094382db3f45f150a6e;hp=ee82cd06485146a85a3a69d9d890de439cbaeacf;hb=1837d3b05b891491c548a4a3b869f5ed24239886;hpb=1ece375ce336479b22d45415cda01528e96f823e diff --git a/dgit b/dgit index ee82cd06..11f473ca 100755 --- a/dgit +++ b/dgit @@ -48,7 +48,7 @@ our $absurdity = undef; ###substituted### our @rpushprotovsn_support = qw(4 3 2); # 4 is new tag format our $protovsn; -our $isuite = 'unstable'; +our $isuite; our $idistro; our $package; our @ropts; @@ -69,13 +69,17 @@ our $rmchanges; our $overwrite_version; # undef: not specified; '': check changelog our $quilt_mode; our $quilt_modes_re = 'linear|smash|auto|nofix|nocheck|gbp|dpm|unapplied'; +our $dodep14tag; +our $dodep14tag_re = 'want|no|always'; our $split_brain_save; our $we_are_responder; +our $we_are_initiator; our $initiator_tempdir; our $patches_applied_dirtily = 00; our $tagformat_want; our $tagformat; our $tagformatfn; +our $chase_dsc_distro=1; our %forceopts = map { $_=>0 } qw(unrepresentable unsupported-source-format @@ -94,6 +98,7 @@ our $orig_f_tail_re = "$orig_f_comp_re\\.tar(?:\\.\\w+)?(?:$orig_f_sig_re)?"; our $git_authline_re = '^([^<>]+) \<(\S+)\> (\d+ [-+]\d+)$'; our $splitbraincache = 'dgit-intern/quilt-cache'; +our $rewritemap = 'dgit-rewrite/map'; our (@git) = qw(git); our (@dget) = qw(dget); @@ -139,7 +144,7 @@ our %opts_cfg_insertpos = map { scalar @{ $opts_opt_map{$_} } } keys %opts_opt_map; -sub finalise_opts_opts(); +sub parseopts_late_defaults(); our $keyid; @@ -182,30 +187,6 @@ sub lref () { return "refs/heads/".lbranch(); } sub lrref () { return "refs/remotes/$remotename/".server_branch($csuite); } sub rrref () { return server_ref($csuite); } -sub lrfetchrefs () { return "refs/dgit-fetch/$csuite"; } -sub lrfetchref () { return lrfetchrefs.'/'.server_branch($csuite); } - -# We fetch some parts of lrfetchrefs/*. Ideally we delete these -# locally fetched refs because they have unhelpful names and clutter -# up gitk etc. So we track whether we have "used up" head ref (ie, -# whether we have made another local ref which refers to this object). -# -# (If we deleted them unconditionally, then we might end up -# re-fetching the same git objects each time dgit fetch was run.) -# -# So, leach use of lrfetchrefs needs to be accompanied by arrangements -# in git_fetch_us to fetch the refs in question, and possibly a call -# to lrfetchref_used. - -our (%lrfetchrefs_f, %lrfetchrefs_d); -# $lrfetchrefs_X{lrfetchrefs."/heads/whatever"} = $objid - -sub lrfetchref_used ($) { - my ($fullrefname) = @_; - my $objid = $lrfetchrefs_f{$fullrefname}; - $lrfetchrefs_d{$fullrefname} = $objid if defined $objid; -} - sub stripepoch ($) { my ($vsn) = @_; $vsn =~ s/^\d+\://; @@ -585,6 +566,8 @@ sub cmd_help () { our $td = $ENV{DGIT_TEST_DUMMY_DIR} || "DGIT_TEST_DUMMY_DIR-unset"; our %defcfg = ('dgit.default.distro' => 'debian', + 'dgit.default.default-suite' => 'unstable', + 'dgit.default.old-dsc-distro' => 'debian', 'dgit-suite.*-security.distro' => 'debian-security', 'dgit.default.username' => '', 'dgit.default.archive-query-default-component' => 'main', @@ -593,6 +576,10 @@ our %defcfg = ('dgit.default.distro' => 'debian', 'dgit.default.sshpsql-dbname' => 'service=projectb', 'dgit.default.aptget-components' => 'main', 'dgit.default.dgit-tag-format' => 'new,old,maint', + 'dgit.dsc-url-proto-ok.http' => 'true', + 'dgit.dsc-url-proto-ok.https' => 'true', + 'dgit.dsc-url-proto-ok.git' => 'true', + 'dgit.default.dsc-url-proto-ok' => 'false', # old means "repo server accepts pushes with old dgit tags" # new means "repo server accepts pushes with new dgit tags" # maint means "repo server accepts split brain pushes" @@ -680,7 +667,10 @@ sub git_get_config ($) { my ($c) = @_; foreach my $src (@gitcfgsources) { my $l = $gitcfgs{$src}{$c}; - printdebug"C $c ".(defined $l ? messagequote "'$l'" : "undef")."\n" + croak "$l $c" if $l && !ref $l; + printdebug"C $c ".(defined $l ? + join " ", map { messagequote "'$_'" } @$l : + "undef")."\n" if $debuglevel >= 4; $l or next; @$l==1 or badcfg "multiple values for $c". @@ -693,16 +683,20 @@ sub git_get_config ($) { sub cfg { foreach my $c (@_) { return undef if $c =~ /RETURN-UNDEF/; + printdebug "C? $c\n" if $debuglevel >= 5; my $v = git_get_config($c); return $v if defined $v; my $dv = $defcfg{$c}; - return $dv if defined $dv; + if (defined $dv) { + printdebug "CD $c $dv\n" if $debuglevel >= 4; + return $dv; + } } badcfg "need value for one of: @_\n". "$us: distro or suite appears not to be (properly) supported"; } -sub access_basedistro () { +sub access_basedistro__noalias () { if (defined $idistro) { return $idistro; } else { @@ -722,9 +716,18 @@ sub access_basedistro () { } } +sub access_basedistro () { + my $noalias = access_basedistro__noalias(); + my $canon = cfg("dgit-distro.$noalias.alias-canon",'RETURN-UNDEF'); + return $canon // $noalias; +} + sub access_nomdistro () { my $base = access_basedistro(); - return cfg("dgit-distro.$base.nominal-distro",'RETURN-UNDEF') // $base; + my $r = cfg("dgit-distro.$base.nominal-distro",'RETURN-UNDEF') // $base; + $r =~ m/^$distro_re$/ or badcfg + "bad syntax for (nominal) distro \`$r' (does not match /^$distro_re$/)"; + return $r; } sub access_quirk () { @@ -788,11 +791,11 @@ sub pushing () { Push failed, before we got started. You can retry the push, after fixing the problem, if you like. END - finalise_opts_opts(); + parseopts_late_defaults(); } sub notpushing () { - finalise_opts_opts(); + parseopts_late_defaults(); } sub supplementary_message ($) { @@ -1519,6 +1522,15 @@ sub access_cfg_tagformats () { split /\,/, access_cfg('dgit-tag-format'); } +sub access_cfg_tagformats_can_splitbrain () { + my %y = map { $_ => 1 } access_cfg_tagformats; + foreach my $needtf (qw(new maint)) { + next if $y{$needtf}; + return 0; + } + return 1; +} + sub need_tagformat ($$) { my ($fmt, $why) = @_; fail "need to use tag format $fmt ($why) but also need". @@ -1668,6 +1680,7 @@ sub create_remote_git_repo () { } our ($dsc_hash,$lastpush_mergeinput); +our ($dsc_distro, $dsc_hint_tag, $dsc_hint_url); our $ud = '.git/dgit/unpack'; @@ -2356,6 +2369,8 @@ END my $path = $ENV{PATH} or die; foreach my $use_absurd (qw(0 1)) { + runcmd @git, qw(checkout -q unpa); + runcmd @git, qw(update-ref -d refs/heads/patch-queue/unpa); local $ENV{PATH} = $path; if ($use_absurd) { chomp $@; @@ -2372,11 +2387,12 @@ END die "only absurd git-apply!\n" if !$use_absurd && forceing [qw(import-gitapply-absurd)]; - local $ENV{PATH} = $path if $use_absurd; + local $ENV{DGIT_ABSURD_DEBUG} = $debuglevel if $use_absurd; + local $ENV{PATH} = $path if $use_absurd; my @showcmd = (gbp_pq, qw(import)); my @realcmd = shell_cmd - 'exec >/dev/null 2>../../gbp-pq-output', @showcmd; + 'exec >/dev/null 2>>../../gbp-pq-output', @showcmd; debugcmd "+",@realcmd; if (system @realcmd) { die +(shellquote @showcmd). @@ -2488,18 +2504,41 @@ sub ensure_we_have_orig () { } } -sub git_fetch_us () { - # Want to fetch only what we are going to use, unless - # deliberately-not-ff, in which case we must fetch everything. +#---------- git fetch ---------- - my @specs = deliberately_not_fast_forward ? qw(tags/*) : - map { "tags/$_" } - (quiltmode_splitbrain - ? (map { $_->('*',access_nomdistro) } - \&debiantag_new, \&debiantag_maintview) - : debiantags('*',access_nomdistro)); - push @specs, server_branch($csuite); - push @specs, qw(heads/*) if deliberately_not_fast_forward; +sub lrfetchrefs () { return "refs/dgit-fetch/".access_basedistro(); } +sub lrfetchref () { return lrfetchrefs.'/'.server_branch($csuite); } + +# We fetch some parts of lrfetchrefs/*. Ideally we delete these +# locally fetched refs because they have unhelpful names and clutter +# up gitk etc. So we track whether we have "used up" head ref (ie, +# whether we have made another local ref which refers to this object). +# +# (If we deleted them unconditionally, then we might end up +# re-fetching the same git objects each time dgit fetch was run.) +# +# So, leach use of lrfetchrefs needs to be accompanied by arrangements +# in git_fetch_us to fetch the refs in question, and possibly a call +# to lrfetchref_used. + +our (%lrfetchrefs_f, %lrfetchrefs_d); +# $lrfetchrefs_X{lrfetchrefs."/heads/whatever"} = $objid + +sub lrfetchref_used ($) { + my ($fullrefname) = @_; + my $objid = $lrfetchrefs_f{$fullrefname}; + $lrfetchrefs_d{$fullrefname} = $objid if defined $objid; +} + +sub git_lrfetch_sane { + my ($supplementary, @specs) = @_; + # Make a 'refs/'.lrfetchrefs.'/*' be just like on server, + # at least as regards @specs. Also leave the results in + # %lrfetchrefs_f, and arrange for lrfetchref_used to be + # able to clean these up. + # + # With $supplementary==1, @specs must not contain wildcards + # and we add to our previous fetches (non-atomically). # This is rather miserable: # When git fetch --prune is passed a fetchspec ending with a *, @@ -2523,30 +2562,33 @@ sub git_fetch_us () { # git fetch to try to generate it. If we don't manage to generate # the target state, we try again. - printdebug "git_fetch_us specs @specs\n"; + my $url = access_giturl(); + + printdebug "git_lrfetch_sane suppl=$supplementary specs @specs\n"; my $specre = join '|', map { my $x = $_; $x =~ s/\W/\\$&/g; - $x =~ s/\\\*$/.*/; + my $wildcard = $x =~ s/\\\*$/.*/; + die if $wildcard && $supplementary; "(?:refs/$x)"; } @specs; - printdebug "git_fetch_us specre=$specre\n"; + printdebug "git_lrfetch_sane specre=$specre\n"; my $wanted_rref = sub { local ($_) = @_; - return m/^(?:$specre)$/o; + return m/^(?:$specre)$/; }; my $fetch_iteration = 0; FETCH_ITERATION: for (;;) { - printdebug "git_fetch_us iteration $fetch_iteration\n"; + printdebug "git_lrfetch_sane iteration $fetch_iteration\n"; if (++$fetch_iteration > 10) { fail "too many iterations trying to get sane fetch!"; } my @look = map { "refs/$_" } @specs; - my @lcmd = (@git, qw(ls-remote -q --refs), access_giturl(), @look); + my @lcmd = (@git, qw(ls-remote -q --refs), $url, @look); debugcmd "|",@lcmd; my %wantr; @@ -2572,13 +2614,14 @@ END "+refs/$_:".lrfetchrefs."/$_"; } @specs; - printdebug "git_fetch_us fspecs @fspecs\n"; + printdebug "git_lrfetch_sane fspecs @fspecs\n"; - my @fcmd = (@git, qw(fetch -p -n -q), access_giturl(), @fspecs); - runcmd_ordryrun_local @git, qw(fetch -p -n -q), access_giturl(), - @fspecs; + my @fcmd = (@git, qw(fetch -p -n -q), $url, @fspecs); + runcmd_ordryrun_local @fcmd if @fspecs; - %lrfetchrefs_f = (); + if (!$supplementary) { + %lrfetchrefs_f = (); + } my %objgot; git_for_each_ref(lrfetchrefs, sub { @@ -2587,6 +2630,10 @@ END $objgot{$objid} = 1; }); + if ($supplementary) { + last; + } + foreach my $lrefname (sort keys %lrfetchrefs_f) { my $rrefname = 'refs'.substr($lrefname, length lrfetchrefs); if (!exists $wantr{$rrefname}) { @@ -2628,8 +2675,35 @@ END } last; } - printdebug "git_fetch_us: git fetch --no-insane emulation complete\n", + + if (defined $csuite) { + printdebug "git_lrfetch_sane: tidying any old suite lrfetchrefs\n"; + git_for_each_ref("refs/dgit-fetch/$csuite", sub { + my ($objid,$objtype,$lrefname,$reftail) = @_; + next if $lrfetchrefs_f{$lrefname}; # $csuite eq $distro ? + runcmd_ordryrun_local @git, qw(update-ref -d), $lrefname; + }); + } + + printdebug "git_lrfetch_sane: git fetch --no-insane emulation complete\n", Dumper(\%lrfetchrefs_f); +} + +sub git_fetch_us () { + # Want to fetch only what we are going to use, unless + # deliberately-not-ff, in which case we must fetch everything. + + my @specs = deliberately_not_fast_forward ? qw(tags/*) : + map { "tags/$_" } + (quiltmode_splitbrain + ? (map { $_->('*',access_nomdistro) } + \&debiantag_new, \&debiantag_maintview) + : debiantags('*',access_nomdistro)); + push @specs, server_branch($csuite); + push @specs, $rewritemap; + push @specs, qw(heads/*) if deliberately_not_fast_forward; + + git_lrfetch_sane 0, @specs; my %here; my @tagpats = debiantags('*',access_nomdistro); @@ -2656,6 +2730,8 @@ END }); } +#---------- dsc and archive handling ---------- + sub mergeinfo_getclogp ($) { # Ensures thit $mi->{Clogp} exists and returns it my ($mi) = @_; @@ -2684,6 +2760,124 @@ sub fetch_from_archive_record_2 ($) { } } +sub parse_dsc_field ($$) { + my ($dsc, $what) = @_; + my $f; + foreach my $field (@ourdscfield) { + $f = $dsc->{$field}; + last if defined $f; + } + if (!defined $f) { + progress "$what: NO git hash"; + } elsif (($dsc_hash, $dsc_distro, $dsc_hint_tag, $dsc_hint_url) + = $f =~ m/^(\w+)\s+($distro_re)\s+($versiontag_re)\s+(\S+)(?:\s|$)/) { + progress "$what: specified git info ($dsc_distro)"; + $dsc_hint_tag = [ $dsc_hint_tag ]; + } elsif ($f =~ m/^\w+\s*$/) { + $dsc_hash = $&; + $dsc_distro //= cfg qw(dgit.default.old-dsc-distro + dgit.default.distro); + $dsc_hint_tag = [ debiantags +(getfield $dsc, 'Version'), + $dsc_distro ]; + progress "$what: specified git hash"; + } else { + fail "$what: invalid Dgit info"; + } +} + +sub resolve_dsc_field_commit ($$) { + my ($already_distro, $already_mapref) = @_; + + return unless defined $dsc_hash; + + my $mapref = + defined $already_mapref && + ($already_distro eq $dsc_distro || !$chase_dsc_distro) + ? $already_mapref : undef; + + my $do_fetch; + $do_fetch = sub { + my ($what, @fetch) = @_; + + local $idistro = $dsc_distro; + my $lrf = lrfetchrefs; + + if (!$chase_dsc_distro) { + progress + "not chasing .dsc distro $dsc_distro: not fetching $what"; + return 0; + } + + progress + ".dsc names distro $dsc_distro: fetching $what"; + + my $url = access_giturl(); + if (!defined $url) { + defined $dsc_hint_url or fail <("rewrite map", $rewritemap) or return; + $mapref = $lrf.'/'.$rewritemap; + my $rewritemapdata = git_cat_file $mapref.':map'; + if (defined $rewritemapdata + && $rewritemapdata =~ m/^$dsc_hash(?:[ \t](\w+))/m) { + progress + "server's git history rewrite map contains a relevant entry!"; + + $dsc_hash = $1; + if (defined $dsc_hash) { + progress "using rewritten git hash in place of .dsc value"; + } else { + progress "server data says .dsc hash is to be disregarded"; + } + } + } + + if (!defined git_cat_file $dsc_hash) { + my @tags = map { "tags/".$_ } @$dsc_hint_tag; + my $lrf = $do_fetch->("additional commits", @tags) && + defined git_cat_file $dsc_hash + or fail <{$field}; - last if defined $dsc_hash; - } - if (defined $dsc_hash) { - $dsc_hash =~ m/\w+/ or fail "invalid hash in .dsc \`$dsc_hash'"; - $dsc_hash = $&; - progress "last upload to archive specified git hash"; - } else { - progress "last upload to archive has NO git hash"; - } + parse_dsc_field($dsc, 'last upload to archive'); + resolve_dsc_field_commit access_basedistro, + lrfetchrefs."/".$rewritemap } else { progress "no version available from the archive"; } @@ -3260,7 +3446,7 @@ sub clone_finish ($) { runcmd qw(bash -ec), <<'END'; set -o pipefail git ls-tree -r --name-only -z HEAD | \ - xargs -0r touch -r . -- + xargs -0r touch -h -r . -- END printdone "ready for work in $dstdir"; } @@ -3524,7 +3710,7 @@ tree $tree parent $dgitview parent $archive_hash author $authline -commiter $authline +committer $authline $msg_msg @@ -3631,8 +3817,12 @@ sub push_parse_changelog ($) { fail "-p specified $package but changelog specified $clogpackage" unless $package eq $clogpackage; my $cversion = getfield $clogp, 'Version'; - my $tag = debiantag($cversion, access_nomdistro); - runcmd @git, qw(check-ref-format), $tag; + + if (!$we_are_initiator) { + # rpush initiator can't do this because it doesn't have $isuite yet + my $tag = debiantag($cversion, access_nomdistro); + runcmd @git, qw(check-ref-format), $tag; + } my $dscfn = dscfn($cversion); @@ -3665,7 +3855,21 @@ sub push_tagwants ($$$$) { TfSuffix => '-maintview', View => 'maint', }; - } + } elsif ($dodep14tag eq 'no' ? 0 + : $dodep14tag eq 'want' ? access_cfg_tagformats_can_splitbrain + : $dodep14tag eq 'always' + ? (access_cfg_tagformats_can_splitbrain or fail < \&debiantag_maintview, + Objid => $dgithead, + TfSuffix => '-dgit', + View => 'dgit', + }; + }; foreach my $tw (@tagwants) { $tw->{Tag} = $tw->{TagFn}($cversion, access_nomdistro); $tw->{Tfn} = sub { $tfbase.$tw->{TfSuffix}.$_[0]; }; @@ -3681,7 +3885,11 @@ sub push_mktags ($$ $$ $) { die unless $tagwants->[0]{View} eq 'dgit'; - $dsc->{$ourdscfield[0]} = $tagwants->[0]{Objid}; + my $declaredistro = access_nomdistro(); + my $reader_giturl = do { local $access_forpush=0; access_giturl(); }; + $dsc->{$ourdscfield[0]} = join " ", + $tagwants->[0]{Objid}, $declaredistro, $tagwants->[0]{Tag}, + $reader_giturl; $dsc->save("$dscfn.tmp") or die $!; my $changes = parsecontrol($changesfile,$changesfilewhat); @@ -3698,7 +3906,6 @@ sub push_mktags ($$ $$ $) { # to control the "tagger" (b) we can do remote signing my $authline = clogp_authline $clogp; my $delibs = join(" ", "",@deliberatelies); - my $declaredistro = access_nomdistro(); my $mktag = sub { my ($tw) = @_; @@ -3800,6 +4007,7 @@ END prep_ud(); access_giturl(); # check that success is vaguely likely + rpush_handle_protovsn_bothends() if $we_are_initiator; select_tagformat(); my $clogpfn = ".git/dgit/changelog.822.tmp"; @@ -3995,8 +4203,12 @@ END runcmd_ordryrun @git, qw(update-ref -m), 'dgit push', lrref(), $dgithead; supplementary_message(<<'END'); -Push failed, after updating the remote git repository. -If you want to try again, you must use a new version number. +Push failed, while obtaining signatures on the .changes and .dsc. +If it was just that the signature failed, you may try again by using +debsign by hand to sign the changes + $changesfile +and then dput to complete the upload. +If you need to change the package, you must use a new version number. END if ($we_are_responder) { my $dryrunsuffix = act_local() ? "" : ".tmp"; @@ -4030,7 +4242,6 @@ END sub cmd_clone { parseopts(); - notpushing(); my $dstdir; badusage "-p is not allowed with clone; specify as argument instead" if defined $package; @@ -4045,8 +4256,9 @@ sub cmd_clone { } else { badusage "incorrect arguments to dgit clone"; } - $dstdir ||= "$package"; + notpushing(); + $dstdir ||= "$package"; if (stat_exists $dstdir) { fail "$dstdir already exists"; } @@ -4085,7 +4297,6 @@ sub branchsuite () { } sub fetchpullargs () { - notpushing(); if (!defined $package) { my $sourcep = parsecontrol('debian/control','debian/control'); $package = getfield $sourcep, 'Source'; @@ -4094,13 +4305,15 @@ sub fetchpullargs () { $isuite = branchsuite(); if (!$isuite) { my $clogp = parsechangelog(); - $isuite = getfield $clogp, 'Distribution'; + my $clogsuite = getfield $clogp, 'Distribution'; + $isuite= $clogsuite if $clogsuite ne 'UNRELEASED'; } } elsif (@ARGV==1) { ($isuite) = @ARGV; } else { badusage "incorrect arguments to dgit fetch or dgit pull"; } + notpushing(); } sub cmd_fetch { @@ -4125,7 +4338,6 @@ END sub cmd_push { parseopts(); - pushing(); badusage "-p is not allowed with dgit push" if defined $package; check_not_dirty(); my $clogp = parsechangelog(); @@ -4138,6 +4350,7 @@ sub cmd_push { badusage "incorrect arguments to dgit push"; } $isuite = getfield $clogp, 'Distribution'; + pushing(); if ($new_package) { local ($package) = $existing_package; # this is a hack canonicalise_suite(); @@ -4168,8 +4381,6 @@ sub cmd_remote_push_build_host { $we_are_responder = 1; $us .= " (build host)"; - pushing(); - open PI, "<&STDIN" or die $!; open STDIN, "/dev/null" or die $!; open PO, ">&STDOUT" or die $!; @@ -4188,7 +4399,6 @@ sub cmd_remote_push_build_host { unless defined $protovsn; responder_send_command("dgit-remote-push-ready $protovsn"); - rpush_handle_protovsn_bothends(); changedir $dir; &cmd_push; } @@ -4250,6 +4460,8 @@ sub cmd_rpush { my @cmd = (@ssh, $host, shellquote @rdgit); debugcmd "+",@cmd; + $we_are_initiator=1; + if (defined $initiator_tempdir) { rmtree $initiator_tempdir; mkdir $initiator_tempdir, 0700 or die "$initiator_tempdir: $!"; @@ -4430,7 +4642,7 @@ END local $ENV{'EDITOR'} = cmdoutput qw(realpath --), $0; local $ENV{'VISUAL'} = $ENV{'EDITOR'}; local $ENV{$fakeeditorenv} = cmdoutput qw(realpath --), $descfn; - runcmd @dpkgsource, qw(--commit .), $patchname; + runcmd @dpkgsource, qw(--commit --include-removal .), $patchname; } } @@ -4465,17 +4677,21 @@ sub quiltify_trees_differ ($$;$$$) { if ($unrepres) { eval { - die "deleted\n" unless $newmode =~ m/[^0]/; - die "not a plain file\n" unless $newmode =~ m/^10\d{4}$/; - if ($oldmode =~ m/[^0]/) { + die "not a plain file\n" + unless $newmode =~ m/^10\d{4}$/ || + $oldmode =~ m/^10\d{4}$/; + if ($oldmode =~ m/[^0]/ && + $newmode =~ m/[^0]/) { die "mode changed\n" if $oldmode ne $newmode; } else { - die "non-default mode\n" unless $newmode =~ m/^100644$/; + die "non-default mode\n" + unless $newmode =~ m/^100644$/ || + $oldmode =~ m/^100644$/; } }; if ($@) { local $/="\n"; chomp $@; - push @$unrepres, [ $f, $@ ]; + push @$unrepres, [ $f, "$@ ($oldmode->$newmode)" ]; } } @@ -4908,13 +5124,10 @@ sub build_maybe_quilt_fixup () { check_for_vendor_patches(); if (quiltmode_splitbrain) { - foreach my $needtf (qw(new maint)) { - next if grep { $_ eq $needtf } access_cfg_tagformats; - fail < 0) { @@ -5682,6 +5897,7 @@ sub cmd_gbp_build { sub cmd_git_build { cmd_gbp_build(); } # compatibility with <= 1.0 sub build_source { + build_prep_early(); my $our_cleanmode = $cleanmode; if ($need_split_build_invocation) { # Pretend that clean is being done some other way. This @@ -5742,6 +5958,7 @@ sub build_source { } sub cmd_build_source { + build_prep_early(); badusage "build-source takes no additional arguments" if @ARGV; build_source(); maybe_unapply_patches_again(); @@ -5770,10 +5987,7 @@ END sub cmd_quilt_fixup { badusage "incorrect arguments to dgit quilt-fixup" if @ARGV; - my $clogp = parsechangelog(); - $version = getfield $clogp, 'Version'; - $package = getfield $clogp, 'Source'; - check_not_dirty(); + build_prep_early(); clean_tree(); build_maybe_quilt_fixup(); } @@ -5841,30 +6055,35 @@ sub cmd_import_dsc { parse_dscdata(); - my $dgit_commit = $dsc->{$ourdscfield[0]}; - if (defined $dgit_commit && - !forceing [qw(import-dsc-with-dgit-field)]) { - $dgit_commit =~ m/\w+/ or fail "invalid hash in .dsc"; + $package = getfield $dsc, 'Source'; + + parse_dsc_field($dsc, "Dgit metadata in .dsc") + unless forceing [qw(import-dsc-with-dgit-field)]; + + if (defined $dsc_hash) { progress "dgit: import-dsc of .dsc with Dgit field, using git hash"; + resolve_dsc_field_commit undef, undef; + } + if (defined $dsc_hash) { my @cmd = (qw(sh -ec), - "echo $dgit_commit | git cat-file --batch-check"); + "echo $dsc_hash | git cat-file --batch-check"); my $objgot = cmdoutput @cmd; if ($objgot =~ m#^\w+ missing\b#) { fail < 0) { progress "Not fast forward, forced update."; } else { - fail "Not fast forward to $dgit_commit"; + fail "Not fast forward to $dsc_hash"; } } @cmd = (@git, qw(update-ref -m), "dgit import-dsc (Dgit): $info", - $dstbranch, $dgit_commit); + $dstbranch, $dsc_hash); runcmd @cmd; progress "dgit: import-dsc updated git ref $dstbranch"; return 0; @@ -5877,7 +6096,6 @@ Specify +$specbranch to overwrite, discarding existing history END if $oldhash && !$force; - $package = getfield $dsc, 'Source'; my @dfi = dsc_files_info(); foreach my $fi (@dfi) { my $f = $fi->{Filename}; @@ -5897,7 +6115,7 @@ END $there .= "/$f"; symlink $there, $here or fail "symlink $there to $here: $!"; progress "made symlink $here -> $there"; - print STDERR Dumper($fi); +# print STDERR Dumper($fi); } my @mergeinputs = generate_commits_from_dsc(); die unless @mergeinputs == 1; @@ -5911,10 +6129,14 @@ END progress "Import, merging."; my $tree = cmdoutput @git, qw(rev-parse), "$newhash:"; my $version = getfield $dsc, 'Version'; + my $clogp = commit_getclogp $newhash; + my $authline = clogp_authline $clogp; $newhash = make_commit_text <",@cmd; exec @cmd or fail "exec git clone: $!\n"; } +sub cmd_print_dgit_repos_server_source_url { + badusage "no arguments allowed to dgit print-dgit-repos-server-source-url" + if @ARGV; + $package = '_dgit-repos-server'; + local $access_forpush = 0; + my $url = access_giturl(); + print $url, "\n" or die $!; +} + sub cmd_setup_mergechangelogs { badusage "no arguments allowed to dgit setup-mergechangelogs" if @ARGV; setup_mergechangelogs(1); } sub cmd_setup_useremail { - badusage "no arguments allowed to dgit setup-mergechangelogs" if @ARGV; + badusage "no arguments allowed to dgit setup-useremail" if @ARGV; setup_useremail(1); } @@ -6079,12 +6311,24 @@ sub parseopts () { } elsif (m/^--no-rm-on-error$/s) { push @ropts, $_; $rmonerror = 0; + } elsif (m/^--no-chase-dsc-distro$/s) { + push @ropts, $_; + $chase_dsc_distro = 0; } elsif (m/^--overwrite$/s) { push @ropts, $_; $overwrite_version = ''; } elsif (m/^--overwrite=(.+)$/s) { push @ropts, $_; $overwrite_version = $1; + } elsif (m/^--dep14tag$/s) { + push @ropts, $_; + $dodep14tag= 'want'; + } elsif (m/^--no-dep14tag$/s) { + push @ropts, $_; + $dodep14tag= 'no'; + } elsif (m/^--always-dep14tag$/s) { + push @ropts, $_; + $dodep14tag= 'always'; } elsif (m/^--delayed=(\d+)$/s) { push @ropts, $_; push @dput, $_; @@ -6115,6 +6359,11 @@ sub parseopts () { # undocumented, for testing push @ropts, $_; $need_split_build_invocation = 1; + } elsif (m/^--config-lookup-explode=(.+)$/s) { + # undocumented, for testing + push @ropts, $_; + $gitcfgs{cmdline}{$1} = 'CONFIG-LOOKUP-EXPLODE'; + # ^ it's supposed to be an array ref } elsif (m/^(--[-0-9a-z]+)(=|$)/ && ($oi = $valopts_long{$1})) { $val = $2 ? $' : undef; #'; $valopt->($oi->{Long}); @@ -6203,7 +6452,11 @@ END } -sub finalise_opts_opts () { +sub parseopts_late_defaults () { + $isuite //= cfg("dgit-distro.$idistro.default-suite", 'RETURN-UNDEF') + if defined $idistro; + $isuite //= cfg('dgit.default.default-suite'); + foreach my $k (keys %opts_opt_map) { my $om = $opts_opt_map{$k}; @@ -6230,6 +6483,40 @@ sub finalise_opts_opts () { @$om[$insertpos..$#$om] ); } } + + if (!defined $rmchanges) { + local $access_forpush; + $rmchanges = access_cfg_bool(0, 'rm-old-changes'); + } + + if (!defined $quilt_mode) { + local $access_forpush; + $quilt_mode = cfg('dgit.force.quilt-mode', 'RETURN-UNDEF') + // access_cfg('quilt-mode', 'RETURN-UNDEF') + // 'linear'; + $quilt_mode =~ m/^($quilt_modes_re)$/ + or badcfg "unknown quilt-mode \`$quilt_mode'"; + $quilt_mode = $1; + } + + if (!defined $dodep14tag) { + local $access_forpush; + $dodep14tag = access_cfg('dep14tag', 'RETURN-UNDEF') // 'want'; + $dodep14tag =~ m/^($dodep14tag_re)$/ + or badcfg "unknown dep14tag setting \`$dodep14tag'"; + $dodep14tag = $1; + } + + $need_split_build_invocation ||= quiltmode_splitbrain(); + + if (!defined $cleanmode) { + local $access_forpush; + $cleanmode = access_cfg('clean-mode', 'RETURN-UNDEF'); + $cleanmode //= 'dpkg-source'; + + badcfg "unknown clean-mode \`$cleanmode'" unless + $cleanmode =~ m/^($cleanmode_re)$(?!\n)/s; + } } if ($ENV{$fakeeditorenv}) { @@ -6254,32 +6541,6 @@ $cmd =~ y/-/_/; my $pre_fn = ${*::}{"pre_$cmd"}; $pre_fn->() if $pre_fn; -if (!defined $rmchanges) { - local $access_forpush; - $rmchanges = access_cfg_bool(0, 'rm-old-changes'); -} - -if (!defined $quilt_mode) { - local $access_forpush; - $quilt_mode = cfg('dgit.force.quilt-mode', 'RETURN-UNDEF') - // access_cfg('quilt-mode', 'RETURN-UNDEF') - // 'linear'; - $quilt_mode =~ m/^($quilt_modes_re)$/ - or badcfg "unknown quilt-mode \`$quilt_mode'"; - $quilt_mode = $1; -} - -$need_split_build_invocation ||= quiltmode_splitbrain(); - -if (!defined $cleanmode) { - local $access_forpush; - $cleanmode = access_cfg('clean-mode', 'RETURN-UNDEF'); - $cleanmode //= 'dpkg-source'; - - badcfg "unknown clean-mode \`$cleanmode'" unless - $cleanmode =~ m/^($cleanmode_re)$(?!\n)/s; -} - my $fn = ${*::}{"cmd_$cmd"}; $fn or badusage "unknown operation $cmd"; $fn->();