) {
chomp or die;
- debug " upd.| $_";
+ printdebug " upd.| $_\n";
m/^(\S+) (\S+) (\S+)$/ or die "$_ ?";
my ($old, $sha1, $refname) = ($1, $2, $3);
- if ($refname =~ m{^refs/tags/(?=debian/)}) {
- reject "pushing multiple tags!" if defined $tagname;
- $tagname = $'; #';
- $tagval = $sha1;
- reject "tag $tagname already exists -".
+ if ($refname =~ m{^refs/tags/(?=(?:archive/)?$distro/)}) {
+ my $tn = $'; #';
+ $tags{$tn} = $sha1;
+ $tagexists_error= "tag $tn already exists -".
" not replacing previously-pushed version"
if $old =~ m/[^0]/;
} elsif ($refname =~ m{^refs/dgit/}) {
@@ -431,13 +545,25 @@ sub readupdates () {
}
STDIN->error and die $!;
- reject "push is missing tag ref update" unless defined $tagname;
+ reject "push is missing tag ref update" unless %tags;
+ my @newtags = grep { m#^archive/# } keys %tags;
+ my @omtags = grep { !m#^archive/# } keys %tags;
+ reject "pushing too many similar tags" if @newtags>1 || @omtags>1;
+ if (@newtags) {
+ ($tagname) = @newtags;
+ ($maint_tagname) = @omtags;
+ } else {
+ ($tagname) = @omtags or die;
+ }
+ $tagval = $tags{$tagname};
+ $maint_tagval = $tags{$maint_tagname // ''};
+
reject "push is missing head ref update" unless defined $suite;
- debug " updates ok.";
+ printdebug " updates ok.\n";
}
sub parsetag () {
- debug " parsetag...";
+ printdebug " parsetag...\n";
open PT, ">dgit-tmp/plaintext" or die $!;
open DS, ">dgit-tmp/plaintext.asc" or die $!;
open T, "-|", qw(git cat-file tag), $tagval or die $!;
@@ -470,11 +596,11 @@ sub parsetag () {
while (length) {
if (s/^distro\=(\S+) //) {
die "$1 != $distro" unless $1 eq $distro;
- } elsif (s/^(--deliberately-$package_re) //) {
+ } elsif (s/^(--deliberately-$deliberately_re) //) {
push @deliberatelies, $1;
- } elsif (s/^supersede:(\S+)=(\w+) //) {
- die "supersede $1 twice" if defined $supersedes{$1};
- $supersedes{$1} = $2;
+ } elsif (s/^previously:(\S+)=(\w+) //) {
+ die "previously $1 twice" if defined $previously{$1};
+ $previously{$1} = $2;
} elsif (s/^[-+.=0-9a-z]\S* //) {
} else {
die "unknown dgit info in tag ($_)";
@@ -493,7 +619,7 @@ sub parsetag () {
T->error and die $!;
close PT or die $!;
close DS or die $!;
- debug " parsetag ok.";
+ printdebug " parsetag ok.\n";
}
sub checksig_keyring ($) {
@@ -504,12 +630,12 @@ sub checksig_keyring ($) {
my $ok = undef;
- debug " checksig keyring $keyringfile...";
+ printdebug " checksig keyring $keyringfile...\n";
our @cmd = (qw(gpgv --status-fd=1 --keyring),
$keyringfile,
qw(dgit-tmp/plaintext.asc dgit-tmp/plaintext));
- debugcmd @cmd;
+ debugcmd '|',@cmd;
open P, "-|", @cmd
or die $!;
@@ -517,7 +643,7 @@ sub checksig_keyring ($) {
while () {
next unless s/^\[GNUPG:\] //;
chomp or die;
- debug " checksig| $_";
+ printdebug " checksig| $_\n";
my @l = split / /, $_;
if ($l[0] eq 'NO_PUBKEY') {
last;
@@ -531,17 +657,17 @@ sub checksig_keyring ($) {
}
close P;
- debug sprintf " checksig ok=%d", !!$ok;
+ printdebug sprintf " checksig ok=%d\n", !!$ok;
return $ok;
}
sub dm_txt_check ($$) {
my ($keyid, $dmtxtfn) = @_;
- debug " dm_txt_check $keyid $dmtxtfn";
+ printdebug " dm_txt_check $keyid $dmtxtfn\n";
open DT, '<', $dmtxtfn or die "$dmtxtfn $!";
while (
) {
- m/^fingerprint:\s+$keyid$/oi
+ m/^fingerprint:\s+\Q$keyid\E$/oi
..0 or next;
if (s/^allow:/ /i..0) {
} else {
@@ -556,11 +682,11 @@ sub dm_txt_check ($$) {
s/\([^()]+\)//;
s/\,//;
chomp or die;
- debug " dm_txt_check allow| $_";
+ printdebug " dm_txt_check allow| $_\n";
foreach my $p (split /\s+/) {
if ($p eq $package) {
# yay!
- debug " dm_txt_check ok";
+ printdebug " dm_txt_check ok\n";
return;
}
}
@@ -572,16 +698,16 @@ sub dm_txt_check ($$) {
sub verifytag () {
foreach my $kas (split /:/, $keyrings) {
- debug "verifytag $kas...";
+ printdebug "verifytag $kas...\n";
$kas =~ s/^([^,]+),// or die;
my $keyid = checksig_keyring $1;
if (defined $keyid) {
if ($kas =~ m/^a$/) {
- debug "verifytag a ok";
+ printdebug "verifytag a ok\n";
return; # yay
} elsif ($kas =~ m/^m([^,]+)$/) {
dm_txt_check($keyid, $1);
- debug "verifytag m ok";
+ printdebug "verifytag m ok\n";
return;
} else {
die;
@@ -591,78 +717,128 @@ sub verifytag () {
reject "key not found in keyrings";
}
-sub checksuite () {
- debug "checksuite ($suitesfile)";
- open SUITES, "<", $suitesfile or die $!;
+sub suite_is_in ($) {
+ my ($sf) = @_;
+ printdebug "suite_is_in ($sf)\n";
+ if (!open SUITES, "<", $sf) {
+ $!==ENOENT or die $!;
+ return 0;
+ }
while () {
chomp;
next unless m/\S/;
next if m/^\#/;
s/\s+$//;
- return if $_ eq $suite;
+ return 1 if $_ eq $suite;
}
die $! if SUITES->error;
+ return 0;
+}
+
+sub checksuite () {
+ printdebug "checksuite ($suitesfile)\n";
+ return if suite_is_in $suitesfile;
reject "unknown suite";
}
sub checktagnoreplay () {
- # We check that the signed tag mentions the name and value of
- # (a) in the case of FRESHREPO all tags in the repo;
- # (b) in the case of just NOFFCHECK all tags referring to
- # the current head for the suite (there must be at least one).
- # This prevents a replay attack using an earlier signed tag.
+ # We need to prevent a replay attack using an earlier signed tag.
+ # We also want to archive in the history the object ids of
+ # anything we remove, even if we get rid of the actual objects.
+ #
+ # So, we check that the signed tag mentions the name and tag
+ # object id of:
+ #
+ # (a) In the case of FRESHREPO: all tags and refs/heads/* in
+ # the repo. That is, effectively, all the things we are
+ # deleting.
+ #
+ # This prevents any tag implying a FRESHREPO push
+ # being replayed into a different state of the repo.
+ #
+ # There is still the folowing risk: If a non-ff push is of a
+ # head which is an ancestor of a previous ff-only push, the
+ # previous push can be replayed.
+ #
+ # So we keep a separate list, as a file in the repo, of all
+ # the tag object ids we have ever seen and removed. Any such
+ # tag object id will be rejected even for ff-only pushes.
+ #
+ # (b) In the case of just NOFFCHECK: all tags referring to the
+ # current head for the suite (there must be at least one).
+ #
+ # This prevents any tag implying a NOFFCHECK push being
+ # replayed to rewind from a different head.
+ #
+ # The possibility of an earlier ff-only push being replayed is
+ # eliminated as follows: the tag from such a push would still
+ # be in our repo, and therefore the replayed push would be
+ # rejected because the set of refs being updated would be
+ # wrong.
+
+ if (!open PREVIOUS, "<", removedtagsfile) {
+ die removedtagsfile." $!" unless $!==ENOENT;
+ } else {
+ # Protocol for updating this file is to append to it, not
+ # write-new-and-rename. So all updates are prefixed with \n
+ # and suffixed with " .\n" so that partial writes can be
+ # ignored.
+ while () {
+ next unless m/^(\w+) (.*) \.\n/;
+ next unless $1 eq $tagval;
+ reject "Replay of previously-rewound upload ($tagval $2)";
+ }
+ die removedtagsfile." $!" if PREVIOUS->error;
+ close PREVIOUS;
+ }
+
return unless $policy & (FRESHREPO|NOFFCHECK);
my $garbagerepo = "$dgitrepos/${package}_garbage";
lockrealtree();
- local $ENV{GIT_DIR};
- foreach my $garb ("$garbagerepo", "$garbagerepo-old") {
- if (stat_exists $garb) {
- $ENV{GIT_DIR} = $garb;
- last;
- }
- }
- if (!defined $ENV{GIT_DIR}) {
- # Nothing to overwrite so the FRESHREPO and NOFFCHECK were
- # pointless. Oh well.
- debug "checktagnoreplay - no garbage, ok";
- return;
- }
+ my $nchecked = 0;
+ my @problems;
- my $onlyreferring;
- if (!($policy & FRESHREPO)) {
- my $branch = server_branch($suite);
- $!=0; $?=0; $_ =
- `git for-each-ref --format='%(objectname)' '[r]efs/$branch'`;
- defined or die "$branch $? $!";
- $? and die "$branch $?";
- if (!length) {
- # No such branch - NOFFCHECK was unnecessary. Oh well.
- debug "checktagnoreplay - not FRESHREPO, new branch, ok";
- return;
+ my $check_ref_previously= sub {
+ my ($objid,$objtype,$fullrefname,$reftail) = @_;
+ my $supkey = $fullrefname;
+ $supkey =~ s{^refs/}{} or die "$supkey $objid ?";
+ my $supobjid = $previously{$supkey};
+ if (!defined $supobjid) {
+ printdebug "checktagnoreply - missing\n";
+ push @problems, "does not declare previously $supkey";
+ } elsif ($supobjid ne $objid) {
+ push @problems, "declared previously $supkey=$supobjid".
+ " but actually previously $supkey=$objid";
+ } else {
+ $nchecked++;
}
- m/^(\w+)\n$/ or die "$branch $_ ?";
- $onlyreferring = $1;
- debug "checktagnoreplay - not FRESHREPO,".
- " checking for overwriting refs/$branch=$onlyreferring";
- }
-
- my @problems;
+ };
- git_for_each_tag_referring($onlyreferring, sub {
- my ($objid,$fullrefname,$tagname) = @_;
- debug "checktagnoreplay - overwriting $fullrefname=$objid";
- my $supers = $supersedes{$fullrefname};
- if (!defined $supers) {
- push @problems, "does not supersede $fullrefname";
- } elsif ($supers ne $objid) {
- push @problems,
- "supersedes $fullrefname=$supers but previously $fullrefname=$objid";
+ if ($policy & FRESHREPO) {
+ foreach my $kind (qw(tags heads)) {
+ git_for_each_ref("refs/$kind", $check_ref_previously);
+ }
+ } else {
+ my $branch= server_branch($suite);
+ my $branchhead= git_get_ref(server_ref($suite));
+ if (!length $branchhead) {
+ # No such branch - NOFFCHECK was unnecessary. Oh well.
+ printdebug "checktagnoreplay - not FRESHREPO, new branch, ok\n";
} else {
- # ok;
+ printdebug "checktagnoreplay - not FRESHREPO,".
+ " checking for overwriting refs/$branch=$branchhead\n";
+ git_for_each_tag_referring($branchhead, sub {
+ my ($tagobjid,$refobjid,$fullrefname,$tagname) = @_;
+ $check_ref_previously->($tagobjid,undef,$fullrefname,undef);
+ });
+ printdebug "checktagnoreplay - not FRESHREPO, nchecked=$nchecked";
+ push @problems, "does not declare previously any tag".
+ " referring to branch head $branch=$branchhead"
+ unless $nchecked;
}
- });
+ }
if (@problems) {
reject "replay attack prevention check failed:".
@@ -670,7 +846,7 @@ sub checktagnoreplay () {
join("; ", @problems).
"\n";
}
- debug "checktagnoreply - all ok"
+ printdebug "checktagnoreplay - all ok ($tagval)\n"
}
sub tagh1 ($) {
@@ -682,76 +858,155 @@ sub tagh1 ($) {
}
sub checks () {
- debug "checks";
+ printdebug "checks\n";
tagh1('type') eq 'commit' or reject "tag refers to wrong kind of object";
tagh1('object') eq $commit or reject "tag refers to wrong commit";
tagh1('tag') eq $tagname or reject "tag name in tag is wrong";
- my $v = $version;
- $v =~ y/~:/_%/;
+ my @expecttagnames = debiantags($version, $distro);
+ printdebug "expected tag @expecttagnames\n";
+ grep { $tagname eq $_ } @expecttagnames or die;
- debug "translated version $v";
- $tagname eq "debian/$v" or die;
+ foreach my $othertag (grep { $_ ne $tagname } @expecttagnames) {
+ reject "tag $othertag (pushed with differing dgit version)".
+ " already exists -".
+ " not replacing previously-pushed version"
+ if git_get_ref "refs/tags/".$othertag;
+ }
lockrealtree();
- my @policy_args = ($package,$version,$suite,$tagname,
- join(",",@deliberatelies));
- $policy = policyhook(NOFFCHECK|FRESHREPO, 'push', @policy_args);
+ @policy_args = ($package,$version,$suite,$tagname,
+ join(",",@deliberatelies));
+ $policy = policyhook(NOFFCHECK|FRESHREPO|NOCOMMITCHECK, 'push', @policy_args);
+
+ if (defined $tagexists_error) {
+ if ($policy & FRESHREPO) {
+ printdebug "ignoring tagexists_error: $tagexists_error\n";
+ } else {
+ reject $tagexists_error;
+ }
+ }
checktagnoreplay();
checksuite();
# check that our ref is being fast-forwarded
- debug "oldcommit $oldcommit";
+ printdebug "oldcommit $oldcommit\n";
if (!($policy & NOFFCHECK) && $oldcommit =~ m/[^0]/) {
$?=0; $!=0; my $mb = `git merge-base $commit $oldcommit`;
chomp $mb;
$mb eq $oldcommit or reject "not fast forward on dgit branch";
}
+ # defend against commits generated by #849041
+ if (!($policy & NOCOMMITCHECK)) {
+ my @checks = qw(%ae %at
+ %ce %ct);
+ my @chk = qw(git log -z);
+ push @chk, '--pretty=tformat:%H%n'.
+ (join "", map { $_, '%n' } @checks);
+ push @chk, "^$oldcommit" if $oldcommit =~ m/[^0]/;
+ push @chk, $commit;;
+ printdebug " ~NOCOMMITCHECK @chk\n";
+ open CHK, "-|", @chk or die $!;
+ local $/ = "\0";
+ while () {
+ next unless m/^$/m;
+ m/^\w+(?=\n)/ or die;
+ reject "corrupted object $& (missing metadata)";
+ }
+ $!=0; $?=0; close CHK or $?==256 or die "$? $!";
+ }
+
if ($policy & FRESHREPO) {
- # This is troublesome. We have been asked by the policy hook
- # to receive the push into a fresh repo. But of course we
- # have actually already mostly received the push into the working
- # repo. (This is unavoidable because the instruction to use a new
- # repo comes ultimately from the signed tag for the dgit push,
- # which has to have been received into some repo.)
+ # It's a bit late to be discovering this here, isn't it ?
+ #
+ # What we do is: Generate a fresh destination repo right now,
+ # and arrange to treat it from now on as if it were a
+ # prospective repo.
+ #
+ # The presence of this fresh destination repo is detected by
+ # the parent, which responds by making a fresh master repo
+ # from the template. (If the repo didn't already exist then
+ # $destrepo was _prospective, and we change it here. This is
+ # OK because the parent's check for _fresh persuades it not to
+ # use _prospective.)
#
- # So what we do is generate a fresh working repo right now and
- # push the head and tag into it. The presence of this fresh
- # working repo is detected by the parent, which responds by
- # making a fresh master repo from the template.
-
$destrepo = "${workrepo}_fresh"; # workrepo lock covers
mkrepo_fromtemplate $destrepo;
}
-
- policyhook(0, 'push-confirm', @policy_args);
}
sub onwardpush () {
- my @cmd = (qw(git send-pack), $destrepo);
- push @cmd, qw(--force) if $policy & NOFFCHECK;
+ my @cmdbase = (qw(git send-pack), $destrepo);
+ push @cmdbase, qw(--force) if $policy & NOFFCHECK;
+
+ if ($ENV{GIT_QUARANTINE_PATH}) {
+ my $recv_wrapper = "$ENV{GIT_QUARANTINE_PATH}/dgit-recv-wrapper";
+ mkscript $recv_wrapper, <<'END';
+#!/bin/sh
+set -e
+unset GIT_QUARANTINE_PATH
+exec git receive-pack "$@"
+END
+ push @cmdbase, "--receive-pack=$recv_wrapper";
+ }
+
+ my @cmd = @cmdbase;
push @cmd, "$commit:refs/dgit/$suite",
"$tagval:refs/tags/$tagname";
- debugcmd @cmd;
+ push @cmd, "$maint_tagval:refs/tags/$maint_tagname"
+ if defined $maint_tagname;
+ debugcmd '+',@cmd;
$!=0;
my $r = system @cmd;
!$r or die "onward push to $destrepo failed: $r $!";
+
+ if (suite_is_in $suitesformasterfile) {
+ @cmd = @cmdbase;
+ push @cmd, "$commit:refs/heads/master";
+ debugcmd '+', @cmd;
+ $!=0; my $r = system @cmd;
+ # tolerate errors (might be not ff)
+ !($r & ~0xff00) or die
+ "onward push to $destrepo#master failed: $r $!";
+ }
+}
+
+sub finalisepush () {
+ if ($destrepo eq realdestrepo) {
+ policyhook(0, 'push-confirm', @policy_args, '');
+ onwardpush();
+ } else {
+ # We are to receive the push into a new repo (perhaps
+ # because the policy push hook asked us to with FRESHREPO, or
+ # perhaps because the repo didn't exist before).
+ #
+ # We want to provide the policy push-confirm hook with a repo
+ # which looks like the one which is going to be installed.
+ # The working repo is no good because it might contain
+ # previous history.
+ #
+ # So we push the objects into the prospective new repo right
+ # away. If the hook declines, we decline, and the prospective
+ # repo is never installed.
+ onwardpush();
+ policyhook(0, 'push-confirm', @policy_args, $destrepo);
+ }
}
sub stunthook () {
- debug "stunthook";
+ printdebug "stunthook in $workrepo\n";
chdir $workrepo or die "chdir $workrepo: $!";
mkdir "dgit-tmp" or $!==EEXIST or die $!;
readupdates();
parsetag();
verifytag();
checks();
- onwardpush();
- debug "stunthook done.";
+ finalisepush();
+ printdebug "stunthook done.\n";
}
#----- git-upload-pack -----
@@ -769,6 +1024,7 @@ sub fixmissing__git_upload_pack () {
sub main__git_upload_pack () {
my $lfh = locksometree($destrepo);
+ printdebug "git-upload-pack in $destrepo\n";
chdir $destrepo or die "$destrepo: $!";
close $lfh;
runcmd qw(git upload-pack), ".";
@@ -783,6 +1039,79 @@ sub argval () {
return $v;
}
+our %indistrodir = (
+ # keys are used for DGIT_DRS_XXX too
+ 'repos' => \$dgitrepos,
+ 'suites' => \$suitesfile,
+ 'suites-master' => \$suitesformasterfile,
+ 'policy-hook' => \$policyhook,
+ 'mirror-hook' => \$mirrorhook,
+ 'dgit-live' => \$dgitlive,
+ );
+
+our @hookenvs = qw(distro suitesfile suitesformasterfile policyhook
+ mirrorhook dgitlive keyrings dgitrepos distrodir);
+
+# workrepo and destrepo handled ad-hoc
+
+sub mode_ssh () {
+ die if @ARGV;
+
+ my $cmd = $ENV{'SSH_ORIGINAL_COMMAND'};
+ $cmd =~ m{
+ ^
+ (?: \S* / )?
+ ( [-0-9a-z]+ )
+ \s+
+ '? (?: \S* / )?
+ ($package_re) \.git
+ '?$
+ }ox
+ or reject "command string not understood";
+ my $method = $1;
+ $package = $2;
+
+ my $funcn = $method;
+ $funcn =~ y/-/_/;
+ my $mainfunc = $main::{"main__$funcn"};
+
+ reject "unknown method" unless $mainfunc;
+
+ policy_checkpackage();
+
+ if (stat_exists realdestrepo) {
+ $destrepo = realdestrepo;
+ } else {
+ printdebug " fixmissing $funcn\n";
+ my $fixfunc = $main::{"fixmissing__$funcn"};
+ &$fixfunc;
+ }
+
+ printdebug " running main $funcn\n";
+ &$mainfunc;
+}
+
+sub mode_cron () {
+ die if @ARGV;
+
+ my $listfh = tempfile();
+ open STDOUT, ">&", $listfh or die $!;
+ policyhook(0,'check-list');
+ open STDOUT, ">&STDERR" or die $!;
+
+ seek $listfh, 0, 0 or die $!;
+ while (<$listfh>) {
+ chomp or die;
+ next if m/^\s*\#/;
+ next unless m/\S/;
+ die unless m/^($package_re)$/;
+
+ $package = $1;
+ policy_checkpackage();
+ }
+ die $! if $listfh->error;
+}
+
sub parseargsdispatch () {
die unless @ARGV;
@@ -790,22 +1119,23 @@ sub parseargsdispatch () {
delete $ENV{'GIT_PREFIX'}; # sets these and they mess things up
if ($ENV{'DGIT_DRS_DEBUG'}) {
- $debug='=';
- open DEBUG, ">&STDERR" or die $!;
+ enabledebug();
}
if ($ARGV[0] eq '--pre-receive-hook') {
- if ($debug) { $debug.="="; }
+ if ($debuglevel) {
+ $debugprefix.="=";
+ printdebug "in stunthook ".(shellquote @ARGV)."\n";
+ foreach my $k (sort keys %ENV) {
+ printdebug "$k=$ENV{$k}\n" if $k =~ m/^DGIT/;
+ }
+ }
shift @ARGV;
@ARGV == 1 or die;
$package = shift @ARGV;
- defined($distro = $ENV{'DGIT_DRS_DISTRO'}) or die;
- defined($dgitrepos = $ENV{'DGIT_DRS_REPOS'}) or die;
- defined($suitesfile = $ENV{'DGIT_DRS_SUITES'}) or die;
+ ${ $main::{$_} } = $ENV{"DGIT_DRS_\U$_"} foreach @hookenvs;
defined($workrepo = $ENV{'DGIT_DRS_WORK'}) or die;
defined($destrepo = $ENV{'DGIT_DRS_DEST'}) or die;
- defined($keyrings = $ENV{'DGIT_DRS_KEYRINGS'}) or die $!;
- defined($policyhook = $ENV{'DGIT_DRS_POLICYHOOK'}) or die $!;
open STDOUT, ">&STDERR" or die $!;
eval {
stunthook();
@@ -817,54 +1147,28 @@ sub parseargsdispatch () {
exit 0;
}
- $ENV{'DGIT_DRS_DISTRO'} = $distro = argval();
- $ENV{'DGIT_DRS_SUITES'} = $suitesfile = argval();
- $ENV{'DGIT_DRS_KEYRINGS'} = $keyrings = argval();
- $ENV{'DGIT_DRS_REPOS'} = $dgitrepos = argval();
- $ENV{'DGIT_DRS_POLICYHOOK'} = $policyhook = argval();
-
- die unless @ARGV==1 && $ARGV[0] eq '--ssh';
+ $distro = argval();
+ $distrodir = argval();
+ $keyrings = argval();
- my $cmd = $ENV{'SSH_ORIGINAL_COMMAND'};
- $cmd =~ m{
- ^
- (?: \S* / )?
- ( [-0-9a-z]+ )
- \s+
- '? (?: \S* / )?
- ($package_re) \.git
- '?$
- }ox
- or reject "command string not understood";
- my $method = $1;
- $package = $2;
- $realdestrepo = "$dgitrepos/$package.git";
-
- my $funcn = $method;
- $funcn =~ y/-/_/;
- my $mainfunc = $main::{"main__$funcn"};
-
- reject "unknown method" unless $mainfunc;
-
- my $lfh = lockrealtree();
+ foreach my $dk (keys %indistrodir) {
+ ${ $indistrodir{$dk} } = "$distrodir/$dk";
+ }
- $policy = policyhook(FRESHREPO,'check-package',$package);
- if ($policy & FRESHREPO) {
- movetogarbage;
+ while (@ARGV && $ARGV[0] =~ m/^--([-0-9a-z]+)=/ && $indistrodir{$1}) {
+ ${ $indistrodir{$1} } = $'; #';
+ shift @ARGV;
}
- close $lfh;
+ $ENV{"DGIT_DRS_\U$_"} = ${ $main::{$_} } foreach @hookenvs;
- if (stat_exists $realdestrepo) {
- $destrepo = $realdestrepo;
- } else {
- debug " fixmissing $funcn";
- my $fixfunc = $main::{"fixmissing__$funcn"};
- &$fixfunc;
- }
+ die unless @ARGV==1;
- debug " running main $funcn";
- &$mainfunc;
+ my $mode = shift @ARGV;
+ die unless $mode =~ m/^--(\w+)$/;
+ my $fn = ${*::}{"mode_$1"};
+ die unless $fn;
+ $fn->();
}
sub unlockall () {