chiark / gitweb /
parsechangelog takes optional args for dpkg-parsechangelog
[dgit.git] / dgit
diff --git a/dgit b/dgit
index a21cddec32952c26243ae029a9edd490cb39d3a3..123c2d513ae2cc05f3f3dd82a90c06a3b64202bd 100755 (executable)
--- a/dgit
+++ b/dgit
@@ -28,6 +28,8 @@ use File::Basename;
 use Dpkg::Version;
 use POSIX;
 
+our $our_version = 'UNRELEASED'; ###substituted###
+
 our $isuite = 'unstable';
 our $idistro;
 our $package;
@@ -207,6 +209,11 @@ sub runcmd_ordryrun {
     }
 }
 
+sub shell_cmd {
+    my ($first_shell, @cmd) = @_;
+    return qw(sh -ec), $first_shell.'; exec "$@"', 'x', @cmd;
+}
+
 our $helpmsg = <<END;
 main usages:
   dgit [dgit-opts] clone [dgit-opts] package [suite] [./dir|/dir]
@@ -230,7 +237,7 @@ sub badusage {
     exit 8;
 }
 
-sub helponly () {
+sub cmd_help () {
     print $helpmsg or die $!;
     exit 0;
 }
@@ -247,6 +254,7 @@ our %defcfg = ('dgit.default.distro' => 'debian',
               'dgit-distro.debian.sshdakls-host' => 'coccia.debian.org',
               'dgit-distro.debian.sshdakls-dir' =>
                   '/srv/ftp-master.debian.org/ftp/dists',
+              'dgit-distro.debian.upload-host' => 'ftp-master', # for dput
               'dgit-distro.debian.mirror' => 'http://ftp.debian.org/debian/');
 
 sub cfg {
@@ -332,7 +340,7 @@ sub getfield ($$) {
 sub parsechangelog {
     my $c = Dpkg::Control::Hash->new();
     my $p = new IO::Handle;
-    my @cmd = (qw(dpkg-parsechangelog));
+    my @cmd = (qw(dpkg-parsechangelog), @_);
     open $p, '-|', @cmd or die $!;
     $c->parse($p);
     $?=0; $!=0; close $p or failedcmd @cmd;
@@ -443,17 +451,15 @@ sub get_archive_dsc () {
        $dscurl = access_cfg('mirror').$subpath;
        $dscdata = url_get($dscurl);
        next unless defined $dscdata;
-       $dscurl = access_cfg('mirror').$subpath;
-       $dscdata = url_get($dscurl);
        my $dscfh = new IO::File \$dscdata, '<' or die $!;
        print DEBUG Dumper($dscdata) if $debug>1;
        $dsc = parsecontrolfh($dscfh,$dscurl, allow_pgp=>1);
        print DEBUG Dumper($dsc) if $debug>1;
        my $fmt = getfield $dsc, 'Format';
        fail "unsupported source format $fmt, sorry" unless $format_ok{$fmt};
-       return $dsc;
+       return;
     }
-    return undef;
+    $dsc = undef;
 }
 
 sub check_for_git () {
@@ -484,7 +490,7 @@ sub create_remote_git_repo () {
     }
 }
 
-our ($dsc_hash,$upload_hash);
+our ($dsc_hash,$lastpush_hash);
 
 our $ud = '.git/dgit/unpack';
 
@@ -513,21 +519,19 @@ sub mktree_in_ud_from_only_subdir () {
 }
 
 sub dsc_files_info () {
-    foreach my $csum (qw(Sha256 Sha1 MD5)) {
-       my $fname = $csum eq 'MD5' ? 'Files' : "Checksums-$csum";
+    foreach my $csumi (['Checksums-Sha256','Digest::SHA', 'new(256)'],
+                      ['Checksums-Sha1',  'Digest::SHA', 'new(1)'],
+                      ['Files',           'Digest::MD5', 'new()']) {
+       my ($fname, $module, $method) = @$csumi;
        my $field = $dsc->{$fname};
        next unless defined $field;
-       my $digest = uc $csum;
-       if (!eval "use Digest::$digest; 1;") {
-           print DEBUG "ignoring $fname because $@\n";
-           next;
-       }
+       eval "use $module; 1;" or die $@;
        my @out;
        foreach (split /\n/, $field) {
            next unless m/\S/;
            m/^(\w+) (\d+) (\S+)$/ or
                fail "could not parse .dsc $fname line \`$_'";
-           my $digester = eval "Digest::$digest->new;" or die $@;
+           my $digester = eval "$module"."->$method;" or die $@;
            push @out, {
                Hash => $1,
                Bytes => $2,
@@ -597,8 +601,8 @@ END
     my $outputhash = make_commit qw(../commit.tmp);
     my $cversion = getfield $clogp, 'Version';
     print "synthesised git commit from .dsc $cversion\n";
-    if ($upload_hash) {
-       runcmd @git, qw(reset --hard), $upload_hash;
+    if ($lastpush_hash) {
+       runcmd @git, qw(reset --hard), $lastpush_hash;
        runcmd qw(sh -ec), 'dpkg-parsechangelog >>../changelogold.tmp';
        my $oldclogp = parsecontrol('../changelogold.tmp','previous changelog');
        my $oversion = getfield $oldclogp, 'Version';
@@ -609,7 +613,7 @@ END
            open C, ">../commit2.tmp" or die $!;
            print C <<END or die $!;
 tree $tree
-parent $upload_hash
+parent $lastpush_hash
 parent $outputhash
 author $authline
 committer $authline
@@ -624,9 +628,9 @@ Version actually in archive:    $cversion (older)
 Last allegedly pushed/uploaded: $oversion (newer or same)
 $later_warning_msg
 END
-            $outputhash = $upload_hash;
+            $outputhash = $lastpush_hash;
         } else {
-           $outputhash = $upload_hash;
+           $outputhash = $lastpush_hash;
        }
     }
     chdir '../../../..' or die $!;
@@ -639,10 +643,20 @@ END
 }
 
 sub ensure_we_have_orig () {
-    foreach my $f (dsc_files()) {
+    foreach my $fi (dsc_files_info()) {
+       my $f = $fi->{Filename};
        next unless is_orig_file($f);
-       if (stat "../$f") {
-           die "$f ?" unless -f _;
+       if (open F, "<", "../$f") {
+           $fi->{Digester}->reset();
+           $fi->{Digester}->addfile(*F);
+           F->error and die $!;
+           my $got = $fi->{Digester}->hexdigest();
+           $got eq $fi->{Hash} or
+               fail "existing file $f has hash $got but .dsc".
+                   " demands hash $fi->{Hash}".
+                   " (perhaps you should delete this file?)";
+           print "using existing $f\n";
+           next;
        } else {
            die "$f $!" unless $!==&ENOENT;
        }
@@ -651,8 +665,7 @@ sub ensure_we_have_orig () {
        $origurl .= "/$f";
        die "$f ?" unless $f =~ m/^${package}_/;
        die "$f ?" if $f =~ m#/#;
-       runcmd_ordryrun qw(sh -ec),'cd ..; exec "$@"','x',
-           @dget,'--',$origurl;
+       runcmd_ordryrun shell_cmd 'cd ..', @dget,'--',$origurl;
     }
 }
 
@@ -673,69 +686,85 @@ sub is_fast_fwd ($$) {
 }
 
 sub git_fetch_us () {
-    badusage "cannot dry run with fetch" if $dryrun;
-    runcmd @git, qw(fetch),access_giturl(),fetchspec();
+    runcmd_ordryrun @git, qw(fetch),access_giturl(),fetchspec();
 }
 
 sub fetch_from_archive () {
     # ensures that lrref() is what is actually in the archive,
     #  one way or another
-    get_archive_dsc() or return 0;
-    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 = $&;
-       print "last upload to archive specified git hash\n";
+    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 = $&;
+           print "last upload to archive specified git hash\n";
+       } else {
+           print "last upload to archive has NO git hash\n";
+       }
     } else {
-       print "last upload to archive has NO git hash\n";
+       print "no version available from the archive\n";
     }
 
     my $lrref_fn = ".git/".lrref();
     if (open H, $lrref_fn) {
-       $upload_hash = <H>;
-       chomp $upload_hash;
-       die "$lrref_fn $upload_hash ?" unless $upload_hash =~ m/^\w+$/;
+       $lastpush_hash = <H>;
+       chomp $lastpush_hash;
+       die "$lrref_fn $lastpush_hash ?" unless $lastpush_hash =~ m/^\w+$/;
     } elsif ($! == &ENOENT) {
-       $upload_hash = '';
+       $lastpush_hash = '';
     } else {
        die "$lrref_fn $!";
     }
-    print DEBUG "previous reference hash=$upload_hash\n";
+    print DEBUG "previous reference hash=$lastpush_hash\n";
     my $hash;
     if (defined $dsc_hash) {
        fail "missing git history even though dsc has hash -".
            " could not find commit $dsc_hash".
            " (should be in ".access_giturl()."#".rrref().")"
-           unless $upload_hash;
+           unless $lastpush_hash;
        $hash = $dsc_hash;
        ensure_we_have_orig();
-       if ($dsc_hash eq $upload_hash) {
-       } elsif (is_fast_fwd($dsc_hash,$upload_hash)) {
+       if ($dsc_hash eq $lastpush_hash) {
+       } elsif (is_fast_fwd($dsc_hash,$lastpush_hash)) {
            print STDERR <<END or die $!;
 
 Git commit in archive is behind the last version allegedly pushed/uploaded.
 Commit referred to by archive:  $dsc_hash
-Last allegedly pushed/uploaded: $upload_hash
+Last allegedly pushed/uploaded: $lastpush_hash
 $later_warning_msg
 END
-           $hash = $upload_hash;
+           $hash = $lastpush_hash;
        } else {
            fail "archive's .dsc refers to ".$dsc_hash.
-               " but this is an ancestor of ".$upload_hash;
+               " but this is an ancestor of ".$lastpush_hash;
        }
-    } else {
+    } elsif ($dsc) {
        $hash = generate_commit_from_dsc();
+    } elsif ($lastpush_hash) {
+       # only in git, not in the archive yet
+       $hash = $lastpush_hash;
+       print STDERR <<END or die $!;
+
+Package not found in the archive, but has allegedly been
+pushed/uploaded using dgit.
+$later_warning_msg
+END
+    } else {
+       print DEBUG "nothing found!\n";
+       return 0;
     }
     print DEBUG "current hash=$hash\n";
-    if ($upload_hash) {
+    if ($lastpush_hash) {
        fail "not fast forward on last upload branch!".
            " (archive's version left in DGIT_ARCHIVE)"
-           unless is_fast_fwd($upload_hash, $hash);
+           unless is_fast_fwd($lastpush_hash, $hash);
     }
-    if ($upload_hash ne $hash) {
+    if ($lastpush_hash ne $hash) {
        my @upd_cmd = (@git, qw(update-ref -m), 'dgit fetch', lrref(), $hash);
        if (!$dryrun) {
            cmdoutput @upd_cmd;
@@ -761,7 +790,7 @@ sub clone ($) {
     if (check_for_git()) {
        print "fetching existing git history\n";
        git_fetch_us();
-       runcmd @git, qw(fetch origin);
+       runcmd_ordryrun @git, qw(fetch origin);
     } else {
        print "starting new git history\n";
     }
@@ -905,6 +934,7 @@ sub dopush () {
        create_remote_git_repo();
     }
     runcmd_ordryrun @git, qw(push),access_giturl(),"HEAD:".rrref();
+    runcmd_ordryrun @git, qw(update-ref -m), 'dgit push', lrref(), 'HEAD';
     if (!$dryrun) {
        rename "../$dscfn.tmp","../$dscfn" or die "$dscfn $!";
     } else {
@@ -915,7 +945,7 @@ sub dopush () {
            $keyid = access_cfg('keyid','RETURN-UNDEF');
        }
        my @tag_cmd = (@git, qw(tag -s -m),
-                      "Release $dversion for $csuite [dgit]");
+                      "$package release $dversion for $csuite [dgit]");
        push @tag_cmd, qw(-u),$keyid if defined $keyid;
        push @tag_cmd, $tag;
        runcmd_ordryrun @tag_cmd;
@@ -1048,6 +1078,7 @@ sub build_maybe_quilt_fixup () {
     my $ncommits = 3;
     my $patchname = "auto-$version-$headref-$time";
     my $msg = cmdoutput @git, qw(log), "-n$ncommits";
+    mkpath '.git/dgit';
     my $descfn = ".git/dgit/quilt-description.tmp";
     open O, '>', $descfn or die "$descfn: $!";
     $msg =~ s/\n/\n /g;
@@ -1191,6 +1222,11 @@ sub cmd_quilt_fixup {
     build_maybe_quilt_fixup();
 }
 
+sub cmd_version {
+    print "dgit version $our_version\n" or die $!;
+    exit 0;
+}
+
 sub parseopts () {
     my $om;
     while (@ARGV) {
@@ -1203,7 +1239,9 @@ sub parseopts () {
            } elsif (m/^--no-sign$/) {
                $sign=0;
            } elsif (m/^--help$/) {
-               helponly();
+               cmd_help();
+           } elsif (m/^--version$/) {
+               cmd_version();
            } elsif (m/^--new$/) {
                $new_package=1;
            } elsif (m/^--(\w+)=(.*)/s &&
@@ -1233,7 +1271,7 @@ sub parseopts () {
                if (s/^-n/-/) {
                    $dryrun=1;
                } elsif (s/^-h/-/) {
-                   helponly();
+                   cmd_help();
                } elsif (s/^-D/-/) {
                    open DEBUG, ">&STDERR" or die $!;
                    $debug++;