chiark / gitweb /
Finish dealing with uncuddled options
[dgit.git] / dgit
diff --git a/dgit b/dgit
index 13b47fd32b1fcd017cb6234965e9495b9000966e..eb9a97c3d0269604ca3bd1a1995510b79b4341ba 100755 (executable)
--- a/dgit
+++ b/dgit
@@ -57,7 +57,7 @@ our $rmonerror = 1;
 our @deliberatelies;
 our %previously;
 our $existing_package = 'dpkg';
 our @deliberatelies;
 our %previously;
 our $existing_package = 'dpkg';
-our $cleanmode = 'dpkg-source';
+our $cleanmode;
 our $changes_since_version;
 our $quilt_mode;
 our $quilt_modes_re = 'linear|smash|auto|nofix|nocheck';
 our $changes_since_version;
 our $quilt_mode;
 our $quilt_modes_re = 'linear|smash|auto|nofix|nocheck';
@@ -67,6 +67,7 @@ our $initiator_tempdir;
 our %format_ok = map { $_=>1 } ("1.0","3.0 (native)","3.0 (quilt)");
 
 our $suite_re = '[-+.0-9a-z]+';
 our %format_ok = map { $_=>1 } ("1.0","3.0 (native)","3.0 (quilt)");
 
 our $suite_re = '[-+.0-9a-z]+';
+our $cleanmode_re = 'dpkg-source(?:-d)?|git|git-ff|check|none';
 
 our (@git) = qw(git);
 our (@dget) = qw(dget);
 
 our (@git) = qw(git);
 our (@dget) = qw(dget);
@@ -91,13 +92,20 @@ our %opts_opt_map = ('dget' => \@dget, # accept for compatibility
                      'sbuild' => \@sbuild,
                      'ssh' => \@ssh,
                      'dgit' => \@dgit,
                      'sbuild' => \@sbuild,
                      'ssh' => \@ssh,
                      'dgit' => \@dgit,
+                     'git' => \@git,
                      'dpkg-source' => \@dpkgsource,
                      'dpkg-buildpackage' => \@dpkgbuildpackage,
                      'dpkg-genchanges' => \@dpkggenchanges,
                      'ch' => \@changesopts,
                      'mergechanges' => \@mergechanges);
 
                      'dpkg-source' => \@dpkgsource,
                      'dpkg-buildpackage' => \@dpkgbuildpackage,
                      'dpkg-genchanges' => \@dpkggenchanges,
                      'ch' => \@changesopts,
                      'mergechanges' => \@mergechanges);
 
-our %opts_opt_cmdonly = ('gpg' => 1);
+our %opts_opt_cmdonly = ('gpg' => 1, 'git' => 1);
+our %opts_cfg_insertpos = map {
+    $_,
+    scalar @{ $opts_opt_map{$_} }
+} keys %opts_opt_map;
+
+sub finalise_opts_opts();
 
 our $keyid;
 
 
 our $keyid;
 
@@ -416,7 +424,8 @@ our $helpmsg = <<END;
 main usages:
   dgit [dgit-opts] clone [dgit-opts] package [suite] [./dir|/dir]
   dgit [dgit-opts] fetch|pull [dgit-opts] [suite]
 main usages:
   dgit [dgit-opts] clone [dgit-opts] package [suite] [./dir|/dir]
   dgit [dgit-opts] fetch|pull [dgit-opts] [suite]
-  dgit [dgit-opts] build [git-buildpackage-opts|dpkg-buildpackage-opts]
+  dgit [dgit-opts] build [dpkg-buildpackage-opts]
+  dgit [dgit-opts] sbuild [sbuild-opts]
   dgit [dgit-opts] push [dgit-opts] [suite]
   dgit [dgit-opts] rpush build-host:build-dir ...
 important dgit options:
   dgit [dgit-opts] push [dgit-opts] [suite]
   dgit [dgit-opts] rpush build-host:build-dir ...
 important dgit options:
@@ -497,28 +506,36 @@ our %defcfg = ('dgit.default.distro' => 'debian',
               'dgit-distro.test-dummy.upload-host' => 'test-dummy',
                );
 
               'dgit-distro.test-dummy.upload-host' => 'test-dummy',
                );
 
