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 $initiator_tempdir;
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);
scalar @{ $opts_opt_map{$_} }
} keys %opts_opt_map;
-sub finalise_opts_opts();
+sub parseopts_late_defaults();
our $keyid;
sub debiantag_maintview ($$) {
my ($v,$distro) = @_;
- $v =~ y/~:/_%/;
- return "$distro/$v";
+ return "$distro/".dep14_version_mangle $v;
}
sub madformat ($) { $_[0] eq '3.0 (quilt)' }
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 () {
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 ($) {
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".
}
our ($dsc_hash,$lastpush_mergeinput);
+our ($dsc_distro, $dsc_hint_tag, $dsc_hint_url);
our $ud = '.git/dgit/unpack';
return $tree;
}
-sub remove_stray_gits () {
+sub git_add_write_tree () {
+ runcmd @git, qw(add -Af .);
+ return git_write_tree();
+}
+
+sub remove_stray_gits ($) {
+ my ($what) = @_;
my @gitscmd = qw(find -name .git -prune -print0);
debugcmd "|",@gitscmd;
open GITS, "-|", @gitscmd or die $!;
local $/="\0";
while (<GITS>) {
chomp or die;
- print STDERR "$us: warning: removing from source package: ",
+ print STDERR "$us: warning: removing from $what: ",
(messagequote $_), "\n";
rmtree $_;
}
$!=0; $?=0; close GITS or failedcmd @gitscmd;
}
-sub mktree_in_ud_from_only_subdir (;$) {
- my ($raw) = @_;
+sub mktree_in_ud_from_only_subdir ($;$) {
+ my ($what,$raw) = @_;
# changes into the subdir
my (@dirs) = <*/.>;
my $dir = $1;
changedir $dir;
- remove_stray_gits();
+ remove_stray_gits($what);
mktree_in_ud_here();
if (!$raw) {
my ($format, $fopts) = get_source_format();
}
}
- runcmd @git, qw(add -Af);
- my $tree=git_write_tree();
+ my $tree=git_add_write_tree();
return ($tree,$dir);
}
$input = $compr_fh;
}
- rmtree "../unpack-tar";
- mkdir "../unpack-tar" or die $!;
+ rmtree "_unpack-tar";
+ mkdir "_unpack-tar" or die $!;
my @tarcmd = qw(tar -x -f -
--no-same-owner --no-same-permissions
--no-acls --no-xattrs --no-selinux);
my $tar_pid = fork // die $!;
if (!$tar_pid) {
- chdir "../unpack-tar" or die $!;
+ chdir "_unpack-tar" or die $!;
open STDIN, "<&", $input or die $!;
exec @tarcmd;
die "dgit (child): exec $tarcmd[0]: $!";
# finally, we have the results in "tarball", but maybe
# with the wrong permissions
- runcmd qw(chmod -R +rwX ../unpack-tar);
- changedir "../unpack-tar";
- my ($tree) = mktree_in_ud_from_only_subdir(1);
- changedir "../../unpack";
- rmtree "../unpack-tar";
+ runcmd qw(chmod -R +rwX _unpack-tar);
+ changedir "_unpack-tar";
+ remove_stray_gits($f);
+ mktree_in_ud_here();
+
+ my ($tree) = git_add_write_tree();
+ my $tentries = cmdoutput @git, qw(ls-tree -z), $tree;
+ if ($tentries =~ m/^\d+ tree (\w+)\t[^\000]+\000$/s) {
+ $tree = $1;
+ printdebug "one subtree $1\n";
+ } else {
+ printdebug "multiple subtrees\n";
+ }
+ changedir "..";
+ rmtree "_unpack-tar";
my $ent = [ $f, $tree ];
push @tartrees, {
push @cmd, qw(-x --), $dscfn;
runcmd @cmd;
- my ($tree,$dir) = mktree_in_ud_from_only_subdir();
+ my ($tree,$dir) = mktree_in_ud_from_only_subdir("source package");
if (madformat $dsc->{format}) {
check_for_vendor_patches();
}
my @pcmd = qw(dpkg-source --before-build .);
runcmd shell_cmd 'exec >/dev/null', @pcmd;
rmtree '.pc';
- runcmd @git, qw(add -Af);
- $dappliedtree = git_write_tree();
+ $dappliedtree = git_add_write_tree();
}
my @clogcmd = qw(dpkg-parsechangelog --format rfc822 --all);
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 $@;
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).
\&debiantag_new, \&debiantag_maintview)
: debiantags('*',access_nomdistro));
push @specs, server_branch($csuite);
+ push @specs, $rewritemap;
push @specs, qw(heads/*) if deliberately_not_fast_forward;
# This is rather miserable:
}
}
+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+) ($distro_re) ($versiontag_re) (\S+)(?:\s|$)/) {
+ progress "$what: specified git info ($dsc_distro)";
+ $dsc_hint_tag = [ $dsc_hint_tag ];
+ } elsif ($f =~ m/^\w+\s*$/) {
+ $dsc_hash = $&;
+ $dsc_distro //= 'debian';
+ $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 $rewritemapdata = git_cat_file $already_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";
+ }
+ }
+}
+
sub fetch_from_archive () {
ensure_setup_existing_tree();
get_archive_dsc();
if ($dsc) {
- foreach my $field (@ourdscfield) {
- $dsc_hash = $dsc->{$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";
}
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";
}
parent $dgitview
parent $archive_hash
author $authline
-commiter $authline
+committer $authline
$msg_msg
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 <<END)
+--dep14tag-always (or equivalent in config) means server must support
+ both "new" and "maint" tag formats, but config says it doesn't.
+END
+ : die "$dodep14tag ?") {
+ push @tagwants, {
+ TagFn => \&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]; };
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);
# 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) = @_;
my $dscpath = "$buildproductsdir/$dscfn";
stat_exists $dscpath or
- fail "looked for .dsc $dscfn, but $!;".
+ fail "looked for .dsc $dscpath, but $!;".
" maybe you forgot to build";
responder_send_file('dsc', $dscpath);
progress "checking that $dscfn corresponds to HEAD";
runcmd qw(dpkg-source -x --),
$dscpath =~ m#^/# ? $dscpath : "../../../$dscpath";
- my ($tree,$dir) = mktree_in_ud_from_only_subdir();
+ my ($tree,$dir) = mktree_in_ud_from_only_subdir("source package");
check_for_vendor_patches() if madformat($dsc->{format});
changedir '../../../..';
my @diffcmd = (@git, qw(diff --quiet), $tree, $dgithead);
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";
sub cmd_clone {
parseopts();
- notpushing();
my $dstdir;
badusage "-p is not allowed with clone; specify as argument instead"
if defined $package;
} else {
badusage "incorrect arguments to dgit clone";
}
- $dstdir ||= "$package";
+ notpushing();
+ $dstdir ||= "$package";
if (stat_exists $dstdir) {
fail "$dstdir already exists";
}
}
sub fetchpullargs () {
- notpushing();
if (!defined $package) {
my $sourcep = parsecontrol('debian/control','debian/control');
$package = getfield $sourcep, 'Source';
} else {
badusage "incorrect arguments to dgit fetch or dgit pull";
}
+ notpushing();
}
sub cmd_fetch {
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;
}
}
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)" ];
}
}
check_for_vendor_patches();
if (quiltmode_splitbrain) {
- foreach my $needtf (qw(new maint)) {
- next if grep { $_ eq $needtf } access_cfg_tagformats;
- fail <<END
+ fail <<END unless access_cfg_tagformats_can_splitbrain;
quilt mode $quilt_mode requires split view so server needs to support
both "new" and "maint" tag formats, but config says it doesn't.
END
- }
}
my $clogp = parsechangelog();
changedir 'fake';
- remove_stray_gits();
+ remove_stray_gits("source package");
mktree_in_ud_here();
rmtree '.pc';
- runcmd @git, qw(add -Af .);
- my $unapplied=git_write_tree();
+ my $unapplied=git_add_write_tree();
printdebug "fake orig tree object $unapplied\n";
ensuredir '.pc';
changedir '../fake';
rmtree '.pc';
- runcmd @git, qw(add -Af .);
- my $oldtiptree=git_write_tree();
+ my $oldtiptree=git_add_write_tree();
printdebug "fake o+d/p tree object $unapplied\n";
changedir '../work';
}
sub cmd_build {
+ build_prep_early();
my @dbp = (@dpkgbuildpackage, qw(-us -uc), changesopts_initial(), @ARGV);
my $wantsrc = massage_dbp_args \@dbp;
if ($wantsrc > 0) {
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
}
sub cmd_build_source {
+ build_prep_early();
badusage "build-source takes no additional arguments" if @ARGV;
build_source();
maybe_unapply_patches_again();
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();
}
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";
+ parse_dsc_field($dsc, "Dgit metadata in .dsc");
+
+ if (defined $dsc_hash
+ && !forceing [qw(import-dsc-with-dgit-field)]) {
progress "dgit: import-dsc of .dsc with Dgit field, using git 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 <<END
-.dsc contains Dgit field referring to object $dgit_commit
+.dsc contains Dgit field referring to object $dsc_hash
Your git tree does not have that object. Try `git fetch' from a
plausible server (browse.dgit.d.o? alioth?), and try the import-dsc again.
END
}
- if ($oldhash && !is_fast_fwd $oldhash, $dgit_commit) {
+ if ($oldhash && !is_fast_fwd $oldhash, $dsc_hash) {
if ($force > 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;
$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;
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 <<END;
tree $tree
parent $newhash
parent $oldhash
+author $authline
+committer $authline
Merge $package ($version) import into $dstbranch
END
} 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, $_;
}
-sub finalise_opts_opts () {
+sub parseopts_late_defaults () {
foreach my $k (keys %opts_opt_map) {
my $om = $opts_opt_map{$k};
@$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}) {
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->();