chiark / gitweb /
Split brain: Check that archive supports "maint", earlier
[dgit.git] / dgit
diff --git a/dgit b/dgit
index 483c864dd88d6538f51f45836cf34e8cb994447d..0cf854d3e9b98f48767068cb756be50c64e68b1f 100755 (executable)
--- a/dgit
+++ b/dgit
@@ -63,7 +63,7 @@ our $existing_package = 'dpkg';
 our $cleanmode;
 our $changes_since_version;
 our $rmchanges;
-our $overwrite_version;
+our $overwrite_version; # undef: not specified; '': check changelog
 our $quilt_mode;
 our $quilt_modes_re = 'linear|smash|auto|nofix|nocheck|gbp|dpm|unapplied';
 our $we_are_responder;
@@ -537,11 +537,13 @@ our %defcfg = ('dgit.default.distro' => 'debian',
               # old means "repo server accepts pushes with old dgit tags"
               # new means "repo server accepts pushes with new dgit tags"
               # maint means "repo server accepts split brain pushes"
+              # hist means "repo server may have old pushes without new tag"
+              #   ("hist" is implied by "old")
               'dgit-distro.debian.archive-query' => 'ftpmasterapi:',
               'dgit-distro.debian.git-check' => 'url',
               'dgit-distro.debian.git-check-suffix' => '/info/refs',
               'dgit-distro.debian.new-private-pushers' => 't',
-              'dgit-distro.debian.dgit-tag-format' => 'old',
+              'dgit-distro.debian.dgit-tag-format' => 'new',
               'dgit-distro.debian/push.git-url' => '',
               'dgit-distro.debian/push.git-host' => 'push.dgit.debian.org',
               'dgit-distro.debian/push.git-user-force' => 'dgit',
@@ -870,11 +872,11 @@ sub getfield ($$) {
     my ($dctrl,$field) = @_;
     my $v = $dctrl->{$field};
     return $v if defined $v;
-    fail "missing field $field in ".$v->get_option('name');
+    fail "missing field $field in ".$dctrl->get_option('name');
 }
 
 sub parsechangelog {
-    my $c = Dpkg::Control::Hash->new();
+    my $c = Dpkg::Control::Hash->new(name => 'parsed changelog');
     my $p = new IO::Handle;
     my @cmd = (qw(dpkg-parsechangelog), @_);
     open $p, '-|', @cmd or die $!;
@@ -2402,31 +2404,59 @@ sub pseudomerge_version_check ($$) {
     my $i_arch_v = [ (getfield $arch_clogp, 'Version'),
                     'version currently in archive' ];
     if (defined $overwrite_version) {
-       infopair_cond_equal([ $overwrite_version, '--overwrite= version' ],
-                           $i_arch_v);
+       if (length $overwrite_version) {
+           infopair_cond_equal([ $overwrite_version,
+                                 '--overwrite= version' ],
+                               $i_arch_v);
+       } else {
+           my $v = $i_arch_v->[0];
+           progress "Checking package changelog for archive version $v ...";
+           eval {
+               my @xa = ("-f$v", "-t$v");
+               my $vclogp = parsechangelog @xa;
+               my $cv = [ (getfield $vclogp, 'Version'),
+                          "Version field from dpkg-parsechangelog @xa" ];
+               infopair_cond_equal($i_arch_v, $cv);
+           };
+           if ($@) {
+               $@ =~ s/^dgit: //gm;
+               fail "$@".
+                   "Perhaps debian/changelog does not mention $v ?";
+           }
+       }
     }
     
     printdebug "pseudomerge_version_check i_arch_v @$i_arch_v\n";
     return $i_arch_v;
 }
 
-sub pseudomerge_make_commit ($$$$$) {
-    my ($clogp, $dgitview, $archive_hash, $i_arch_v, $msg) = @_;
+sub pseudomerge_make_commit ($$$$ $$) {
+    my ($clogp, $dgitview, $archive_hash, $i_arch_v,
+       $msg_cmd, $msg_msg) = @_;
     progress "Declaring that HEAD inciudes all changes in $i_arch_v->[0]...";
 
     my $tree = cmdoutput qw(git rev-parse), "${dgitview}:";
     my $authline = clogp_authline $clogp;
 
+    chomp $msg_msg;
+    $msg_cmd .=
+       !defined $overwrite_version ? ""
+       : !length  $overwrite_version ? " --overwrite"
+       : " --overwrite=".$overwrite_version;
+
     mkpath '.git/dgit';
     my $pmf = ".git/dgit/pseudomerge";
     open MC, ">", $pmf or die "$pmf $!";
-    print MC <<END, $msg or die $!;
+    print MC <<END or die $!;
 tree $tree
 parent $dgitview
 parent $archive_hash
 author $authline
 commiter $authline
 
+$msg_msg
+
+[$msg_cmd]
 END
     close MC or die $!;
 
@@ -2475,20 +2505,48 @@ sub splitbrain_pseudomerge ($$$$) {
 
     my $r = pseudomerge_make_commit
        $clogp, $dgitview, $archive_hash, $i_arch_v,
+       "dgit --quilt=$quilt_mode",
        (defined $overwrite_version ? <<END_OVERWR : <<END_MAKEFF);
 Declare fast forward from $overwrite_version
-
-[dgit --quilt=$quilt_mode --overwrite-version=$overwrite_version]
 END_OVERWR
 Make fast forward from $i_arch_v->[0]
-
-[dgit --quilt=$quilt_mode]
 END_MAKEFF
 
     progress "Made pseudo-merge of $i_arch_v->[0] into dgit view.";
     return $r;
 }      
 
+sub plain_overwrite_pseudomerge ($$$) {
+    my ($clogp, $head, $archive_hash) = @_;
+
+    printdebug "plain_overwrite_pseudomerge...";
+
+    my $i_arch_v = pseudomerge_version_check($clogp, $archive_hash);
+
+    my @tagformats = access_cfg_tagformats();
+    my @t_overwr =
+       map { $_->($i_arch_v->[0], access_basedistro) }
+       (grep { m/^(?:old|hist)$/ } @tagformats)
+       ? \&debiantags : \&debiantag_new;
+    my $i_overwr = infopair_lrf_tag_lookup \@t_overwr, "previous version tag";
+    my $i_archive = [ $archive_hash, "current archive contents" ];
+
+    infopair_cond_equal($i_overwr, $i_archive);
+
+    return $head if is_fast_fwd $archive_hash, $head;
+
+    my $m = "Declare fast forward from $i_arch_v->[0]";
+
+    my $r = pseudomerge_make_commit
+       $clogp, $head, $archive_hash, $i_arch_v,
+       "dgit", $m;
+
+    runcmd @git, qw(update-ref -m), $m, 'HEAD', $r, $head;
+
+    progress "Make pseudo-merge of $i_arch_v->[0] into your HEAD.";
+    return $r;
+}
+
 sub push_parse_changelog ($) {
     my ($clogpfn) = @_;
 
@@ -2716,6 +2774,12 @@ END
        }
     }
 
+    if (defined $overwrite_version && !defined $maintviewhead) {
+       $dgithead = plain_overwrite_pseudomerge($clogp,
+                                               $dgithead,
+                                               $archive_hash);
+    }
+
     check_not_dirty();
 
     my $forceflag = '';
@@ -2727,10 +2791,10 @@ END
        } else {
            fail "dgit push: HEAD is not a descendant".
                " of the archive's version.\n".
-               "dgit: To overwrite its contents,".
-               " use git merge -s ours ".lrref().".\n".
-               "dgit: To rewind history, if permitted by the archive,".
-               " use --deliberately-not-fast-forward";
+               "To overwrite the archive's contents,".
+               " pass --overwrite[=VERSION].\n".
+               "To rewind history, if permitted by the archive,".
+               " use --deliberately-not-fast-forward.";
        }
     }
 
@@ -3632,6 +3696,16 @@ sub build_maybe_quilt_fixup () {
 
     check_for_vendor_patches();
 
+    if (quiltmode_splitbrain) {
+       foreach my $needtf (qw(new maint)) {
+           next if grep { $_ eq $needtf } access_cfg_tagformats;
+           fail <<END
+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();
     my $headref = git_rev_parse('HEAD');
 
@@ -4519,6 +4593,12 @@ sub parseopts () {
            } elsif (m/^--no-rm-on-error$/s) {
                push @ropts, $_;
                $rmonerror = 0;
+           } elsif (m/^--overwrite$/s) {
+               push @ropts, $_;
+               $overwrite_version = '';
+           } elsif (m/^--overwrite=(.+)$/s) {
+               push @ropts, $_;
+               $overwrite_version = $1;
            } elsif (m/^--(no-)?rm-old-changes$/s) {
                push @ropts, $_;
                $rmchanges = !$1;