chiark / gitweb /
New --ignore-dirty option to skip noncritical check. Closes: #720895.
[dgit.git] / dgit
diff --git a/dgit b/dgit
index ff6956e1919f67e656aab48dab3a373b41d1bc53..02acfa7fac5be920236d18cc4fdd51f101be3901 100755 (executable)
--- a/dgit
+++ b/dgit
@@ -36,6 +36,7 @@ our $sign = 1;
 our $dryrun = 0;
 our $changesfile;
 our $new_package = 0;
+our $ignoredirty = 0;
 our $existing_package = 'dpkg';
 our $cleanmode = 'dpkg-source';
 
@@ -68,7 +69,7 @@ our $debug = 0;
 open DEBUG, ">/dev/null" or die $!;
 
 our $remotename = 'dgit';
-our $ourdscfield = 'Vcs-Dgit-Master';
+our @ourdscfield = qw(Dgit Vcs-Dgit-Master);
 our $branchprefix = 'dgit';
 our $csuite;
 
@@ -83,7 +84,16 @@ sub debiantag ($) {
     return "debian/$v";
 }
 
-sub dscfn ($) { return "${package}_$_[0].dsc"; }
+sub stripepoch ($) {
+    my ($vsn) = @_;
+    $vsn =~ s/^\d+\://;
+    return $vsn;
+}
+
+sub dscfn ($) {
+    my ($vsn) = @_;
+    return "${package}_".(stripepoch $vsn).".dsc";
+}
 
 sub changesopts () { return @changesopts[1..$#changesopts]; }
 
@@ -230,13 +240,13 @@ our %defcfg = ('dgit.default.distro' => 'debian',
               'dgit.default.ssh' => 'ssh',
               'dgit-distro.debian.git-host' => 'git.debian.org',
               'dgit-distro.debian.git-proto' => 'git+ssh://',
-              'dgit-distro.debian.git-path' => '/git/dgit-repos',
+              'dgit-distro.debian.git-path' => '/git/dgit-repos/repos',
               'dgit-distro.debian.git-check' => 'ssh-cmd',
               'dgit-distro.debian.git-create' => 'ssh-cmd',
               'dgit-distro.debian.sshdakls-host' => 'coccia.debian.org',
               'dgit-distro.debian.sshdakls-dir' =>
                   '/srv/ftp-master.debian.org/ftp/dists',
-              'dgit-distro.debian.mirror' => 'http://http.debian.net/debian/');
+              'dgit-distro.debian.mirror' => 'http://ftp.debian.org/debian/');
 
 sub cfg {
     foreach my $c (@_) {
@@ -399,7 +409,7 @@ sub madison_parse ($) {
        }
        $5 eq 'source' or die "$rmad ?";
        my $prefix = substr($package, 0, $package =~ m/^l/ ? 4 : 1);
-       my $subpath = "/pool/$component/$prefix/$package/${package}_$vsn.dsc";
+       my $subpath = "/pool/$component/$prefix/$package/".dscfn($vsn);
        push @out, [$vsn,$subpath,$newsuite];
     }
     return sort { -version_compare_string($a->[0],$b->[0]); } @out;
@@ -415,6 +425,8 @@ sub canonicalise_suite_madison ($$) {
 }
 
 sub canonicalise_suite () {
+    return if defined $csuite;
+    fail "cannot operate on $isuite suite" if $isuite eq 'UNRELEASED';
     $csuite = archive_query('canonicalise_suite');
     if ($isuite ne $csuite) {
        # madison canonicalises for us
@@ -488,7 +500,7 @@ sub mktree_in_ud_from_only_subdir () {
     $dirs[0] =~ m#^([^/]+)/\.$# or die;
     my $dir = $1;
     chdir $dir or die "$dir $!";
-    die if stat '.git';
+    fail "source package contains .git directory" if stat '.git';
     die $! unless $!==&ENOENT;
     runcmd qw(git init -q);
     rmtree('.git/objects');
@@ -591,12 +603,9 @@ Last allegedly pushed/uploaded: $oversion (newer or same)
 $later_warning_msg
 END
             $outputhash = $upload_hash;
-       } elsif ($outputhash ne $upload_hash) {
-           fail "version in archive ($cversion)".
-               " is same as version in git".
-               " to-be-uploaded (upload/) branch ($oversion)".
-               " but archive version hash no commit hash?!";
-        }
+        } else {
+           $outputhash = $upload_hash;
+       }
     }
     chdir '../../../..' or die $!;
     runcmd @git, qw(update-ref -m),"dgit fetch import $cversion",
@@ -631,8 +640,14 @@ sub rev_parse ($) {
 
 sub is_fast_fwd ($$) {
     my ($ancestor,$child) = @_;
-    my $mb = cmdoutput @git, qw(merge-base), $ancestor, $child;
-    return rev_parse($mb) eq rev_parse($ancestor);
+    my @cmd = (@git, qw(merge-base), $ancestor, $child);
+    my $mb = cmdoutput_errok @cmd;
+    if (defined $mb) {
+       return rev_parse($mb) eq rev_parse($ancestor);
+    } else {
+       $?==256 or failedcmd @cmd;
+       return 0;
+    }
 }
 
 sub git_fetch_us () {
@@ -644,7 +659,10 @@ sub fetch_from_archive () {
     # ensures that lrref() is what is actually in the archive,
     #  one way or another
     get_archive_dsc() or return 0;
-    $dsc_hash = $dsc->{$ourdscfield};
+    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 = $&;
@@ -746,6 +764,7 @@ sub pull () {
 }
 
 sub check_not_dirty () {
+    return if $ignoredirty;
     my @cmd = (@git, qw(diff --quiet HEAD));
     printcmd(\*DEBUG,"+",@cmd) if $debug>0;
     $!=0; $?=0; system @cmd;
@@ -763,7 +782,7 @@ sub commit_quilty_patch () {
     my $bad=0;
     foreach my $l (split /\n/, $output) {
        next unless $l =~ m/\S/;
-       if ($l =~ m{^\?\? (.pc|debian/patches)}) {
+       if ($l =~ m{^(?:\?\?| M) (.pc|debian/patches)}) {
            $adds{$1}++;
        } else {
            print STDERR "git status: $l\n";
@@ -832,15 +851,15 @@ sub dopush () {
 #    runcmd @git, qw(fetch -p ), "$alioth_git/$package.git",
 #        map { lref($_).":".rref($_) }
 #        (uploadbranch());
-    $dsc->{$ourdscfield} = rev_parse('HEAD');
+    $dsc->{$ourdscfield[0]} = rev_parse('HEAD');
     $dsc->save("../$dscfn.tmp") or die $!;
     if (!$changesfile) {
-       my $multi = "../${package}_${cversion}_multi.changes";
+       my $multi = "../${package}_".(stripepoch $cversion)."_multi.changes";
        if (stat "$multi") {
            $changesfile = $multi;
        } else {
            $!==&ENOENT or die "$multi: $!";
-           my $pat = "${package}_${cversion}_*.changes";
+           my $pat = "${package}_".(stripepoch $cversion)."_*.changes";
            my @cs = glob "../$pat";
            fail "failed to find unique changes file".
                " (looked for $pat in .., or $multi);".
@@ -854,6 +873,7 @@ sub dopush () {
        $changes->{$field} eq $clogp->{$field} or
            fail "changes field $field \`$changes->{$field}'".
                " does not match changelog \`$clogp->{$field}'";
+    }
     my $tag = debiantag($dversion);
     if (!check_for_git()) {
        create_remote_git_repo();
@@ -950,7 +970,7 @@ sub cmd_pull {
 sub cmd_push {
     parseopts();
     badusage "-p is not allowed with dgit push" if defined $package;
-    runcmd @git, qw(diff --quiet HEAD);
+    check_not_dirty();
     my $clogp = parsechangelog();
     $package = getfield $clogp, 'Source';
     if (@ARGV==0) {
@@ -966,7 +986,10 @@ sub cmd_push {
        git_fetch_us();
     }
     if (fetch_from_archive()) {
-       is_fast_fwd(lrref(), 'HEAD') or die;
+       is_fast_fwd(lrref(), 'HEAD') or
+           fail "dgit push: HEAD is not a descendant".
+               " of the archive's version.\n".
+               "$us: To overwrite it, use git-merge -s ours ".lrref().".";
     } else {
        $new_package or
            fail "package appears to be new in this suite;".
@@ -991,18 +1014,23 @@ sub build_maybe_quilt_fixup () {
     chomp;
     return unless madformat($_);
     # sigh
+    my $clogp = parsechangelog();
+    my $version = getfield $clogp, 'Version';
+    my $author = getfield $clogp, 'Maintainer';
     my $headref = rev_parse('HEAD');
     my $time = time;
+    my $ncommits = 3;
     my $patchname = "auto-$version-$headref-$time";
-    my $author = cmdoutput @git, qw(log -n1), '--pretty=format:%an <%ae>';
-    my $msg = cmdoutput @git, qw(log -n1), "--pretty=format:%s\n%b";
+    my $msg = cmdoutput @git, qw(log), "-n$ncommits";
     my $descfn = ".git/dgit/quilt-description.tmp";
     open O, '>', $descfn or die "$descfn: $!";
     $msg =~ s/\n/\n /g;
     $msg =~ s/^\s+$/ ./mg;
     print O <<END or die $!;
-Description: $msg
- [generated from git commit $headref]
+Description: Automatically generated patch ($clogp->{Version})
+ Last (up to) $ncommits git changes, FYI:
+ .
+ $msg
 Author: $author
 
 ---
@@ -1015,6 +1043,13 @@ END
        local $ENV{$fakeeditorenv} = cmdoutput qw(realpath --), $descfn;
        runcmd_ordryrun @dpkgsource, qw(--commit .), $patchname;
     }
+
+    if (!open P, '>>', ".pc/applied-patches") {
+       $!==&ENOENT or die $!;
+    } else {
+       close P;
+    }
+
     commit_quilty_patch();
 }
 
@@ -1037,19 +1072,32 @@ sub quilt_fixup_editor () {
     exit 0;
 }
 
-sub cmd_build {
-    # we pass further options and args to git-buildpackage
-    badusage "-p is not allowed with dgit build" if defined $package;
-    badusage "dgit build implies --clean=dpkg-source" if defined $package;
+sub build_prep () {
+    badusage "-p is not allowed when building" if defined $package;
+    check_not_dirty();
     my $clogp = parsechangelog();
     $isuite = getfield $clogp, 'Distribution';
     $package = getfield $clogp, 'Source';
     $version = getfield $clogp, 'Version';
     build_maybe_quilt_fixup();
+}
+
+sub cmd_build {
+    badusage "dgit build implies --clean=dpkg-source"
+       if $cleanmode ne 'dpkg-source';
+    build_prep();
+    runcmd_ordryrun @dpkgbuildpackage, qw(-us -uc), changesopts(), @ARGV;
+    printdone "build successful\n";
+}
+
+sub cmd_git_build {
+    badusage "dgit git-build implies --clean=dpkg-source"
+       if $cleanmode ne 'dpkg-source';
+    build_prep();
     my @cmd =
        (qw(git-buildpackage -us -uc --git-no-sign-tags),
         "--git-builder=@dpkgbuildpackage");
-    unless (grep { m/^--git-debian-branch/ } @ARGV) {
+    unless (grep { m/^--git-debian-branch|^--git-ignore-branch/ } @ARGV) {
        canonicalise_suite();
        push @cmd, "--git-debian-branch=".lbranch();
     }
@@ -1059,15 +1107,9 @@ sub cmd_build {
 }
 
 sub build_source {
-    badusage "-p is not allowed with this action" if defined $package;
-    check_not_dirty();
-    my $clogp = parsechangelog();
-    $package = getfield $clogp, 'Source';
-    my $isuite = getfield $clogp, 'Distribution';
-    $version = getfield $clogp, 'Version';
-    $sourcechanges = "${package}_${version}_source.changes";
+    build_prep();
+    $sourcechanges = "${package}_".(stripepoch $version)."_source.changes";
     $dscfn = dscfn($version);
-    build_maybe_quilt_fixup();
     if ($cleanmode eq 'dpkg-source') {
        runcmd_ordryrun (@dpkgbuildpackage, qw(-us -uc -S)), changesopts();
     } else {
@@ -1098,7 +1140,7 @@ sub cmd_build_source {
 sub cmd_sbuild {
     build_source();
     chdir ".." or die $!;
-    my $pat = "${package}_${version}_*.changes";
+    my $pat = "${package}_".(stripepoch $version)."_*.changes";
     if (!$dryrun) {
        stat $dscfn or fail "$dscfn (in parent directory): $!";
        stat $sourcechanges or fail "$sourcechanges (in parent directory): $!";
@@ -1109,7 +1151,7 @@ sub cmd_sbuild {
     }
     runcmd_ordryrun @sbuild, @ARGV, qw(-d), $isuite, $dscfn;
     runcmd_ordryrun @mergechanges, glob $pat;
-    my $multichanges = "${package}_${version}_multi.changes";
+    my $multichanges = "${package}_".(stripepoch $version)."_multi.changes";
     if (!$dryrun) {
        stat $multichanges or fail "$multichanges: $!";
     }
@@ -1153,6 +1195,8 @@ sub parseopts () {
                $cleanmode = $1;
            } elsif (m/^--clean=(.*)$/s) {
                badusage "unknown cleaning mode \`$1'";
+           } elsif (m/^--ignore-dirty$/s) {
+               $ignoredirty = 1;
            } else {
                badusage "unknown long option \`$_'";
            }