-sub git_get_config ($) {
-    my ($c) = @_;
+our %gitcfg;
 
 
-    our %git_get_config_memo;
-    if (exists $git_get_config_memo{$c}) {
-       return $git_get_config_memo{$c};
-    }
+sub git_slurp_config () {
+    local ($debuglevel) = $debuglevel-2;
+    local $/="\0";
 
 
-    my $v;
-    my @cmd = (@git, qw(config --), $c);
-    {
-       local ($debuglevel) = $debuglevel-2;
-       $v = cmdoutput_errok @cmd;
-    };
-    if ($?==0) {
-    } elsif ($?==256) {
-       $v = undef;
-    } else {
-       failedcmd @cmd;
+    my @cmd = (@git, qw(config -z --get-regexp .*));
+    debugcmd "|",@cmd;
+
+    open GITS, "-|", @cmd or failedcmd @cmd;
+    while (<GITS>) {
+       chomp or die;
+       printdebug "=> ", (messagequote $_), "\n";
+       m/\n/ or die "$_ ?";
+       push @{ $gitcfg{$`} }, $'; #';
     }
     }
-    $git_get_config_memo{$c} = $v;
-    return $v;
+    $!=0; $?=0;
+    close GITS
+       or ($!==0 && $?==256)
+       or failedcmd @cmd;
+}
+
+sub git_get_config ($) {
+    my ($c) = @_;
+    my $l = $gitcfg{$c};
+    printdebug"C $c ".(defined $l ? messagequote "'$l'" : "undef")."\n"
+       if $debuglevel >= 4;
+    $l or return undef;
+    @$l==1 or badcfg "multiple values for $c" if @$l > 1;
+    return $l->[0];
 }
 
 sub cfg {
 }
 
 sub cfg {
@@ -603,6 +620,11 @@ sub pushing () {
 Push failed, before we got started.
 You can retry the push, after fixing the problem, if you like.
 END
 Push failed, before we got started.
 You can retry the push, after fixing the problem, if you like.
 END
+    finalise_opts_opts();
+}
+
+sub notpushing () {
+    finalise_opts_opts();
 }
 
 sub supplementary_message ($) {
 }
 
 sub supplementary_message ($) {
@@ -638,7 +660,7 @@ sub access_distros () {
     @l;
 }
 
     @l;
 }
 
-sub access_cfg (@) {
+sub access_cfg_cfgs (@) {
     my (@keys) = @_;
     my @cfgs;
     # The nesting of these loops determines the search order.  We put
     my (@keys) = @_;
     my @cfgs;
     # The nesting of these loops determines the search order.  We put
@@ -665,10 +687,21 @@ sub access_cfg (@) {
     }
     push @cfgs, map { "dgit.default.$_" } @realkeys;
     push @cfgs, @rundef;
     }
     push @cfgs, map { "dgit.default.$_" } @realkeys;
     push @cfgs, @rundef;
+    return @cfgs;
+}
+
+sub access_cfg (@) {
+    my (@keys) = @_;
+    my (@cfgs) = access_cfg_cfgs(@keys);
     my $value = cfg(@cfgs);
     return $value;
 }
 
     my $value = cfg(@cfgs);
     return $value;
 }
 
+sub access_cfg_bool ($$) {
+    my ($def, @keys) = @_;
+    parse_cfg_bool($keys[0], $def, access_cfg(@keys, 'RETURN-UNDEF'));
+}
+
 sub string_to_ssh ($) {
     my ($spec) = @_;
     if ($spec =~ m/\s/) {
 sub string_to_ssh ($) {
     my ($spec) = @_;
     if ($spec =~ m/\s/) {
@@ -959,7 +992,7 @@ sub sshpsql ($$$) {
     open P, "-|", @cmd or die $!;
     while (<P>) {
        chomp or die;
     open P, "-|", @cmd or die $!;
     while (<P>) {
        chomp or die;
-       printdebug("$debugprefix>|$_|\n");
+       printdebug(">|$_|\n");
        push @rows, $_;
     }
     $!=0; $?=0; close P or failedcmd @cmd;
        push @rows, $_;
     }
     $!=0; $?=0; close P or failedcmd @cmd;
@@ -1127,6 +1160,9 @@ sub check_for_git () {
        my $url = "$prefix/$package$suffix";
        my @cmd = (qw(curl -sS -I), $url);
        my $result = cmdoutput @cmd;
        my $url = "$prefix/$package$suffix";
        my @cmd = (qw(curl -sS -I), $url);
        my $result = cmdoutput @cmd;
+       $result =~ s/^\S+ 200 .*\n\r?\n//;
+       # curl -sS -I with https_proxy prints
+       # HTTP/1.0 200 Connection established
        $result =~ m/^\S+ (404|200) /s or
            fail "unexpected results from git check query - ".
                Dumper($prefix, $result);
        $result =~ m/^\S+ (404|200) /s or
            fail "unexpected results from git check query - ".
                Dumper($prefix, $result);
@@ -1184,14 +1220,7 @@ sub git_write_tree () {
     return $tree;
 }
 
     return $tree;
 }
 
-sub mktree_in_ud_from_only_subdir () {
-    # changes into the subdir
-    my (@dirs) = <*/.>;
-    die unless @dirs==1;
-    $dirs[0] =~ m#^([^/]+)/\.$# or die;
-    my $dir = $1;
-    changedir $dir;
-
+sub remove_stray_gits () {
     my @gitscmd = qw(find -name .git -prune -print0);
     debugcmd "|",@gitscmd;
     open GITS, "-|", @gitscmd or failedcmd @gitscmd;
     my @gitscmd = qw(find -name .git -prune -print0);
     debugcmd "|",@gitscmd;
     open GITS, "-|", @gitscmd or failedcmd @gitscmd;
@@ -1205,7 +1234,17 @@ sub mktree_in_ud_from_only_subdir () {
        }
     }
     $!=0; $?=0; close GITS or failedcmd @gitscmd;
        }
     }
     $!=0; $?=0; close GITS or failedcmd @gitscmd;
+}
 
 
+sub mktree_in_ud_from_only_subdir () {
+    # changes into the subdir
+    my (@dirs) = <*/.>;
+    die unless @dirs==1;
+    $dirs[0] =~ m#^([^/]+)/\.$# or die;
+    my $dir = $1;
+    changedir $dir;
+
+    remove_stray_gits();
     mktree_in_ud_here();
     my $format=get_source_format();
     if (madformat($format)) {
     mktree_in_ud_here();
     my $format=get_source_format();
     if (madformat($format)) {
@@ -1608,7 +1647,10 @@ sub set_local_git_config ($$) {
     runcmd @git, qw(config), $k, $v;
 }
 
     runcmd @git, qw(config), $k, $v;
 }
 
-sub setup_mergechangelogs () {
+sub setup_mergechangelogs (;$) {
+    my ($always) = @_;
+    return unless $always || access_cfg_bool(1, 'setup-mergechangelogs');
+
     my $driver = 'dpkg-mergechangelogs';
     my $cb = "merge.$driver";
     my $attrs = '.git/info/attributes';
     my $driver = 'dpkg-mergechangelogs';
     my $cb = "merge.$driver";
     my $attrs = '.git/info/attributes';
@@ -1635,6 +1677,26 @@ sub setup_mergechangelogs () {
     rename "$attrs.new", "$attrs" or die "$attrs: $!";
 }
 
     rename "$attrs.new", "$attrs" or die "$attrs: $!";
 }
 
+sub setup_useremail (;$) {
+    my ($always) = @_;
+    return unless $always || access_cfg_bool(1, 'setup-useremail');
+
+    my $setup = sub {
+       my ($k, $envvar) = @_;
+       my $v = access_cfg("user-$k", 'RETURN-UNDEF') // $ENV{$envvar};
+       return unless defined $v;
+       set_local_git_config "user.$k", $v;
+    };
+
+    $setup->('email', 'DEBEMAIL');
+    $setup->('name', 'DEBFULLNAME');
+}
+
+sub setup_new_tree () {
+    setup_mergechangelogs();
+    setup_useremail();
+}
+
 sub clone ($) {
     my ($dstdir) = @_;
     canonicalise_suite();
 sub clone ($) {
     my ($dstdir) = @_;
     canonicalise_suite();
@@ -1664,7 +1726,7 @@ sub clone ($) {
        $vcsgiturl =~ s/\s+-b\s+\S+//g;
        runcmd @git, qw(remote add vcs-git), $vcsgiturl;
     }
        $vcsgiturl =~ s/\s+-b\s+\S+//g;
        runcmd @git, qw(remote add vcs-git), $vcsgiturl;
     }
-    setup_mergechangelogs();
+    setup_new_tree();
     runcmd @git, qw(reset --hard), lrref();
     printdone "ready for work in $dstdir";
 }
     runcmd @git, qw(reset --hard), lrref();
     printdone "ready for work in $dstdir";
 }
@@ -1816,6 +1878,9 @@ END
        if (!defined $keyid) {
            $keyid = access_cfg('keyid','RETURN-UNDEF');
        }
        if (!defined $keyid) {
            $keyid = access_cfg('keyid','RETURN-UNDEF');
        }
+        if (!defined $keyid) {
+           $keyid = getfield $clogp, 'Maintainer';
+        }
        unlink $tfn->('.tmp.asc') or $!==&ENOENT or die $!;
        my @sign_cmd = (@gpg, qw(--detach-sign --armor));
        push @sign_cmd, qw(-u),$keyid if defined $keyid;
        unlink $tfn->('.tmp.asc') or $!==&ENOENT or die $!;
        my @sign_cmd = (@gpg, qw(--detach-sign --armor));
        push @sign_cmd, qw(-u),$keyid if defined $keyid;
@@ -2004,6 +2069,7 @@ END
 
 sub cmd_clone {
     parseopts();
 
 sub cmd_clone {
     parseopts();
+    notpushing();
     my $dstdir;
     badusage "-p is not allowed with clone; specify as argument instead"
        if defined $package;
     my $dstdir;
     badusage "-p is not allowed with clone; specify as argument instead"
        if defined $package;
@@ -2051,6 +2117,7 @@ sub branchsuite () {
 }
 
 sub fetchpullargs () {
 }
 
 sub fetchpullargs () {
+    notpushing();
     if (!defined $package) {
        my $sourcep = parsecontrol('debian/control','debian/control');
        $package = getfield $sourcep, 'Source';
     if (!defined $package) {
        my $sourcep = parsecontrol('debian/control','debian/control');
        $package = getfield $sourcep, 'Source';
@@ -2084,8 +2151,8 @@ sub cmd_pull {
 }
 
 sub cmd_push {
 }
 
 sub cmd_push {
-    pushing();
     parseopts();
     parseopts();
+    pushing();
     badusage "-p is not allowed with dgit push" if defined $package;
     check_not_dirty();
     my $clogp = parsechangelog();
     badusage "-p is not allowed with dgit push" if defined $package;
     check_not_dirty();
     my $clogp = parsechangelog();
@@ -2451,6 +2518,7 @@ sub quiltify ($$) {
     # should be contained within debian/patches.
 
     changedir '../fake';
     # should be contained within debian/patches.
 
     changedir '../fake';
+    remove_stray_gits();
     mktree_in_ud_here();
     rmtree '.pc';
     runcmd @git, 'add', '.';
     mktree_in_ud_here();
     rmtree '.pc';
     runcmd @git, 'add', '.';
@@ -2800,7 +2868,10 @@ sub quilt_fixup_editor () {
 
 #----- other building -----
 
 
 #----- other building -----
 
+our $suppress_clean;
+
 sub clean_tree () {
 sub clean_tree () {
+    return if $suppress_clean;
     if ($cleanmode eq 'dpkg-source') {
        runcmd_ordryrun_local @dpkgbuildpackage, qw(-T clean);
     } elsif ($cleanmode eq 'dpkg-source-d') {
     if ($cleanmode eq 'dpkg-source') {
        runcmd_ordryrun_local @dpkgbuildpackage, qw(-T clean);
     } elsif ($cleanmode eq 'dpkg-source-d') {
@@ -2823,10 +2894,12 @@ sub clean_tree () {
 
 sub cmd_clean () {
     badusage "clean takes no additional arguments" if @ARGV;
 
 sub cmd_clean () {
     badusage "clean takes no additional arguments" if @ARGV;
+    notpushing();
     clean_tree();
 }
 
 sub build_prep () {
     clean_tree();
 }
 
 sub build_prep () {
+    notpushing();
     badusage "-p is not allowed when building" if defined $package;
     check_not_dirty();
     clean_tree();
     badusage "-p is not allowed when building" if defined $package;
     check_not_dirty();
     clean_tree();
@@ -2837,8 +2910,11 @@ sub build_prep () {
     build_maybe_quilt_fixup();
 }
 
     build_maybe_quilt_fixup();
 }
 
-sub changesopts () {
+sub changesopts_initial () {
     my @opts =@changesopts[1..$#changesopts];
     my @opts =@changesopts[1..$#changesopts];
+}
+
+sub changesopts_version () {
     if (!defined $changes_since_version) {
        my @vsns = archive_query('archive_query');
        my @quirk = access_quirk();
     if (!defined $changes_since_version) {
        my @vsns = archive_query('archive_query');
        my @quirk = access_quirk();
@@ -2859,40 +2935,60 @@ sub changesopts () {
        }
     }
     if ($changes_since_version ne '_') {
        }
     }
     if ($changes_since_version ne '_') {
-       unshift @opts, "-v$changes_since_version";
+       return ("-v$changes_since_version");
+    } else {
+       return ();
     }
     }
-    return @opts;
 }
 
 }
 
-sub massage_dbp_args ($) {
-    my ($cmd) = @_;
-    return unless $cleanmode =~ m/git|none/;
+sub changesopts () {
+    return (changesopts_initial(), changesopts_version());
+}
+
+sub massage_dbp_args ($;$) {
+    my ($cmd,$xargs) = @_;
+    if ($cleanmode eq 'dpkg-source') {
+       $suppress_clean = 1;
+       return;
+    }
     debugcmd '#massaging#', @$cmd if $debuglevel>1;
     my @newcmd = shift @$cmd;
     # -nc has the side effect of specifying -b if nothing else specified
     push @newcmd, '-nc';
     # and some combinations of -S, -b, et al, are errors, rather than
     # later simply overriding earlier
     debugcmd '#massaging#', @$cmd if $debuglevel>1;
     my @newcmd = shift @$cmd;
     # -nc has the side effect of specifying -b if nothing else specified
     push @newcmd, '-nc';
     # and some combinations of -S, -b, et al, are errors, rather than
     # later simply overriding earlier
-    push @newcmd, '-F' unless grep { m/^-[bBASF]$/ } @$cmd;
+    push @newcmd, '-F' unless grep { m/^-[bBASF]$/ } (@$cmd, @$xargs);
     push @newcmd, @$cmd;
     @$cmd = @newcmd;
 }
 
 sub cmd_build {
     push @newcmd, @$cmd;
     @$cmd = @newcmd;
 }
 
 sub cmd_build {
-    build_prep();
-    my @dbp = (@dpkgbuildpackage, qw(-us -uc), changesopts(), @ARGV);
+    my @dbp = (@dpkgbuildpackage, qw(-us -uc), changesopts_initial(), @ARGV);
     massage_dbp_args \@dbp;
     massage_dbp_args \@dbp;
+    build_prep();
+    push @dbp, changesopts_version();
     runcmd_ordryrun_local @dbp;
     printdone "build successful\n";
 }
 
     runcmd_ordryrun_local @dbp;
     printdone "build successful\n";
 }
 
-sub cmd_git_build {
-    build_prep();
+sub cmd_gbp_build {
     my @dbp = @dpkgbuildpackage;
     my @dbp = @dpkgbuildpackage;
-    massage_dbp_args \@dbp;
-    my @cmd =
-       (qw(git-buildpackage -us -uc --git-no-sign-tags),
-        "--git-builder=@dbp");
+    massage_dbp_args \@dbp, \@ARGV;
+
+    my @cmd;
+    if (length executable_on_path('git-buildpackage')) {
+       @cmd = qw(git-buildpackage);
+    } else {
+       @cmd = qw(gbp buildpackage);
+    }
+    push @cmd, (qw(-us -uc --git-no-sign-tags), "--git-builder=@dbp");
+
+    if ($cleanmode eq 'dpkg-source') {
+       $suppress_clean = 1;
+    } else {
+       push @cmd, '--git-cleaner=true';
+    }
+    build_prep();
     unless (grep { m/^--git-debian-branch|^--git-ignore-branch/ } @ARGV) {
        canonicalise_suite();
        push @cmd, "--git-debian-branch=".lbranch();
     unless (grep { m/^--git-debian-branch|^--git-ignore-branch/ } @ARGV) {
        canonicalise_suite();
        push @cmd, "--git-debian-branch=".lbranch();
@@ -2901,8 +2997,13 @@ sub cmd_git_build {
     runcmd_ordryrun_local @cmd, @ARGV;
     printdone "build successful\n";
 }
     runcmd_ordryrun_local @cmd, @ARGV;
     printdone "build successful\n";
 }
+sub cmd_git_build { cmd_gbp_build(); } # compatibility with <= 1.0
 
 sub build_source {
 
 sub build_source {
+    if ($cleanmode =~ m/^dpkg-source/) {
+       # dpkg-source will clean, so we shouldn't
+       $suppress_clean = 1;
+    }
     build_prep();
     $sourcechanges = "${package}_".(stripepoch $version)."_source.changes";
     $dscfn = dscfn($version);
     build_prep();
     $sourcechanges = "${package}_".(stripepoch $version)."_source.changes";
     $dscfn = dscfn($version);
@@ -2987,7 +3088,17 @@ sub cmd_clone_dgit_repos_server {
 
 sub cmd_setup_mergechangelogs {
     badusage "no arguments allowed to dgit setup-mergechangelogs" if @ARGV;
 
 sub cmd_setup_mergechangelogs {
     badusage "no arguments allowed to dgit setup-mergechangelogs" if @ARGV;
-    setup_mergechangelogs();
+    setup_mergechangelogs(1);
+}
+
+sub cmd_setup_useremail {
+    badusage "no arguments allowed to dgit setup-mergechangelogs" if @ARGV;
+    setup_useremail(1);
+}
+
+sub cmd_setup_new_tree {
+    badusage "no arguments allowed to dgit setup-tree" if @ARGV;
+    setup_new_tree();
 }
 
 #---------- argument parsing and main program ----------
 }
 
 #---------- argument parsing and main program ----------
@@ -2997,6 +3108,44 @@ sub cmd_version {
     exit 0;
 }
 
     exit 0;
 }
 
+our (%valopts_long, %valopts_short);
+our @rvalopts;
+
+sub defvalopt ($$$$) {
+    my ($long,$short,$val_re,$how) = @_;
+    my $oi = { Long => $long, Short => $short, Re => $val_re, How => $how };
+    $valopts_long{$long} = $oi;
+    $valopts_short{$short} = $oi;
+    # $how subref should:
+    #   do whatever assignemnt or thing it likes with $_[0]
+    #   if the option should not be passed on to remote, @rvalopts=()
+    # or $how can be a scalar ref, meaning simply assign the value
+}
+
+defvalopt '--since-version', '-v', '[^_]+|_', \$changes_since_version;
+defvalopt '--distro',        '-d', '.+',      \$idistro;
+defvalopt '',                '-k', '.+',      \$keyid;
+defvalopt '--existing-package','', '.*',      \$existing_package;
+defvalopt '--build-products-dir','','.*',     \$buildproductsdir;
+defvalopt '--clean',       '', $cleanmode_re, \$cleanmode;
+defvalopt '--quilt',     '', $quilt_modes_re, \$quilt_mode;
+
+defvalopt '', '-c', '.*=.*', sub { push @git, '-c', @_; };
+
+defvalopt '', '-C', '.+', sub {
+    ($changesfile) = (@_);
+    if ($changesfile =~ s#^(.*)/##) {
+       $buildproductsdir = $1;
+    }
+};
+
+defvalopt '--initiator-tempdir','','.*', sub {
+    ($initiator_tempdir) = (@_);
+    $initiator_tempdir =~ m#^/# or
+       badusage "--initiator-tempdir must be used specify an".
+       " absolute, not relative, directory."
+};
+
 sub parseopts () {
     my $om;
 
 sub parseopts () {
     my $om;
 
@@ -3006,6 +3155,27 @@ sub parseopts () {
        @ssh = ($ENV{'GIT_SSH'});
     }
 
        @ssh = ($ENV{'GIT_SSH'});
     }
 
+    my $oi;
+    my $val;
+    my $valopt = sub {
+       my ($what) = @_;
+       @rvalopts = ($_);
+       if (!defined $val) {
+           badusage "$what needs a value" unless length @ARGV;
+           $val = shift @ARGV;
+           push @rvalopts, $val;
+       }
+       badusage "bad value \`$val' for $what" unless
+           $val =~ m/^$oi->{Re}$(?!\n)/s;
+       my $how = $oi->{How};
+       if (ref($how) eq 'SCALAR') {
+           $$how = $val;
+       } else {
+           $how->($val);
+       }
+       push @ropts, @rvalopts;
+    };
+
     while (@ARGV) {
        last unless $ARGV[0] =~ m/^-/;
        $_ = shift @ARGV;
     while (@ARGV) {
        last unless $ARGV[0] =~ m/^-/;
        $_ = shift @ARGV;
@@ -3027,9 +3197,6 @@ sub parseopts () {
            } elsif (m/^--new$/) {
                push @ropts, $_;
                $new_package=1;
            } elsif (m/^--new$/) {
                push @ropts, $_;
                $new_package=1;
-           } elsif (m/^--since-version=([^_]+|_)$/) {
-               push @ropts, $_;
-               $changes_since_version = $1;
            } elsif (m/^--([-0-9a-z]+)=(.+)/s &&
                     ($om = $opts_opt_map{$1}) &&
                     length $om->[0]) {
            } elsif (m/^--([-0-9a-z]+)=(.+)/s &&
                     ($om = $opts_opt_map{$1}) &&
                     length $om->[0]) {
@@ -3040,30 +3207,6 @@ sub parseopts () {
                     ($om = $opts_opt_map{$1})) {
                push @ropts, $_;
                push @$om, $2;
                     ($om = $opts_opt_map{$1})) {
                push @ropts, $_;
                push @$om, $2;
-           } elsif (m/^--existing-package=(.*)/s) {
-               push @ropts, $_;
-               $existing_package = $1;
-           } elsif (m/^--initiator-tempdir=(.*)/s) {
-               $initiator_tempdir = $1;
-               $initiator_tempdir =~ m#^/# or
-                   badusage "--initiator-tempdir must be used specify an".
-                       " absolute, not relative, directory."
-           } elsif (m/^--distro=(.*)/s) {
-               push @ropts, $_;
-               $idistro = $1;
-           } elsif (m/^--build-products-dir=(.*)/s) {
-               push @ropts, $_;
-               $buildproductsdir = $1;
-           } elsif (m/^--clean=(dpkg-source(?:-d)?|git|git-ff|check|none)$/s) {
-               push @ropts, $_;
-               $cleanmode = $1;
-           } elsif (m/^--clean=(.*)$/s) {
-               badusage "unknown cleaning mode \`$1'";
-           } elsif (m/^--quilt=($quilt_modes_re)$/s) {
-               push @ropts, $_;
-               $quilt_mode = $1;
-           } elsif (m/^--quilt=(.*)$/s) {
-               badusage "unknown quilt fixup mode \`$1'";
            } elsif (m/^--ignore-dirty$/s) {
                push @ropts, $_;
                $ignoredirty = 1;
            } elsif (m/^--ignore-dirty$/s) {
                push @ropts, $_;
                $ignoredirty = 1;
@@ -3076,6 +3219,9 @@ sub parseopts () {
            } elsif (m/^--deliberately-($deliberately_re)$/s) {
                push @ropts, $_;
                push @deliberatelies, $&;
            } elsif (m/^--deliberately-($deliberately_re)$/s) {
                push @ropts, $_;
                push @deliberatelies, $&;
+           } elsif (m/^(--[-0-9a-z]+)(=|$)/ && ($oi = $valopts_long{$1})) {
+               $val = $2 ? $' : undef; #';
+               $valopt->($oi->{Long});
            } else {
                badusage "unknown long option \`$_'";
            }
            } else {
                badusage "unknown long option \`$_'";
            }
@@ -3096,30 +3242,10 @@ sub parseopts () {
                } elsif (s/^-N/-/) {
                    push @ropts, $&;
                    $new_package=1;
                } elsif (s/^-N/-/) {
                    push @ropts, $&;
                    $new_package=1;
-               } elsif (s/^-v([^_]+|_)$//s) {
-                   push @ropts, $&;
-                   $changes_since_version = $1;
                } elsif (m/^-m/) {
                    push @ropts, $&;
                    push @changesopts, $_;
                    $_ = '';
                } elsif (m/^-m/) {
                    push @ropts, $&;
                    push @changesopts, $_;
                    $_ = '';
-               } elsif (s/^-c(.*=.*)//s) {
-                   push @ropts, $&;
-                   push @git, '-c', $1;
-               } elsif (s/^-d(.+)//s) {
-                   push @ropts, $&;
-                   $idistro = $1;
-               } elsif (s/^-C(.+)//s) {
-                   push @ropts, $&;
-                   $changesfile = $1;
-                   if ($changesfile =~ s#^(.*)/##) {
-                       $buildproductsdir = $1;
-                   }
-               } elsif (s/^-k(.+)//s) {
-                   $keyid=$1;
-               } elsif (m/^-[vdCk]$/) {
-                   badusage
- "option \`$_' requires an argument (and no space before the argument)";
                } elsif (s/^-wn$//s) {
                    push @ropts, $&;
                    $cleanmode = 'none';
                } elsif (s/^-wn$//s) {
                    push @ropts, $&;
                    $cleanmode = 'none';
@@ -3138,6 +3264,11 @@ sub parseopts () {
                } elsif (s/^-wc$//s) {
                    push @ropts, $&;
                    $cleanmode = 'check';
                } elsif (s/^-wc$//s) {
                    push @ropts, $&;
                    $cleanmode = 'check';
+               } elsif (m/^-[a-zA-Z]/ && ($oi = $valopts_short{$&})) {
+                   $val = $'; #';
+                   $val = undef unless length $val;
+                   $valopt->($oi->{Short});
+                   $_ = '';
                } else {
                    badusage "unknown short option \`$_'";
                }
                } else {
                    badusage "unknown short option \`$_'";
                }
@@ -3146,11 +3277,40 @@ sub parseopts () {
     }
 }
 
     }
 }
 
+sub finalise_opts_opts () {
+    foreach my $k (keys %opts_opt_map) {
+       my $om = $opts_opt_map{$k};
+
+       my $v = access_cfg("cmd-$k", 'RETURN-UNDEF');
+       if (defined $v) {
+           badcfg "cannot set command for $k"
+               unless length $om->[0];
+           $om->[0] = $v;
+       }
+
+       foreach my $c (access_cfg_cfgs("opts-$k")) {
+           my $vl = $gitcfg{$c};
+           printdebug "CL $c ",
+               ($vl ? join " ", map { shellquote } @$vl : ""),
+               "\n" if $debuglevel >= 4;
+           next unless $vl;
+           badcfg "cannot configure options for $k"
+               if $opts_opt_cmdonly{$k};
+           my $insertpos = $opts_cfg_insertpos{$k};
+           @$om = ( @$om[0..$insertpos-1],
+                    @$vl,
+                    @$om[$insertpos..$#$om] );
+       }
+    }
+}
+
 if ($ENV{$fakeeditorenv}) {
 if ($ENV{$fakeeditorenv}) {
+    git_slurp_config();
     quilt_fixup_editor();
 }
 
 parseopts();
     quilt_fixup_editor();
 }
 
 parseopts();
+git_slurp_config();
 
 print STDERR "DRY RUN ONLY\n" if $dryrun_level > 1;
 print STDERR "DAMP RUN - WILL MAKE LOCAL (UNSIGNED) CHANGES\n"
 
 print STDERR "DRY RUN ONLY\n" if $dryrun_level > 1;
 print STDERR "DAMP RUN - WILL MAKE LOCAL (UNSIGNED) CHANGES\n"
@@ -3172,6 +3332,15 @@ if (!defined $quilt_mode) {
     $quilt_mode = $1;
 }
 
     $quilt_mode = $1;
 }
 
+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->();
 my $fn = ${*::}{"cmd_$cmd"};
 $fn or badusage "unknown operation $cmd";
 $fn->();