chiark / gitweb /
Merge branch 'buster'
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Tue, 2 Jul 2019 13:36:04 +0000 (14:36 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Tue, 2 Jul 2019 13:36:04 +0000 (14:36 +0100)
30 files changed:
Debian/Dgit.pm
debian/tests/control
dgit
dgit-maint-debrebase.7.pod
dgit-maint-merge.7.pod
dgit.1
dgit.7
git-debrebase
git-debrebase.1.pod
infra/dgit-repos-policy-debian
infra/dgit-repos-server
tests/enumerate-tests
tests/lib
tests/lib-baredebian [new file with mode: 0644]
tests/setup/baredebian [new file with mode: 0755]
tests/tests/baredebian-multitar [new file with mode: 0755]
tests/tests/baredebian-plusgit [new file with mode: 0755]
tests/tests/baredebian-push [new file with mode: 0755]
tests/tests/baredebian-tarball [new file with mode: 0755]
tests/tests/debpolicy-taintrm [new file with mode: 0755]
tests/tests/drs-push-rejects
tests/tests/forcesplit-linear [new file with mode: 0755]
tests/tests/forcesplit-overwrite [new file with mode: 0755]
tests/tests/newtag-clone-nogit [deleted file]
tests/tests/oldnewtagalt [deleted file]
tests/tests/oldtag-clone-nogit [deleted file]
tests/tests/overwrite-splitbrains
tests/tests/quilt-gbp
tests/tests/quilt-splitbrains
tests/tests/rpush-quilt

index 61476d9f8e18b8adc6dacb7a8926e68c604fc58b..887fbb599caa978c5ce3c499487dec73a3cfab20 100644 (file)
@@ -43,9 +43,10 @@ BEGIN {
     @ISA         = qw(Exporter);
     @EXPORT      = qw(setup_sigwarn forkcheck_setup forkcheck_mainprocess
                      dep14_version_mangle
-                      debiantags debiantag_old debiantag_new
+                      debiantags debiantag_new
                      debiantag_maintview
                      upstreamversion
+                     upstream_commitish_search resolve_upstream_version
                      stripepoch source_file_leafname is_orig_file_of_p_v
                      server_branch server_ref
                       stat_exists link_ltarget rename_link_xf
@@ -59,7 +60,9 @@ BEGIN {
                       git_for_each_tag_referring is_fast_fwd
                      git_check_unmodified
                      git_reflog_action_msg  git_update_ref_cmd
-                     make_commit_text
+                     rm_subdir_cached read_tree_subdir
+                     read_tree_debian read_tree_upstream
+                     make_commit hash_commit hash_commit_text
                      reflog_cache_insert reflog_cache_lookup
                       $package_re $component_re $deliberately_re
                      $distro_re $versiontag_re $series_filename_re
@@ -233,11 +236,6 @@ sub dep14_version_mangle ($) {
     return $v;
 }
 
-sub debiantag_old ($$) { 
-    my ($v,$distro) = @_;
-    return "$distro/". dep14_version_mangle $v;
-}
-
 sub debiantag_new ($$) { 
     my ($v,$distro) = @_;
     return "archive/$distro/".dep14_version_mangle $v;
@@ -250,7 +248,7 @@ sub debiantag_maintview ($$) {
 
 sub debiantags ($$) {
     my ($version,$distro) = @_;
-    map { $_->($version, $distro) } (\&debiantag_new, \&debiantag_old);
+    map { $_->($version, $distro) } (\&debiantag_new, \&debiantag_maintview);
 }
 
 sub stripepoch ($) {
@@ -622,6 +620,41 @@ sub git_check_unmodified () {
     }
 }
 
+sub upstream_commitish_search ($$) {
+    my ($upstream_version, $tried) = @_;
+    # todo: at some point maybe use git-deborig to do this
+    foreach my $tagpfx ('', 'v', 'upstream/') {
+       my $tag = $tagpfx.(dep14_version_mangle $upstream_version);
+       my $new_upstream = git_get_ref "refs/tags/$tag";
+       push @$tried, $tag;
+       return $new_upstream if length $new_upstream;
+    }
+}
+
+sub resolve_upstream_version ($$) {
+    my ($new_upstream, $upstream_version) = @_;
+
+    my $used = $new_upstream;
+    my $message = __ 'using specified upstream commitish';
+    if (!defined $new_upstream) {
+       my @tried;
+       $new_upstream = upstream_commitish_search $upstream_version, \@tried;
+       if (!length $new_upstream) {
+           fail f_
+               "Could not determine appropriate upstream commitish.\n".
+               " (Tried these tags: %s)\n".
+               " Check version, and specify upstream commitish explicitly.",
+               "@tried";
+       }
+       $used = $tried[-1];
+       $message = f_ 'using upstream from git tag %s', $used;
+    }
+    $new_upstream = git_rev_parse $new_upstream;
+
+    return ($new_upstream, $used, $message);
+    # used is a human-readable idea of what we found
+}
+
 sub is_fast_fwd ($$) {
     my ($ancestor,$child) = @_;
     my @cmd = (qw(git merge-base), $ancestor, $child);
@@ -649,6 +682,39 @@ sub git_update_ref_cmd {
     return qw(git update-ref -m), $msg, @_;
 }
 
+sub rm_subdir_cached ($) {
+    my ($subdir) = @_;
+    runcmd qw(git rm --quiet -rf --cached --ignore-unmatch), $subdir;
+}
+
+sub read_tree_subdir ($$) {
+    my ($subdir, $new_tree_object) = @_;
+    # If $new_tree_object is '', the subtree is deleted.
+    confess unless defined $new_tree_object;
+    rm_subdir_cached $subdir;
+    runcmd qw(git read-tree), "--prefix=$subdir/", $new_tree_object
+       if length $new_tree_object;
+}
+
+sub read_tree_debian ($) {
+    my ($treeish) = @_;
+    read_tree_subdir 'debian', "$treeish:debian";
+    rm_subdir_cached 'debian/patches';
+}
+
+sub read_tree_upstream ($;$$) {
+    my ($treeish, $keep_patches, $tree_with_debian) = @_;
+    # if $tree_with_debian is supplied, will use that for debian/
+    # otherwise will save and restore it.  If $tree_with_debian
+    # is '' then debian/ is deleted.
+    my $debian =
+       defined $tree_with_debian ? "$tree_with_debian:debian"
+       : cmdoutput qw(git write-tree --prefix=debian/);
+    runcmd qw(git read-tree), $treeish;
+    read_tree_subdir 'debian', $debian;
+    rm_subdir_cached 'debian/patches' unless $keep_patches;
+}
+
 sub changedir ($) {
     my ($newdir) = @_;
     printdebug "CD $newdir\n";
@@ -771,7 +837,21 @@ sub parsechangelog_loop ($$$) {
     close CLOGS or $?==SIGPIPE or failedcmd @$clogcmd;
 }      
 
-sub make_commit_text ($) {
+sub make_commit ($$) {
+    my ($parents, $message_paras) = @_;
+    my $tree = cmdoutput qw(git write-tree);
+    my @cmd = (qw(git commit-tree), $tree);
+    push @cmd, qw(-p), $_ foreach @$parents;
+    push @cmd, qw(-m), $_ foreach @$message_paras;
+    return cmdoutput @cmd;
+}
+
+sub hash_commit ($) {
+    my ($file) = @_;
+    return cmdoutput qw(git hash-object -w -t commit), $file;
+}
+
+sub hash_commit_text ($) {
     my ($text) = @_;
     my ($out, $in);
     my @cmd = (qw(git hash-object -w -t commit --stdin));
@@ -812,7 +892,7 @@ sub reflog_cache_insert ($$$) {
        # git update-ref doesn't always update, in this case.  *sigh*
        my $authline = (ucfirst _us()).
            ' <'._us().'@example.com> 1000000000 +0000';
-       my $dummy = make_commit_text <<ENDU.(__ <<END);
+       my $dummy = hash_commit_text <<ENDU.(__ <<END);
 tree $tree
 parent $value
 author $authline
index 7186da1f9dd3f5c2b43ac199da3941edceb511b9..2b495accf219fda8f06ae36e972f45d0b34f64fc 100644 (file)
@@ -1,3 +1,7 @@
+Tests: baredebian-multitar baredebian-plusgit baredebian-push baredebian-tarball
+Tests-Directory: tests/tests
+Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, faketime, quilt, git-debrebase, git-buildpackage, libdpkg-perl, libgit-wrapper-perl, liblist-compare-perl, libstring-shellquote-perl, libtry-tiny-perl
+
 Tests: build-modes-gbp
 Tests-Directory: tests/tests
 Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, faketime, git-buildpackage
@@ -68,7 +72,7 @@ Tests: trustingpolicy-replay
 Tests-Directory: tests/tests
 Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, faketime, dput-ng
 
-Tests: absurd-gitapply badcommit-rewrite build-modes build-modes-long build-modes-source checkout clone-clogsigpipe clone-gitnosuite clone-nogit debpolicy-dbretry debpolicy-newreject debpolicy-quilt-gbp defdistro-rpush defdistro-setup distropatches-reject dpkgsourceignores-correct drs-clone-nogit drs-push-masterupdate drs-push-rejects dsd-clone-nogit dsd-divert fetch-localgitonly fetch-somegit-notlast gbp-orig gitconfig gitworktree import-dsc import-linkorigs import-maintmangle import-native import-nonnative import-tarbomb inarchivecopy mismatches-contents mismatches-dscchanges multisuite newtag-clone-nogit oldnewtagalt oldtag-clone-nogit orig-include-exclude orig-include-exclude-chkquery overwrite-chkclog overwrite-junk overwrite-splitbrains overwrite-version pbuilder protocol-compat push-buildproductsdir push-newpackage push-newrepeat push-nextdgit push-source push-source-with-changes quilt quilt-gbp quilt-gbp-build-modes quilt-singlepatch quilt-splitbrains quilt-useremail rpush rpush-quilt sourceonlypolicy tag-updates unrepresentable version-opt
+Tests: absurd-gitapply badcommit-rewrite build-modes build-modes-long build-modes-source checkout clone-clogsigpipe clone-gitnosuite clone-nogit debpolicy-dbretry debpolicy-newreject debpolicy-quilt-gbp debpolicy-taintrm defdistro-rpush defdistro-setup distropatches-reject dpkgsourceignores-correct drs-clone-nogit drs-push-masterupdate drs-push-rejects dsd-clone-nogit dsd-divert fetch-localgitonly fetch-somegit-notlast forcesplit-linear forcesplit-overwrite gbp-orig gitconfig gitworktree import-dsc import-linkorigs import-maintmangle import-native import-nonnative import-tarbomb inarchivecopy mismatches-contents mismatches-dscchanges multisuite orig-include-exclude orig-include-exclude-chkquery overwrite-chkclog overwrite-junk overwrite-splitbrains overwrite-version pbuilder protocol-compat push-buildproductsdir push-newpackage push-newrepeat push-nextdgit push-source push-source-with-changes quilt quilt-gbp quilt-gbp-build-modes quilt-singlepatch quilt-splitbrains quilt-useremail rpush rpush-quilt sourceonlypolicy tag-updates unrepresentable version-opt
 Tests-Directory: tests/tests
 Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, faketime
 
diff --git a/dgit b/dgit
index f1b322ad68450760d395ce4a1e71e0fa9ef29635..2d235a633fa9433d5febcf8b78ba2c9d7a62a4ec 100755 (executable)
--- a/dgit
+++ b/dgit
@@ -53,7 +53,7 @@ use Debian::Dgit;
 our $our_version = 'UNRELEASED'; ###substituted###
 our $absurdity = undef; ###substituted###
 
-our @rpushprotovsn_support = qw(4 3 2); # 4 is new tag format
+our @rpushprotovsn_support = qw(4 5); # 5 drops tag format specification
 our $protovsn;
 
 our $cmd;
@@ -79,16 +79,19 @@ our $changes_since_version;
 our $rmchanges;
 our $overwrite_version; # undef: not specified; '': check changelog
 our $quilt_mode;
-our $quilt_modes_re = 'linear|smash|auto|nofix|nocheck|gbp|dpm|unapplied';
+our $quilt_upstream_commitish;
+our $quilt_upstream_commitish_used;
+our $quilt_upstream_commitish_message;
+our $quilt_options_re = 'gbp|dpm|baredebian(?:\+tarball|\+git)?';
+our $quilt_modes_re = "linear|smash|auto|nofix|nocheck|unapplied|$quilt_options_re";
+our $splitview_mode;
+our $splitview_modes_re = qr{auto|always|never};
 our $dodep14tag;
 our %internal_object_save;
 our $we_are_responder;
 our $we_are_initiator;
 our $initiator_tempdir;
 our $patches_applied_dirtily = 00;
-our $tagformat_want;
-our $tagformat;
-our $tagformatfn;
 our $chase_dsc_distro=1;
 
 our %forceopts = map { $_=>0 }
@@ -175,7 +178,31 @@ our $keyid;
 autoflush STDOUT 1;
 
 our $supplementary_message = '';
-our $split_brain = 0;
+our $made_split_brain = 0;
+our $do_split_brain;
+
+# Interactions between quilt mode and split brain
+# (currently, split brain only implemented iff
+#  madformat_wantfixup && quiltmode_splitting)
+#
+#   source format        sane           `3.0 (quilt)'
+#                                       madformat_wantfixup()
+#
+#   quilt mode                          normal              quiltmode
+#                                       (eg linear)         _splitbrain
+#
+#   ------------      ------------------------------------------------
+#
+#   no split          no q cache        no q cache          forbidden,
+#     brain           PM on master      q fixup on master   prevented
+#   !do_split_brain()                    PM on master
+#
+#   split brain       no q cache        q fixup cached, to dgit view
+#                     PM in dgit view   PM in dgit view
+#
+# PM = pseudomerge to make ff, due to overwrite (or split view)
+# "no q cache" = do not record in cache on build, do not check cache
+# `3.0 (quilt)' with --quilt=nocheck is treated as sane format
 
 END {
     local ($@, $?);
@@ -193,11 +220,6 @@ if (!defined $absurdity) {
     $absurdity =~ s{/[^/]+$}{/absurd} or die;
 }
 
-sub debiantag ($$) {
-    my ($v,$distro) = @_;
-    return $tagformatfn->($v, $distro);
-}
-
 sub madformat ($) { $_[0] eq '3.0 (quilt)' }
 
 sub lbranch () { return "$branchprefix/$csuite"; }
@@ -275,10 +297,12 @@ sub deliberately_not_fast_forward () {
     }
 }
 
-sub quiltmode_splitbrain () {
-    $quilt_mode =~ m/gbp|dpm|unapplied/;
+sub quiltmode_splitting () {
+    $quilt_mode =~ m/gbp|dpm|unapplied|baredebian/;
 }
 
+sub do_split_brain () { !!($do_split_brain // confess) }
+
 sub opts_opt_multi_cmd {
     my $extra = shift;
     my @cmd;
@@ -424,7 +448,7 @@ sub branch_is_gdr ($) {
 #  > progress NBYTES
 #  [NBYTES message]
 #
-#  > supplementary-message NBYTES          # $protovsn >= 3
+#  > supplementary-message NBYTES
 #  [NBYTES message]
 #
 # main sequence:
@@ -444,7 +468,7 @@ sub branch_is_gdr ($) {
 #
 #  > param head DGIT-VIEW-HEAD
 #  > param csuite SUITE
-#  > param tagformat old|new
+#  > param tagformat new              # $protovsn == 4
 #  > param maint-view MAINT-VIEW-HEAD
 #
 #  > param buildinfo-filename P_V_X.buildinfo   # zero or more times
@@ -709,7 +733,6 @@ our %defcfg = ('dgit.default.distro' => 'debian',
               'dgit.default.archive-query' => 'madison:',
               'dgit.default.sshpsql-dbname' => 'service=projectb',
               'dgit.default.aptget-components' => 'main',
-              'dgit.default.dgit-tag-format' => 'new,old,maint',
               'dgit.default.source-only-uploads' => 'ok',
               'dgit.dsc-url-proto-ok.http'    => 'true',
               'dgit.dsc-url-proto-ok.https'   => 'true',
@@ -919,6 +942,20 @@ sub access_forpush () {
     return $access_forpush;
 }
 
+sub default_from_access_cfg ($$$;$) {
+    my ($var, $keybase, $defval, $permit_re) = @_;
+    return if defined $$var;
+
+    $$var = access_cfg("$keybase-newer", 'RETURN-UNDEF');
+    $$var = undef if $$var && $$var !~ m/^$permit_re$/;
+
+    $$var //= access_cfg($keybase, 'RETURN-UNDEF');
+    $$var //= $defval;
+
+    badcfg f_ "unknown %s \`%s'", $keybase, $$var
+       if defined $permit_re and $$var !~ m/$permit_re/;
+}
+
 sub pushing () {
     confess +(__ 'internal error').' '.Dumper($access_forpush)," ?" if
        defined $access_forpush and !$access_forpush;
@@ -936,12 +973,36 @@ sub notpushing () {
     parseopts_late_defaults();
 }
 
+sub determine_whether_split_brain () {
+    my ($format,) = get_source_format();
+
+    {
+       local $access_forpush;
+       default_from_access_cfg(\$splitview_mode, 'split-view', 'auto',
+                               $splitview_modes_re);
+       $do_split_brain = 1 if $splitview_mode eq 'always';
+    }
+
+    printdebug "format $format, quilt mode $quilt_mode\n";
+
+    if (madformat_wantfixup($format) && quiltmode_splitting()) {
+       $splitview_mode ne 'never' or
+           fail f_ "dgit: quilt mode \`%s' (for format \`%s')".
+                   " implies split view, but split-view set to \`%s'",
+                   $quilt_mode, $format, $splitview_mode;
+       $do_split_brain = 1;
+    }
+    $do_split_brain //= 0;
+
+    return ($format);
+}
+
 sub supplementary_message ($) {
     my ($msg) = @_;
     if (!$we_are_responder) {
        $supplementary_message = $msg;
        return;
-    } elsif ($protovsn >= 3) {
+    } else {
        responder_send_command "supplementary-message ".length($msg)
            or confess "$!";
        print PO $msg or confess "$!";
@@ -1643,58 +1704,6 @@ sub archive_query_dummycat ($$) {
 sub file_in_archive_dummycat () { return undef; }
 sub package_not_wholly_new_dummycat () { return undef; }
 
-#---------- tag format handling ----------
-# (untranslated, because everything should be new tag format by now)
-
-sub access_cfg_tagformats () {
-    split /\,/, access_cfg('dgit-tag-format');
-}
-
-sub access_cfg_tagformats_can_splitbrain () {
-    my %y = map { $_ => 1 } access_cfg_tagformats;
-    foreach my $needtf (qw(new maint)) {
-       next if $y{$needtf};
-       return 0;
-    }
-    return 1;
-}
-
-sub need_tagformat ($$) {
-    my ($fmt, $why) = @_;
-    fail "need to use tag format $fmt ($why) but also need".
-       " to use tag format $tagformat_want->[0] ($tagformat_want->[1])".
-       " - no way to proceed"
-       if $tagformat_want && $tagformat_want->[0] ne $fmt;
-    $tagformat_want = [$fmt, $why, $tagformat_want->[2] // 0];
-}
-
-sub select_tagformat () {
-    # sets $tagformatfn
-    return if $tagformatfn && !$tagformat_want;
-    die 'bug' if $tagformatfn && $tagformat_want;
-    # ... $tagformat_want assigned after previous select_tagformat
-
-    my (@supported) = grep { $_ =~ m/^(?:old|new)$/ } access_cfg_tagformats();
-    printdebug "select_tagformat supported @supported\n";
-
-    $tagformat_want //= [ $supported[0], "distro access configuration", 0 ];
-    printdebug "select_tagformat specified @$tagformat_want\n";
-
-    my ($fmt,$why,$override) = @$tagformat_want;
-
-    fail "target distro supports tag formats @supported".
-       " but have to use $fmt ($why)"
-       unless $override
-           or grep { $_ eq $fmt } @supported;
-
-    $tagformat_want = undef;
-    $tagformat = $fmt;
-    $tagformatfn = ${*::}{"debiantag_$fmt"};
-
-    fail "trying to use unknown tag format \`$fmt' ($why) !"
-       unless $tagformatfn;
-}
-
 #---------- archive query entrypoints and rest of program ----------
 
 sub canonicalise_suite () {
@@ -2109,11 +2118,6 @@ END
     }
 }
 
-sub make_commit ($) {
-    my ($file) = @_;
-    return cmdoutput @git, qw(hash-object -w -t commit), $file;
-}
-
 sub clogp_authline ($) {
     my ($clogp) = @_;
     my $author = getfield $clogp, 'Maintainer';
@@ -2267,62 +2271,9 @@ sub dotdot_bpd_transfer_origs ($$$) {
     closedir DD;
 }
 
-sub generate_commits_from_dsc () {
-    # See big comment in fetch_from_archive, below.
-    # See also README.dsc-import.
-    prep_ud();
-    changedir $playground;
-
-    my $bpd_abs = bpd_abs();
-    my $upstreamv = upstreamversion $dsc->{version};
-    my @dfi = dsc_files_info();
-
-    dotdot_bpd_transfer_origs $bpd_abs, $upstreamv,
-       sub { grep { $_->{Filename} eq $_[0] } @dfi };
-
-    foreach my $fi (@dfi) {
-       my $f = $fi->{Filename};
-       die "$f ?" if $f =~ m#/|^\.|\.dsc$|\.tmp$#;
-       my $upper_f = "$bpd_abs/$f";
-
-       printdebug "considering reusing $f: ";
-
-       if (link_ltarget "$upper_f,fetch", $f) {
-           printdebug "linked (using ...,fetch).\n";
-       } elsif ((printdebug "($!) "),
-                $! != ENOENT) {
-           fail f_ "accessing %s: %s", "$buildproductsdir/$f,fetch", $!;
-       } elsif (link_ltarget $upper_f, $f) {
-           printdebug "linked.\n";
-       } elsif ((printdebug "($!) "),
-                $! != ENOENT) {
-           fail f_ "accessing %s: %s", "$buildproductsdir/$f", $!;
-       } else {
-           printdebug "absent.\n";
-       }
-
-       my $refetched;
-       complete_file_from_dsc('.', $fi, \$refetched)
-           or next;
-
-       printdebug "considering saving $f: ";
-
-       if (rename_link_xf 1, $f, $upper_f) {
-           printdebug "linked.\n";
-       } elsif ((printdebug "($@) "),
-                $! != EEXIST) {
-           fail f_ "saving %s: %s", "$buildproductsdir/$f", $@;
-       } elsif (!$refetched) {
-           printdebug "no need.\n";
-       } elsif (rename_link_xf 1, $f, "$upper_f,fetch") {
-           printdebug "linked (using ...,fetch).\n";
-       } elsif ((printdebug "($@) "),
-                $! != EEXIST) {
-           fail f_ "saving %s: %s", "$buildproductsdir/$f,fetch", $@;
-       } else {
-           printdebug "cannot.\n";
-       }
-    }
+sub import_tarball_tartrees ($$) {
+    my ($upstreamv, $dfi) = @_;
+    # cwd should be the playground
 
     # We unpack and record the orig tarballs first, so that we only
     # need disk space for one private copy of the unpacked source.
@@ -2332,14 +2283,13 @@ sub generate_commits_from_dsc () {
     my @tartrees;
     my $orig_f_base = srcfn $upstreamv, '';
 
-    foreach my $fi (@dfi) {
+    foreach my $fi (@$dfi) {
        # We actually import, and record as a commit, every tarball
        # (unless there is only one file, in which case there seems
        # little point.
 
        my $f = $fi->{Filename};
        printdebug "import considering $f ";
-       (printdebug "only one dfi\n"), next if @dfi == 1;
        (printdebug "not tar\n"), next unless $f =~ m/\.tar(\.\w+)?$/;
        (printdebug "signature\n"), next if $f =~ m/$orig_f_sig_re$/o;
        my $compr_ext = $1;
@@ -2351,6 +2301,7 @@ sub generate_commits_from_dsc () {
                          $compr_ext, $orig_f_part
                         ), "\n";
 
+       my $path = $fi->{Path} // $f;
        my $input = new IO::File $f, '<' or die "$f $!";
        my $compr_pid;
        my @compr_cmd;
@@ -2416,6 +2367,7 @@ sub generate_commits_from_dsc () {
             Sort => (!$orig_f_part         ? 2 :
                     $orig_f_part =~ m/-/g ? 1 :
                                             0),
+            OrigPart => $orig_f_part, # 'orig', 'orig-XXX', or undef 
             F => $f,
             Tree => $tree,
         };
@@ -2429,36 +2381,15 @@ sub generate_commits_from_dsc () {
        $a->{F}    cmp $b->{F}
     } @tartrees;
 
-    my $any_orig = grep { $_->{Orig} } @tartrees;
-
-    my $dscfn = "$package.dsc";
+    @tartrees;
+}
 
-    my $treeimporthow = 'package';
+sub import_tarball_commits ($$) {
+    my ($tartrees, $upstreamv) = @_;
+    # cwd should be a playtree which has a relevant debian/changelog
+    # fills in $tt->{Commit} for each one
 
-    open D, ">", $dscfn or die "$dscfn: $!";
-    print D $dscdata or die "$dscfn: $!";
-    close D or die "$dscfn: $!";
-    my @cmd = qw(dpkg-source);
-    push @cmd, '--no-check' if $dsc_checked;
-    if (madformat $dsc->{format}) {
-       push @cmd, '--skip-patches';
-       $treeimporthow = 'unpatched';
-    }
-    push @cmd, qw(-x --), $dscfn;
-    runcmd @cmd;
-
-    my ($tree,$dir) = mktree_in_ud_from_only_subdir(__ "source package");
-    if (madformat $dsc->{format}) { 
-       check_for_vendor_patches();
-    }
-
-    my $dappliedtree;
-    if (madformat $dsc->{format}) {
-       my @pcmd = qw(dpkg-source --before-build .);
-       runcmd shell_cmd 'exec >/dev/null', @pcmd;
-       rmtree '.pc';
-       $dappliedtree = git_add_write_tree();
-    }
+    my $any_orig = grep { $_->{Orig} } @$tartrees;
 
     my @clogcmd = qw(dpkg-parsechangelog --format rfc822 --all);
     my $clogp;
@@ -2512,20 +2443,22 @@ sub generate_commits_from_dsc () {
     $changes =~ s/^\n//; # Changes: \n
     my $cversion = getfield $clogp, 'Version';
 
-    if (@tartrees) {
+    my $r1authline;
+    if (@$tartrees) {
        $r1clogp //= $clogp; # maybe there's only one entry;
-       my $r1authline = clogp_authline $r1clogp;
+        $r1authline = clogp_authline $r1clogp;
        # Strictly, r1authline might now be wrong if it's going to be
        # unused because !$any_orig.  Whatever.
 
        printdebug "import tartrees authline   $authline\n";
        printdebug "import tartrees r1authline $r1authline\n";
 
-       foreach my $tt (@tartrees) {
+       foreach my $tt (@$tartrees) {
            printdebug "import tartree $tt->{F} $tt->{Tree}\n";
 
-           my $mbody = f_ "Import %s", $tt->{F};
-           $tt->{Commit} = make_commit_text($tt->{Orig} ? <<END_O : <<END_T);
+           # untranslated so that different people's imports are identical
+           my $mbody = sprintf "Import %s", $tt->{F};
+           $tt->{Commit} = hash_commit_text($tt->{Orig} ? <<END_O : <<END_T);
 tree $tt->{Tree}
 author $r1authline
 committer $r1authline
@@ -2545,6 +2478,104 @@ END_T
        }
     }
 
+    return ($authline, $r1authline, $clogp, $changes);
+}
+
+sub generate_commits_from_dsc () {
+    # See big comment in fetch_from_archive, below.
+    # See also README.dsc-import.
+    prep_ud();
+    changedir $playground;
+
+    my $bpd_abs = bpd_abs();
+    my $upstreamv = upstreamversion $dsc->{version};
+    my @dfi = dsc_files_info();
+
+    dotdot_bpd_transfer_origs $bpd_abs, $upstreamv,
+       sub { grep { $_->{Filename} eq $_[0] } @dfi };
+
+    foreach my $fi (@dfi) {
+       my $f = $fi->{Filename};
+       die "$f ?" if $f =~ m#/|^\.|\.dsc$|\.tmp$#;
+       my $upper_f = "$bpd_abs/$f";
+
+       printdebug "considering reusing $f: ";
+
+       if (link_ltarget "$upper_f,fetch", $f) {
+           printdebug "linked (using ...,fetch).\n";
+       } elsif ((printdebug "($!) "),
+                $! != ENOENT) {
+           fail f_ "accessing %s: %s", "$buildproductsdir/$f,fetch", $!;
+       } elsif (link_ltarget $upper_f, $f) {
+           printdebug "linked.\n";
+       } elsif ((printdebug "($!) "),
+                $! != ENOENT) {
+           fail f_ "accessing %s: %s", "$buildproductsdir/$f", $!;
+       } else {
+           printdebug "absent.\n";
+       }
+
+       my $refetched;
+       complete_file_from_dsc('.', $fi, \$refetched)
+           or next;
+
+       printdebug "considering saving $f: ";
+
+       if (rename_link_xf 1, $f, $upper_f) {
+           printdebug "linked.\n";
+       } elsif ((printdebug "($@) "),
+                $! != EEXIST) {
+           fail f_ "saving %s: %s", "$buildproductsdir/$f", $@;
+       } elsif (!$refetched) {
+           printdebug "no need.\n";
+       } elsif (rename_link_xf 1, $f, "$upper_f,fetch") {
+           printdebug "linked (using ...,fetch).\n";
+       } elsif ((printdebug "($@) "),
+                $! != EEXIST) {
+           fail f_ "saving %s: %s", "$buildproductsdir/$f,fetch", $@;
+       } else {
+           printdebug "cannot.\n";
+       }
+    }
+
+    my @tartrees;
+    @tartrees = import_tarball_tartrees($upstreamv, \@dfi)
+       unless @dfi == 1; # only one file in .dsc
+
+    my $dscfn = "$package.dsc";
+
+    my $treeimporthow = 'package';
+
+    open D, ">", $dscfn or die "$dscfn: $!";
+    print D $dscdata or die "$dscfn: $!";
+    close D or die "$dscfn: $!";
+    my @cmd = qw(dpkg-source);
+    push @cmd, '--no-check' if $dsc_checked;
+    if (madformat $dsc->{format}) {
+       push @cmd, '--skip-patches';
+       $treeimporthow = 'unpatched';
+    }
+    push @cmd, qw(-x --), $dscfn;
+    runcmd @cmd;
+
+    my ($tree,$dir) = mktree_in_ud_from_only_subdir(__ "source package");
+    if (madformat $dsc->{format}) { 
+       check_for_vendor_patches();
+    }
+
+    my $dappliedtree;
+    if (madformat $dsc->{format}) {
+       my @pcmd = qw(dpkg-source --before-build .);
+       runcmd shell_cmd 'exec >/dev/null', @pcmd;
+       rmtree '.pc';
+       $dappliedtree = git_add_write_tree();
+    }
+
+    my ($authline, $r1authline, $clogp, $changes) =
+       import_tarball_commits(\@tartrees, $upstreamv);
+
+    my $cversion = getfield $clogp, 'Version';
+
     printdebug "import main commit\n";
 
     open C, ">../commit.tmp" or confess "$!";
@@ -2564,14 +2595,14 @@ $changes
 END
 
     close C or confess "$!";
-    my $rawimport_hash = make_commit qw(../commit.tmp);
+    my $rawimport_hash = hash_commit qw(../commit.tmp);
 
     if (madformat $dsc->{format}) {
        printdebug "import apply patches...\n";
 
        # regularise the state of the working tree so that
        # the checkout of $rawimport_hash works nicely.
-       my $dappliedcommit = make_commit_text(<<END);
+       my $dappliedcommit = hash_commit_text(<<END);
 tree $dappliedtree
 author $authline
 committer $authline
@@ -2664,7 +2695,10 @@ END
        if ($vcmp < 0) {
            @output = ($rawimport_mergeinput, $lastpush_mergeinput,
                { ReverseParents => 1,
-                 Message => (f_ <<END, $package, $cversion, $csuite) });
+                 # untranslated so that different people's pseudomerges
+                 # are not needlessly different (although they will
+                 # still differ if the series of pulls is different)
+                 Message => (sprintf <<END, $package, $cversion, $csuite) });
 Record %s (%s) in archive suite %s
 END
        } elsif ($vcmp > 0) {
@@ -2945,11 +2979,7 @@ sub git_fetch_us () {
     # deliberately-not-ff, in which case we must fetch everything.
 
     my @specs = deliberately_not_fast_forward ? qw(tags/*) :
-       map { "tags/$_" }
-       (quiltmode_splitbrain
-        ? (map { $_->('*',access_nomdistro) }
-           \&debiantag_new, \&debiantag_maintview)
-        : debiantags('*',access_nomdistro));
+       map { "tags/$_" } debiantags('*',access_nomdistro);
     push @specs, server_branch($csuite);
     push @specs, $rewritemap;
     push @specs, qw(heads/*) if deliberately_not_fast_forward;
@@ -3425,7 +3455,7 @@ END
        }
 
        close MC or confess "$!";
-       $hash = make_commit $mcf;
+       $hash = hash_commit $mcf;
     } else {
        $hash = $mergeinputs[0]{Commit};
     }
@@ -3786,7 +3816,7 @@ sub fork_for_multisuite ($) {
        $commit .=
            "author $authline\n".
            "committer $authline\n\n";
-       $output = make_commit_text $commit.$msg;
+       $output = hash_commit_text $commit.$msg;
        printdebug "multisuite merge generated $output\n";
     }
 
@@ -4096,6 +4126,7 @@ sub pseudomerge_version_check ($$) {
                $cd = $gf->('Distribution');
            };
            if ($@) {
+                $@ =~ s/^\n//s;
                $@ =~ s/^dgit: //gm;
                fail "$@".
                    f_ "Perhaps debian/changelog does not mention %s ?", $v;
@@ -4112,7 +4143,7 @@ END
     return $i_arch_v;
 }
 
-sub pseudomerge_make_commit ($$$$ $$) {
+sub pseudomerge_hash_commit ($$$$ $$) {
     my ($clogp, $dgitview, $archive_hash, $i_arch_v,
        $msg_cmd, $msg_msg) = @_;
     progress f_ "Declaring that HEAD includes all changes in %s...",
@@ -4144,7 +4175,7 @@ $msg_msg
 END
     close MC or confess "$!";
 
-    return make_commit($pmf);
+    return hash_commit($pmf);
 }
 
 sub splitbrain_pseudomerge ($$$$) {
@@ -4202,7 +4233,7 @@ ENDT
     }
 
     my $arch_v = $i_arch_v->[0];
-    my $r = pseudomerge_make_commit
+    my $r = pseudomerge_hash_commit
        $clogp, $dgitview, $archive_hash, $i_arch_v,
        "dgit --quilt=$quilt_mode",
        (defined $overwrite_version
@@ -4226,7 +4257,7 @@ sub plain_overwrite_pseudomerge ($$$) {
 
     my $m = f_ "Declare fast forward from %s", $i_arch_v->[0];
 
-    my $r = pseudomerge_make_commit
+    my $r = pseudomerge_hash_commit
        $clogp, $head, $archive_hash, $i_arch_v,
        "dgit", $m;
 
@@ -4251,7 +4282,7 @@ sub push_parse_changelog ($) {
 
     if (!$we_are_initiator) {
        # rpush initiator can't do this because it doesn't have $isuite yet
-       my $tag = debiantag($cversion, access_nomdistro);
+       my $tag = debiantag_new($cversion, access_nomdistro);
        runcmd @git, qw(check-ref-format), $tag;
     }
 
@@ -4275,7 +4306,7 @@ sub push_tagwants ($$$$) {
     my ($cversion, $dgithead, $maintviewhead, $tfbase) = @_;
     my @tagwants;
     push @tagwants, {
-        TagFn => \&debiantag,
+        TagFn => \&debiantag_new,
        Objid => $dgithead,
         TfSuffix => '',
         View => 'dgit',
@@ -4287,14 +4318,7 @@ sub push_tagwants ($$$$) {
            TfSuffix => '-maintview',
             View => 'maint',
         };
-    } elsif ($dodep14tag eq 'no' ? 0
-            : $dodep14tag eq 'want' ? access_cfg_tagformats_can_splitbrain
-            : $dodep14tag eq 'always'
-            ? (access_cfg_tagformats_can_splitbrain or fail <<END)
---dep14tag-always (or equivalent in config) means server must support
- both "new" and "maint" tag formats, but config says it doesn't.
-END
-           : die "$dodep14tag ?") {
+    } elsif ($dodep14tag ne 'no') {
        push @tagwants, {
            TagFn => \&debiantag_maintview,
            Objid => $dgithead,
@@ -4438,14 +4462,10 @@ Push failed, while preparing your push.
 You can retry the push, after fixing the problem, if you like.
 END
 
-    need_tagformat 'new', "quilt mode $quilt_mode"
-        if quiltmode_splitbrain;
-
     prep_ud();
 
     access_giturl(); # check that success is vaguely likely
     rpush_handle_protovsn_bothends() if $we_are_initiator;
-    select_tagformat();
 
     my $clogpfn = dgit_privdir()."/changelog.822.tmp";
     runcmd shell_cmd "exec >$clogpfn", qw(dpkg-parsechangelog);
@@ -4465,13 +4485,12 @@ END
     push_parse_dsc($dscpath, $dscfn, $cversion);
 
     my $format = getfield $dsc, 'Format';
-    printdebug "format $format\n";
 
     my $symref = git_get_symref();
     my $actualhead = git_rev_parse('HEAD');
 
     if (branch_is_gdr_unstitched_ff($symref, $actualhead, $archive_hash)) {
-       if (quiltmode_splitbrain()) {
+       if (quiltmode_splitting()) {
            my ($ffq_prev, $gdrlast) = branch_gdr_info($symref, $actualhead);
            fail f_ <<END, $ffq_prev, $quilt_mode;
 Branch is managed by git-debrebase (%s
@@ -4491,9 +4510,8 @@ END
 
     if (madformat_wantfixup($format)) {
        # user might have not used dgit build, so maybe do this now:
-       if (quiltmode_splitbrain()) {
+       if (do_split_brain()) {
            changedir $playground;
-           quilt_make_fake_dsc($upstreamversion);
            my $cachekey;
            ($dgithead, $cachekey) =
                quilt_check_splitbrain_cache($actualhead, $upstreamversion);
@@ -4501,17 +4519,22 @@ END
  "--quilt=%s but no cached dgit view:
  perhaps HEAD changed since dgit build[-source] ?",
                               $quilt_mode;
-           $split_brain = 1;
-           $dgithead = splitbrain_pseudomerge($clogp,
-                                              $actualhead, $dgithead,
-                                              $archive_hash);
-           $maintviewhead = $actualhead;
-           changedir $maindir;
-           prep_ud(); # so _only_subdir() works, below
-       } else {
+       }
+       if (!do_split_brain()) {
+           # In split brain mode, do not attempt to incorporate dirty
+           # stuff from the user's working tree.  That would be mad.
            commit_quilty_patch();
        }
     }
+    if (do_split_brain()) {
+       $made_split_brain = 1;
+       $dgithead = splitbrain_pseudomerge($clogp,
+                                          $actualhead, $dgithead,
+                                          $archive_hash);
+       $maintviewhead = $actualhead;
+       changedir $maindir;
+       prep_ud(); # so _only_subdir() works, below
+    }
 
     if (defined $overwrite_version && !defined $maintviewhead
        && $archive_hash) {
@@ -4538,6 +4561,8 @@ END
        }
     }
 
+    confess unless !!$made_split_brain == do_split_brain();
+
     changedir $playground;
     progress f_ "checking that %s corresponds to HEAD", $dscfn;
     runcmd qw(dpkg-source -x --),
@@ -4551,7 +4576,7 @@ END
     my $r = system @diffcmd;
     if ($r) {
        if ($r==256) {
-           my $referent = $split_brain ? $dgithead : 'HEAD';
+           my $referent = $made_split_brain ? $dgithead : 'HEAD';
            my $diffs = cmdoutput @git, qw(diff --stat), $tree, $dgithead;
 
            my @mode_changes;
@@ -4652,10 +4677,8 @@ ENDT
     responder_send_command("param head $dgithead");
     responder_send_command("param csuite $csuite");
     responder_send_command("param isuite $isuite");
-    responder_send_command("param tagformat $tagformat");
+    responder_send_command("param tagformat new"); # needed in $protovsn==4
     if (defined $maintviewhead) {
-       confess "internal error (protovsn=$protovsn)"
-           if defined $protovsn and $protovsn < 4;
        responder_send_command("param maint-view $maintviewhead");
     }
 
@@ -4858,10 +4881,11 @@ sub cmd_fetch {
 sub cmd_pull {
     parseopts();
     fetchpullargs();
-    if (quiltmode_splitbrain()) {
+    determine_whether_split_brain();
+    if (do_split_brain()) {
        my ($format, $fopts) = get_source_format();
        madformat($format) and fail f_ <<END, $quilt_mode
-dgit pull not yet supported in split view mode (--quilt=%s)
+dgit pull not yet supported in split view mode (including with view-splitting quilt modes)
 END
     }
     pull();
@@ -4951,6 +4975,7 @@ sub prep_push () {
     parseopts();
     build_or_push_prep_early();
     pushing();
+    build_or_push_prep_modes();
     check_not_dirty();
     my $specsuite;
     if (@ARGV==0) {
@@ -5024,10 +5049,6 @@ sub cmd_remote_push_responder { cmd_remote_push_build_host(); }
 #     a good error message)
 
 sub rpush_handle_protovsn_bothends () {
-    if ($protovsn < 4) {
-       need_tagformat 'old', "rpush negotiated protocol $protovsn";
-    }
-    select_tagformat();
 }
 
 our $i_tmp;
@@ -5095,7 +5116,6 @@ sub cmd_rpush {
     changedir $i_tmp;
     ($protovsn) = initiator_expect { m/^dgit-remote-push-ready (\S+)/ };
     die "$protovsn ?" unless grep { $_ eq $protovsn } @rpushprotovsn_support;
-    $supplementary_message = '' unless $protovsn >= 3;
 
     for (;;) {
        my ($icmd,$iargs) = initiator_expect {
@@ -5168,11 +5188,6 @@ sub i_resp_want ($) {
     pushing();
     rpush_handle_protovsn_bothends();
 
-    fail f_ "rpush negotiated protocol version %s".
-       " which does not support quilt mode %s",
-       $protovsn, $quilt_mode
-       if quiltmode_splitbrain && $protovsn < 4;
-
     my @localpaths = i_method "i_want", $keyword;
     printdebug "[[  $keyword @localpaths\n";
     foreach my $localpath (@localpaths) {
@@ -5242,11 +5257,10 @@ sub i_want_signed_tag {
     my $maintview = $i_param{'maint-view'};
     die if defined $maintview && $maintview =~ m/[^0-9a-f]/;
 
-    select_tagformat();
-    if ($protovsn >= 4) {
+    if ($protovsn == 4) {
        my $p = $i_param{'tagformat'} // '<undef>';
-       $p eq $tagformat
-           or badproto \*RO, "tag format mismatch: $p vs. $tagformat";
+       $p eq 'new'
+           or badproto \*RO, "tag format mismatch: $p vs. new";
     }
 
     die unless $i_param{'csuite'} =~ m/^$suite_re$/;
@@ -5374,19 +5388,11 @@ sub quiltify_tree_sentinelfiles ($) {
     return $r;
 }
 
-sub quiltify_splitbrain_needed () {
-    if (!$split_brain) {
-       progress __ "dgit view: changes are required...";
-       runcmd @git, qw(checkout -q -b dgit-view);
-       $split_brain = 1;
-    }
-}
-
-sub quiltify_splitbrain ($$$$$$$) {
+sub quiltify_splitting ($$$$$$$) {
     my ($clogp, $unapplied, $headref, $oldtiptree, $diffbits,
        $editedignores, $cachekey) = @_;
     my $gitignore_special = 1;
-    if ($quilt_mode !~ m/gbp|dpm/) {
+    if ($quilt_mode !~ m/gbp|dpm|baredebian/) {
        # treat .gitignore just like any other upstream file
        $diffbits = { %$diffbits };
        $_ = !!$_ foreach values %$diffbits;
@@ -5401,6 +5407,8 @@ sub quiltify_splitbrain ($$$$$$$) {
     local $ENV{GIT_AUTHOR_EMAIL} = $authline[1];
     local $ENV{GIT_AUTHOR_DATE} =  $authline[2];
 
+    confess unless do_split_brain();
+
     my $fulldiffhint = sub {
        my ($x,$y) = @_;
        my $cmd = "git diff $x $y -- :/ ':!debian'";
@@ -5409,14 +5417,14 @@ sub quiltify_splitbrain ($$$$$$$) {
                  $cmd;
     };
 
-    if ($quilt_mode =~ m/gbp|unapplied/ &&
+    if ($quilt_mode =~ m/gbp|unapplied|baredebian/ &&
        ($diffbits->{O2H} & 01)) {
        my $msg = f_
  "--quilt=%s specified, implying patches-unapplied git tree\n".
  " but git tree differs from orig in upstream files.",
                      $quilt_mode;
        $msg .= $fulldiffhint->($unapplied, 'HEAD');
-       if (!stat_exists "debian/patches") {
+       if (!stat_exists "debian/patches" and $quilt_mode !~ m/baredebian/) {
            $msg .= __
  "\n ... debian/patches is missing; perhaps this is a patch queue branch?";
        }  
@@ -5429,9 +5437,24 @@ sub quiltify_splitbrain ($$$$$$$) {
  but git tree differs from result of applying debian/patches to upstream
 END
     }
-    if ($quilt_mode =~ m/gbp|unapplied/ &&
+    if ($quilt_mode =~ m/baredebian/) {
+       # We need to construct a merge which has upstream files from
+       # upstream and debian/ files from HEAD.
+
+       read_tree_upstream $quilt_upstream_commitish, 1, $headref;
+       my $version = getfield $clogp, 'Version';
+       my $upsversion = upstreamversion $version;
+       my $merge = make_commit
+           [ $headref, $quilt_upstream_commitish ],
+ [ +(f_ <<ENDT, $upsversion), $quilt_upstream_commitish_message, <<ENDU ];
+Combine debian/ with upstream source for %s
+ENDT
+[dgit ($our_version) baredebian-merge $version $quilt_upstream_commitish_used]
+ENDU
+       runcmd @git, qw(reset -q --hard), $merge;
+    }
+    if ($quilt_mode =~ m/gbp|unapplied|baredebian/ &&
        ($diffbits->{O2A} & 01)) { # some patches
-       quiltify_splitbrain_needed();
        progress __ "dgit view: creating patches-applied version using gbp pq";
        runcmd shell_cmd 'exec >/dev/null', gbp_pq, qw(import);
        # gbp pq import creates a fresh branch; push back to dgit-view
@@ -5448,7 +5471,6 @@ END
     }
     if (($diffbits->{O2H} & 02) && # user has modified .gitignore
        !($diffbits->{O2A} & 02)) { # patches do not change .gitignore
-       quiltify_splitbrain_needed();
        progress __
            "dgit view: creating patch to represent .gitignore changes";
         ensuredir "debian/patches";
@@ -5488,16 +5510,6 @@ END
 [dgit ($our_version) update-gitignore-quilt-fixup]
 ENDU
     }
-
-    my $dgitview = git_rev_parse 'HEAD';
-
-    changedir $maindir;
-    reflog_cache_insert "refs/$splitbraincache", $cachekey, $dgitview;
-
-    changedir "$playground/work";
-
-    my $saved = maybe_split_brain_save $headref, $dgitview, __ "converted";
-    progress f_ "dgit view: created (%s)", $saved;
 }
 
 sub quiltify ($$$$) {
@@ -5778,8 +5790,6 @@ sub quiltify ($$$$) {
 
        runcmd @git, qw(checkout -q), $cc, qw(debian/changelog);
     }
-
-    runcmd @git, qw(checkout -q master);
 }
 
 sub build_maybe_quilt_fixup () {
@@ -5789,55 +5799,60 @@ sub build_maybe_quilt_fixup () {
 
     check_for_vendor_patches();
 
-    if (quiltmode_splitbrain) {
-       fail <<END unless access_cfg_tagformats_can_splitbrain;
-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');
     my $symref = git_get_symref();
-
-    if ($quilt_mode eq 'linear'
-       && !$fopts->{'single-debian-patch'}
-       && branch_is_gdr($headref)) {
-       # This is much faster.  It also makes patches that gdr
-       # likes better for future updates without laundering.
-       #
-       # However, it can fail in some casses where we would
-       # succeed: if there are existing patches, which correspond
-       # to a prefix of the branch, but are not in gbp/gdr
-       # format, gdr will fail (exiting status 7), but we might
-       # be able to figure out where to start linearising.  That
-       # will be slower so hopefully there's not much to do.
-       my @cmd = (@git_debrebase,
-                  qw(--noop-ok -funclean-mixed -funclean-ordering
-                     make-patches --quiet-would-amend));
-       # We tolerate soe snags that gdr wouldn't, by default.
-       if (act_local()) {
-           debugcmd "+",@cmd;
-           $!=0; $?=-1;
-           failedcmd @cmd
-               if system @cmd
-               and not ($? == 7*256 or
-                        $? == -1 && $!==ENOENT);
-       } else {
-           dryrun_report @cmd;
-       }
-       $headref = git_rev_parse('HEAD');
-    }
+    my $upstreamversion = upstreamversion $version;
 
     prep_ud();
     changedir $playground;
 
-    my $upstreamversion = upstreamversion $version;
+    my $splitbrain_cachekey;
+
+    if (do_split_brain()) {
+       my $cachehit;
+       ($cachehit, $splitbrain_cachekey) =
+           quilt_check_splitbrain_cache($headref, $upstreamversion);
+       if ($cachehit) {
+           changedir $maindir;
+           return;
+       }
+    }
+
+    unpack_playtree_need_cd_work($headref);
+    if (do_split_brain()) {
+       runcmd @git, qw(checkout -q -b dgit-view);
+       # so long as work is not deleted, its current branch will
+       # remain dgit-view, rather than master, so subsequent calls to
+       #  unpack_playtree_need_cd_work
+       # will DTRT, resetting dgit-view.
+       confess if $made_split_brain;
+       $made_split_brain = 1;
+    }
+    chdir '..';
 
     if ($fopts->{'single-debian-patch'}) {
+       fail f_
+ "quilt mode %s does not make sense (or is not supported) with single-debian-patch",
+           $quilt_mode
+           if quiltmode_splitting();
        quilt_fixup_singlepatch($clogp, $headref, $upstreamversion);
     } else {
-       quilt_fixup_multipatch($clogp, $headref, $upstreamversion);
+       quilt_fixup_multipatch($clogp, $headref, $upstreamversion,
+                             $splitbrain_cachekey);
+    }
+
+    if (do_split_brain()) {
+       my $dgitview = git_rev_parse 'HEAD';
+
+       changedir $maindir;
+       reflog_cache_insert "refs/$splitbraincache",
+           $splitbrain_cachekey, $dgitview;
+
+       changedir "$playground/work";
+
+       my $saved = maybe_split_brain_save $headref, $dgitview, __ "converted";
+       progress f_ "dgit view: created (%s)", $saved;
     }
 
     changedir $maindir;
@@ -5845,12 +5860,22 @@ END
         @git, qw(pull --ff-only -q), "$playground/work", qw(master);
 }
 
-sub unpack_playtree_mkwork ($) {
+sub build_check_quilt_splitbrain () {
+    build_maybe_quilt_fixup();
+}
+
+sub unpack_playtree_need_cd_work ($) {
     my ($headref) = @_;
 
-    mkdir "work" or confess "$!";
-    changedir "work";
-    mktree_in_ud_here();
+    # prep_ud() must have been called already.
+    if (!chdir "work") {
+       # Check in the filesystem because sometimes we run prep_ud
+       # in between multiple calls to unpack_playtree_need_cd_work.
+       confess "$!" unless $!==ENOENT;
+       mkdir "work" or confess "$!";
+       changedir "work";
+       mktree_in_ud_here();
+    }
     runcmd @git, qw(reset -q --hard), $headref;
 }
 
@@ -5899,7 +5924,7 @@ sub quilt_fixup_singlepatch ($$$) {
     # necessary to build the source package.
 
     unpack_playtree_linkorigs($upstreamversion, sub { });
-    unpack_playtree_mkwork($headref);
+    unpack_playtree_need_cd_work($headref);
 
     rmtree("debian/patches");
 
@@ -5915,9 +5940,14 @@ sub quilt_fixup_singlepatch ($$$) {
     commit_quilty_patch();
 }
 
-sub quilt_make_fake_dsc ($) {
+sub quilt_need_fake_dsc ($) {
+    # cwd should be playground
     my ($upstreamversion) = @_;
 
+    return if stat_exists "fake.dsc";
+    # ^ OK to test this as a sentinel because if we created it
+    # we must either have done the rest too, or crashed.
+
     my $fakeversion="$upstreamversion-~~DGITFAKE";
 
     my $fakedsc=new IO::File 'fake.dsc', '>' or confess "$!";
@@ -5961,8 +5991,9 @@ END
 sub quilt_fakedsc2unapplied ($$) {
     my ($headref, $upstreamversion) = @_;
     # must be run in the playground
-    # quilt_make_fake_dsc must have been called
+    # quilt_need_fake_dsc must have been called
 
+    quilt_need_fake_dsc($upstreamversion);
     runcmd qw(sh -ec),
         'exec dpkg-source --no-check --skip-patches -x fake.dsc >/dev/null';
 
@@ -5990,6 +6021,8 @@ sub quilt_check_splitbrain_cache ($$) {
     # Computes the cache key and looks in the cache.
     # Returns ($dgit_view_commitid, $cachekey) or (undef, $cachekey)
 
+    quilt_need_fake_dsc($upstreamversion);
+
     my $splitbrain_cachekey;
     
     progress f_
@@ -6001,6 +6034,7 @@ sub quilt_check_splitbrain_cache ($$) {
     push @cachekey, $upstreamversion;
     push @cachekey, $quilt_mode;
     push @cachekey, $headref;
+    push @cachekey, $quilt_upstream_commitish // '-';
 
     push @cachekey, hashfile('fake.dsc');
 
@@ -6021,12 +6055,12 @@ sub quilt_check_splitbrain_cache ($$) {
        "refs/$splitbraincache", $splitbrain_cachekey;
 
     if ($cachehit) {
-       unpack_playtree_mkwork($headref);
+       unpack_playtree_need_cd_work($headref);
        my $saved = maybe_split_brain_save $headref, $cachehit, "cache-hit";
        if ($cachehit ne $headref) {
            progress f_ "dgit view: found cached (%s)", $saved;
            runcmd @git, qw(checkout -q -b dgit-view), $cachehit;
-           $split_brain = 1;
+           $made_split_brain = 1;
            return ($cachehit, $splitbrain_cachekey);
        }
        progress __ "dgit view: found cached, no changes required";
@@ -6037,8 +6071,32 @@ sub quilt_check_splitbrain_cache ($$) {
     return (undef, $splitbrain_cachekey);
 }
 
+sub baredebian_origtarballs_scan ($$$) {
+    my ($fakedfi, $upstreamversion, $dir) = @_;
+    if (!opendir OD, $dir) {
+       return if $! == ENOENT;
+       fail "opendir $dir (origs): $!";
+    }
+
+    while ($!=0, defined(my $leaf = readdir OD)) {
+       {
+           local ($debuglevel) = $debuglevel-1;
+           printdebug "BDOS $dir $leaf ?\n";
+       }
+       next unless is_orig_file_of_vsn $leaf, $upstreamversion;
+       next if grep { $_->{Filename} eq $leaf } @$fakedfi;
+       push @$fakedfi, {
+            Filename => $leaf,
+            Path => "$dir/$leaf",
+                       };
+    }
+
+    die "$dir; $!" if $!;
+    closedir OD;
+}
+
 sub quilt_fixup_multipatch ($$$) {
-    my ($clogp, $headref, $upstreamversion) = @_;
+    my ($clogp, $headref, $upstreamversion, $splitbrain_cachekey) = @_;
 
     progress f_ "examining quilt state (multiple patches, %s mode)",
                $quilt_mode;
@@ -6112,16 +6170,39 @@ sub quilt_fixup_multipatch ($$$) {
     # afterwards with dpkg-source --before-build.  That lets us save a
     # tree object corresponding to .origs.
 
-    my $splitbrain_cachekey;
+    if ($quilt_mode eq 'linear'
+       && branch_is_gdr($headref)) {
+       # This is much faster.  It also makes patches that gdr
+       # likes better for future updates without laundering.
+       #
+       # However, it can fail in some casses where we would
+       # succeed: if there are existing patches, which correspond
+       # to a prefix of the branch, but are not in gbp/gdr
+       # format, gdr will fail (exiting status 7), but we might
+       # be able to figure out where to start linearising.  That
+       # will be slower so hopefully there's not much to do.
 
-    quilt_make_fake_dsc($upstreamversion);
+       unpack_playtree_need_cd_work $headref;
 
-    if (quiltmode_splitbrain()) {
-       my $cachehit;
-       ($cachehit, $splitbrain_cachekey) =
-           quilt_check_splitbrain_cache($headref, $upstreamversion);
-       return if $cachehit;
+       my @cmd = (@git_debrebase,
+                  qw(--noop-ok -funclean-mixed -funclean-ordering
+                     make-patches --quiet-would-amend));
+       # We tolerate soe snags that gdr wouldn't, by default.
+       if (act_local()) {
+           debugcmd "+",@cmd;
+           $!=0; $?=-1;
+           failedcmd @cmd
+               if system @cmd
+               and not ($? == 7*256 or
+                        $? == -1 && $!==ENOENT);
+       } else {
+           dryrun_report @cmd;
+       }
+       $headref = git_rev_parse('HEAD');
+
+       chdir '..';
     }
+
     my $unapplied=quilt_fakedsc2unapplied($headref, $upstreamversion);
 
     ensuredir '.pc';
@@ -6140,7 +6221,7 @@ END
 
     changedir '..';
 
-    unpack_playtree_mkwork($headref);
+    unpack_playtree_need_cd_work($headref);
 
     my $mustdeletepc=0;
     if (stat_exists ".pc") {
@@ -6161,15 +6242,97 @@ END
     # We calculate some guesswork now about what kind of tree this might
     # be.  This is mostly for error reporting.
 
+    my $tentries = cmdoutput @git, qw(ls-tree --name-only -z), $headref;
+    my $onlydebian = $tentries eq "debian\0";
+
+    my $uheadref = $headref;
+    my $uhead_whatshort = 'HEAD';
+
+    if ($quilt_mode =~ m/baredebian\+tarball/) {
+       # We need to make a tarball import.  Yuk.
+       # We want to do this here so that we have a $uheadref value
+
+       my @fakedfi;
+       baredebian_origtarballs_scan \@fakedfi, $upstreamversion, bpd_abs();
+       baredebian_origtarballs_scan \@fakedfi, $upstreamversion,
+           "$maindir/.." unless $buildproductsdir eq '..';
+       changedir '..';
+
+       my @tartrees = import_tarball_tartrees $upstreamversion, \@fakedfi;
+
+       fail __ "baredebian quilt fixup: could not find any origs"
+           unless @tartrees;
+
+       changedir 'work';
+       my ($authline, $r1authline, $clogp,) =
+           import_tarball_commits \@tartrees, $upstreamversion;
+
+       if (@tartrees == 1) {
+           $uheadref = $tartrees[0]{Commit};
+           # TRANSLATORS: this translation must fit in the ASCII art
+           # quilt differences display.  The untranslated display
+           # says %9.9s, so with that display it must be at most 9
+           # characters.
+           $uhead_whatshort = __ 'tarball';
+       } else {
+           # on .dsc import we do not make a separate commit, but
+           # here we need to do so
+           rm_subdir_cached '.';
+           my $parents;
+           foreach my $ti (@tartrees) {
+               my $c = $ti->{Commit};
+               if ($ti->{OrigPart} eq 'orig') {
+                   runcmd qw(git read-tree), $c;
+               } elsif ($ti->{OrigPart} =~ m/orig-/) {
+                   read_tree_subdir $', $c;
+               } else {
+                   confess "$ti->OrigPart} ?"
+               }
+               $parents .= "parent $c\n";
+           }
+           my $tree = git_write_tree();
+           my $mbody = f_ 'Combine orig tarballs for %s %s',
+               $package, $upstreamversion;
+           $uheadref = hash_commit_text <<END;
+tree $tree
+${parents}author $r1authline
+committer $r1authline
+
+$mbody
+
+[dgit import tarballs combine $package $upstreamversion]
+END
+           # TRANSLATORS: this translation must fit in the ASCII art
+           # quilt differences display.  The untranslated display
+           # says %9.9s, so with that display it must be at most 9
+           # characters.  This fragmentt is referring to multiple
+           # orig tarballs in a source package.
+           $uhead_whatshort = __ 'tarballs';
+
+           runcmd @git, qw(reset -q);
+       }
+       $quilt_upstream_commitish = $uheadref;
+       $quilt_upstream_commitish_used = '*orig*';
+       $quilt_upstream_commitish_message = '';
+    }
+    if ($quilt_mode =~ m/baredebian$/) {
+       $uheadref = $quilt_upstream_commitish;
+       # TRANSLATORS: this translation must fit in the ASCII art
+       # quilt differences display.  The untranslated display
+       # says %9.9s, so with that display it must be at most 9
+       # characters.
+       $uhead_whatshort = __ 'upstream';
+    }
+
     my %editedignores;
     my @unrepres;
     my $diffbits = {
         # H = user's HEAD
         # O = orig, without patches applied
         # A = "applied", ie orig with H's debian/patches applied
-        O2H => quiltify_trees_differ($unapplied,$headref,   1,
+        O2H => quiltify_trees_differ($unapplied,$uheadref,   1,
                                     \%editedignores, \@unrepres),
-        H2A => quiltify_trees_differ($headref,  $oldtiptree,1),
+        H2A => quiltify_trees_differ($uheadref, $oldtiptree,1),
         O2A => quiltify_trees_differ($unapplied,$oldtiptree,1),
     };
 
@@ -6184,13 +6347,23 @@ END
     progress f_
 "%s: base trees orig=%.20s o+d/p=%.20s",
               $us, $unapplied, $oldtiptree;
+    # TRANSLATORS: Try to keep this ascii-art layout right.  The 0s in
+    # %9.00009s will be ignored and are there to make the format the
+    # same length (9 characters) as the output it generates.  If you
+    # change the value 9, your translations of "upstream" and
+    # 'tarball' must fit into the new length, and you should change
+    # the number of 0s.  Do not reduce it below 4 as HEAD has to fit
+    # too.
     progress f_
 "%s: quilt differences: src:  %s orig %s     gitignores:  %s orig %s\n".
-"%s: quilt differences:      HEAD %s o+d/p               HEAD %s o+d/p",
+"%s: quilt differences: %9.00009s %s o+d/p          %9.00009s %s o+d/p",
   $us,                      $dl[0], $dl[1],              $dl[3], $dl[4],
-  $us,                          $dl[2],                     $dl[5];
+  $us,        $uhead_whatshort, $dl[2],   $uhead_whatshort, $dl[5];
 
-    if (@unrepres) {
+    if (@unrepres && $quilt_mode !~ m/baredebian/) {
+       # With baredebian, even if the upstream commitish has this
+       # problem, we don't want to print this message, as nothing
+       # is going to try to make a patch out of it anyway.
        print STDERR f_ "dgit:  cannot represent change: %s: %s\n",
                        $_->[1], $_->[0]
            foreach @unrepres;
@@ -6200,7 +6373,11 @@ END
     }
 
     my @failsuggestion;
-    if (!($diffbits->{O2H} & $diffbits->{O2A})) {
+    if ($onlydebian) {
+       push @failsuggestion, [ 'onlydebian', __
+ "This has only a debian/ directory; you probably want --quilt=bare debian." ]
+           unless $quilt_mode =~ m/baredebian/;
+    } elsif (!($diffbits->{O2H} & $diffbits->{O2A})) {
         push @failsuggestion, [ 'unapplied', __
  "This might be a patches-unapplied branch." ];
     } elsif (!($diffbits->{H2A} & $diffbits->{O2A})) {
@@ -6215,17 +6392,20 @@ END
        if stat_exists '.gitattributes';
 
     push @failsuggestion, [ 'origs', __
- "Maybe orig tarball(s) are not identical to git representation?" ];
-
-    if (quiltmode_splitbrain()) {
-       quiltify_splitbrain($clogp, $unapplied, $headref, $oldtiptree,
-                            $diffbits, \%editedignores,
-                           $splitbrain_cachekey);
+ "Maybe orig tarball(s) are not identical to git representation?" ]
+       unless $onlydebian && $quilt_mode !~ m/baredebian/;
+              # ^ in that case, we didn't really look properly
+
+    if (quiltmode_splitting()) {
+       quiltify_splitting($clogp, $unapplied, $headref, $oldtiptree,
+                          $diffbits, \%editedignores,
+                          $splitbrain_cachekey);
        return;
     }
 
     progress f_ "starting quiltify (multiple patches, %s mode)", $quilt_mode;
     quiltify($clogp,$headref,$oldtiptree,\@failsuggestion);
+    runcmd @git, qw(checkout -q), (qw(master dgit-view)[do_split_brain()]);
 
     if (!open P, '>>', ".pc/applied-patches") {
        $!==&ENOENT or confess "$!";
@@ -6260,7 +6440,7 @@ sub quilt_fixup_editor () {
 }
 
 sub maybe_apply_patches_dirtily () {
-    return unless $quilt_mode =~ m/gbp|unapplied/;
+    return unless $quilt_mode =~ m/gbp|unapplied|baredebian/;
     print STDERR __ <<END or confess "$!";
 
 dgit: Building, or cleaning with rules target, in patches-unapplied tree.
@@ -6332,6 +6512,18 @@ sub clean_tree_check () {
 sub clean_tree () {
     # We always clean the tree ourselves, rather than leave it to the
     # builder (dpkg-source, or soemthing which calls dpkg-source).
+    if ($quilt_mode =~ m/baredebian/ and $cleanmode =~ m/git/) {
+       fail f_ <<END, $quilt_mode, $cleanmode;
+quilt mode %s (generally needs untracked upstream files)
+contradicts clean mode %s (which would delete them)
+END
+       # This is not 100% true: dgit build-source and push-source
+       # (for example) could operate just fine with no upstream
+       # source in the working tree.  But it doesn't seem likely that
+       # the user wants dgit to proactively delete such things.
+       # -wn, for example, would produce identical output without
+       # deleting anything from the working tree.
+    }
     if ($cleanmode =~ m{^dpkg-source}) {
        my @cmd = @dpkgbuildpackage;
        push @cmd, qw(-d) if $cleanmode =~ m{^dpkg-source-d};
@@ -6375,9 +6567,30 @@ sub build_or_push_prep_early () {
     $dscfn = dscfn($version);
 }
 
+sub build_or_push_prep_modes () {
+    my ($format,) = determine_whether_split_brain();
+
+    fail __ "dgit: --include-dirty is not supported with split view".
+            " (including with view-splitting quilt modes)"
+       if do_split_brain() && $includedirty;
+
+    if (madformat_wantfixup $format and $quilt_mode =~ m/baredebian$/) {
+       ($quilt_upstream_commitish, $quilt_upstream_commitish_used,
+        $quilt_upstream_commitish_message)
+           = resolve_upstream_version
+           $quilt_upstream_commitish, upstreamversion $version;
+       progress f_ "dgit: --quilt=%s, %s", $quilt_mode,
+           $quilt_upstream_commitish_message;
+    } elsif (defined $quilt_upstream_commitish) {
+       fail __
+ "dgit: --upstream-commitish only makes sense with --quilt=baredebian"
+    }
+}
+
 sub build_prep_early () {
     build_or_push_prep_early();
     notpushing();
+    build_or_push_prep_modes();
     check_not_dirty();
 }
 
@@ -6399,7 +6612,7 @@ sub build_prep ($) {
        # said -wc we should still do the check.
        clean_tree_check();
     }
-    build_maybe_quilt_fixup();
+    build_check_quilt_splitbrain();
     if ($rmchanges) {
        my $pat = changespat $version;
        foreach my $f (glob "$buildproductsdir/$pat") {
@@ -6688,7 +6901,7 @@ sub building_source_in_playtree {
     #
     # Note that if we are building a source package in split brain
     # mode we do not support including uncommitted changes, because
-    # that makes quilt fixup too hard.  I.e. ($split_brain && (dgit is
+    # that makes quilt fixup too hard.  I.e. ($made_split_brain && (dgit is
     # building a source package)) => !$includedirty
     return !$includedirty;
 }
@@ -6699,6 +6912,8 @@ sub build_source {
        unlink "$buildproductsdir/$sourcechanges" or $!==ENOENT
            or fail f_ "remove %s: %s", $sourcechanges, $!;
     }
+#    confess unless !!$made_split_brain == do_split_brain();
+
     my @cmd = (@dpkgsource, qw(-b --));
     my $leafdir;
     if (building_source_in_playtree()) {
@@ -6707,12 +6922,12 @@ sub build_source {
         # If we are in split brain, there is already a playtree with
         # the thing we should package into a .dsc (thanks to quilt
         # fixup).  If not, make a playtree
-        prep_ud() unless $split_brain;
+        prep_ud() unless $made_split_brain;
         changedir $playground;
-        unless ($split_brain) {
+        unless ($made_split_brain) {
             my $upstreamversion = upstreamversion $version;
             unpack_playtree_linkorigs($upstreamversion, sub { });
-            unpack_playtree_mkwork($headref);
+            unpack_playtree_need_cd_work($headref);
             changedir '..';
         }
     } else {
@@ -6779,7 +6994,7 @@ sub cmd_push_source {
        "dgit push-source: --include-dirty/--ignore-dirty does not make".
        "sense with push-source!"
        if $includedirty;
-    build_maybe_quilt_fixup();
+    build_check_quilt_splitbrain();
     if ($changesfile) {
         my $changes = parsecontrol("$buildproductsdir/$changesfile",
                                    __ "source changes file");
@@ -6878,7 +7093,6 @@ sub cmd_print_unapplied_treeish {
     prep_ud();
     changedir $playground;
     my $uv = upstreamversion $version;
-    quilt_make_fake_dsc($uv);
     my $u = quilt_fakedsc2unapplied($headref, $uv);
     print $u, "\n" or confess "$!";
 }
@@ -7070,7 +7284,7 @@ END
            my $version = getfield $dsc, 'Version';
            my $clogp = commit_getclogp $newhash;
            my $authline = clogp_authline $clogp;
-           $newhash = make_commit_text <<ENDU
+           $newhash = hash_commit_text <<ENDU
 tree $tree
 parent $newhash
 parent $oldhash
@@ -7302,7 +7516,7 @@ sub parseopts () {
                push @ropts, $_;
                my $cmd = shift @$om;
                @$om = ($cmd, grep { $_ ne $2 } @$om);
-           } elsif (m/^--(gbp|dpm)$/s) {
+           } elsif (m/^--($quilt_options_re)$/s) {
                push @ropts, "--quilt=$1";
                $quilt_mode = $1;
            } elsif (m/^--(?:ignore|include)-dirty$/s) {
@@ -7320,16 +7534,25 @@ sub parseopts () {
            } elsif (m/^--overwrite$/s) {
                push @ropts, $_;
                $overwrite_version = '';
+           } elsif (m/^--split-(?:view|brain)$/s) {
+               push @ropts, $_;
+               $splitview_mode = 'always';
+           } elsif (m/^--split-(?:view|brain)=($splitview_modes_re)$/s) {
+               push @ropts, $_;
+               $splitview_mode = $1;
            } elsif (m/^--overwrite=(.+)$/s) {
                push @ropts, $_;
                $overwrite_version = $1;
            } elsif (m/^--delayed=(\d+)$/s) {
                push @ropts, $_;
                push @dput, $_;
-           } elsif (my ($k,$v) =
-                    m/^--save-(dgit-view)=(.+)$/s ||
+           } elsif (m/^--upstream-commitish=(.+)$/s) {
+               push @ropts, $_;
+               $quilt_upstream_commitish = $1;
+           } elsif (m/^--save-(dgit-view)=(.+)$/s ||
                     m/^--(dgit-view)-save=(.+)$/s
                     ) {
+               my ($k,$v) = ($1,$2);
                push @ropts, $_;
                $v =~ s#^(?!refs/)#refs/heads/#;
                $internal_object_save{$k} = $v;
@@ -7348,11 +7571,6 @@ sub parseopts () {
                    f_ "%s: warning: ignoring unknown force option %s\n",
                       $us, $_;
                $_='';
-           } elsif (m/^--dgit-tag-format=(old|new)$/s) {
-               # undocumented, for testing
-               push @ropts, $_;
-               $tagformat_want = [ $1, 'command line', 1 ];
-               # 1 menas overrides distro configuration
            } elsif (m/^--config-lookup-explode=(.+)$/s) {
                # undocumented, for testing
                push @ropts, $_;
@@ -7498,6 +7716,7 @@ sub parseopts_late_defaults () {
            or badcfg f_ "unknown quilt-mode \`%s'", $quilt_mode;
        $quilt_mode = $1;
     }
+    $quilt_mode =~ s/^(baredebian)\+git$/$1/;
 
     foreach my $moc (@modeopt_cfgs) {
        local $access_forpush;
@@ -7510,19 +7729,10 @@ sub parseopts_late_defaults () {
        $$vr = $v;
     }
 
-    fail __ "dgit: --include-dirty is not supported in split view quilt mode"
-       if $split_brain && $includedirty;
-
-    if (!defined $cleanmode) {
+    {
        local $access_forpush;
-       $cleanmode = access_cfg('clean-mode-newer', 'RETURN-UNDEF');
-       $cleanmode = undef if $cleanmode && $cleanmode !~ m/^$cleanmode_re$/;
-
-       $cleanmode //= access_cfg('clean-mode', 'RETURN-UNDEF');
-       $cleanmode //= 'dpkg-source';
-
-       badcfg f_ "unknown clean-mode \`%s'", $cleanmode unless
-           $cleanmode =~ m/$cleanmode_re/;
+       default_from_access_cfg(\$cleanmode, 'clean-mode', 'dpkg-source',
+                               $cleanmode_re);
     }
 
     $buildproductsdir //= access_cfg('build-products-dir', 'RETURN-UNDEF');
index b91ed1648ea676355c3727c7fcd327ea2b7bf375..9c9598bb6570c52439cd14928be4983b6671c2d2 100644 (file)
@@ -134,6 +134,32 @@ using git-diff(1) to compare the imported tarball to the release tag.
 
 =back
 
+=head3 Using untagged upstream commits
+
+=over 4
+
+Sometimes upstream does not tag their releases, or you want to package
+an unreleased git snapshot.  In such a case you can create your own
+upstream release tag, of the form B<upstream/>I<ver>, where I<ver> is
+the upstream version you plan to put in I<debian/changelog>.  The
+B<upstream/> prefix ensures that your tag will not clash with any tags
+upstream later creates.
+
+For example, suppose that the latest upstream release is 1.2.2 and you
+want to package git commit ab34c21 which was made on 2013-12-11.  A
+common convention is to use the upstream version number
+1.2.2+git20131211.ab34c21 and so you could use
+
+=over 4
+
+    % git tag -s upstream/1.2.2+git20131211.ab34c21 ab34c21
+
+=back
+
+to obtain a release tag, and then proceed as above.
+
+=back
+
 =head2 When upstream releases only tarballs
 
 Because we want to work in git, we need a virtual upstream branch with
@@ -331,6 +357,10 @@ release, and importing that release using git-debrebase(1).
 
 =back
 
+If you want to package an untagged upstream commit (because upstream
+does not tag releases or because you want to package an upstream
+development snapshot), see "Using untagged upstream commits" above.
+
 =head3 When upstream releases only tarballs
 
 You will need the I<debian/gbp.conf> from "When upstream releases only
@@ -375,9 +405,10 @@ release:
 
 =back
 
-Pass I<--stat> just to see the list of changed files, which is useful
-to determine whether there are any new or deleted files that may need
-accounting for in your copyright file.
+Also, diff with I<--name-status> and I<--diff-filter=ADR> to see
+just the list of added or removed files, which is useful to determine
+whether there are any new or deleted files that may need accounting
+for in your copyright file.
 
 If you obtained a tarball from upstream, you are ready to try a build.
 If you merged a git tag from upstream, you will first need to generate
index c20a2525d1d62a0d96229f7104a4e03bec880718..0ccd8c7e7fd6f5885e5c471ec75f3a7c429b3f50 100644 (file)
@@ -120,6 +120,32 @@ upstream's tarball instead of running git-deborig(1).
 
 =back
 
+=head3 Using untagged upstream commits
+
+=over 4
+
+Sometimes upstream does not tag their releases, or you want to package
+an unreleased git snapshot.  In such a case you can create your own
+upstream release tag, of the form B<upstream/>I<ver>, where I<ver> is
+the upstream version you plan to put in I<debian/changelog>.  The
+B<upstream/> prefix ensures that your tag will not clash with any tags
+upstream later creates.
+
+For example, suppose that the latest upstream release is 1.2.2 and you
+want to package git commit ab34c21 which was made on 2013-12-11.  A
+common convention is to use the upstream version number
+1.2.2+git20131211.ab34c21 and so you could use
+
+=over 4
+
+    % git tag -s upstream/1.2.2+git20131211.ab34c21 ab34c21
+
+=back
+
+to obtain a release tag, and then proceed as above.
+
+=back
+
 =head2 When upstream releases only tarballs
 
 We need a virtual upstream branch with virtual release tags.
@@ -340,6 +366,10 @@ to git), you can just run dpkg-buildpackage(1) or debuild(1) instead.
 
 =back
 
+If you want to package an untagged upstream commit (because upstream
+does not tag releases or because you want to package an upstream
+development snapshot), see "Using untagged upstream commits" above.
+
 =head3 When upstream releases only tarballs
 
 You will need the I<debian/gbp.conf> from "When upstream releases only
@@ -371,7 +401,7 @@ accounting for in your copyright file:
 
 =over 4
 
-    % git diff --stat master..1.2.3 -- . ':!debian'
+    % git diff --name-status --diff-filter=ADR master..1.2.3 -- . ':!debian'
 
 =back
 
diff --git a/dgit.1 b/dgit.1
index 682562cab85b56fb7ee45215d89de3e39b13bc3c..6e0c4f1186c2113795bc24cb14db02f396beef78 100644 (file)
--- a/dgit.1
+++ b/dgit.1
@@ -642,6 +642,18 @@ include the changes from your working tree.
 This can be useful with build, if you plan to commit later.  (dgit
 push will still ensure that the .dsc you upload and the git tree
 you push are identical, so this option won't make broken pushes.)
+
+Note that this does
+.BR not
+prevent dgit from cleaning your tree, so if the changes in your
+working tree are in the form of untracked files, those might still be
+deleted, especially with --clean=git.
+If you want to include untracked files in the build, you can
+use --clean=none or --clean=dpkg-source[-d]
+in addition to --include-dirty.
+Note that this
+combination can fail if the untracked files are under
+\fIdebian/patches/\fR.
 .TP
 .BR --ignore-dirty
 Deprecated alias for --include-dirty.
@@ -695,7 +707,7 @@ of git merge -s ours) to stitch the archive's version into your own
 git history, so that your push is a fast forward from the archive.
 
 (In quilt mode
-.BR gbp ", " dpm " or " unpatched ,
+.BR gbp ", " dpm ", " unpatched " or " baredebian *,
 implying a split between the dgit view and the
 maintainer view, the pseudo-merge will appear only in the dgit view.)
 .TP
@@ -742,7 +754,7 @@ it can mean that
 dgit fails to find necessary git commits.
 .TP
 .BR \-\-save-dgit-view= \fIbranch\fR|\fIref\fR
-Specifies that when a split view quilt mode is in operation,
+Specifies that when split view is in operation,
 and dgit calculates
 (or looks up in its cache)
 a dgit view corresponding to your HEAD,
@@ -753,10 +765,7 @@ so don't specify a branch you want to keep.
 
 This option is effective only with the following operations:
 quilt-fixup; push; all builds.
-And it is only effective with
-\-\-[quilt=]gbp,
-\-\-[quilt=]dpm,
-\-\-quilt=unpatched.
+And it is only effective when split view is actually in operation.
 
 If ref does not start with refs/
 it is taken to be a branch -
@@ -786,7 +795,7 @@ Debian, use this when you are making a renewed upload of an entirely
 new source package whose previous version was not accepted for release
 from NEW because of problems with copyright or redistributibility.
 
-In split view quilt modes,
+When split view is in operation,
 this also prevents the construction by dgit of a pseudomerge
 to make the dgit view fast forwarding.
 Normally only one of
@@ -856,13 +865,17 @@ Do not check whether source format `3.0 (quilt)' metadata needs
 fixing up.  If you use this option and the metadata did in fact need
 fixing up, dgit push will fail.
 .TP
-.BR -- [ quilt= ] gbp " | " -- [ quilt= ] dpm " | " --quilt=unapplied
+.BR -- [ quilt= ] gbp " | " -- [ quilt= ] dpm " | " --quilt=unapplied " | " -- [ quilt= ] baredebian [ +git | +tarball ]
 Tell dgit that you are using a nearly-dgit-compatible git branch,
 aka a
 .BR "maintainer view" ,
 and
 do not want your branch changed by dgit.
 
+These quilt modes are known as
+.BR "splitting quilt modes" .
+See --split-view, below.
+
 .B --gbp
 (short for
 .BR --quilt=gbp )
@@ -884,7 +897,32 @@ specifies that your HEAD is a patches-unapplied git branch (and
 that any changes to upstream .gitignore files are represented as
 patches in debian/patches).
 
-With --quilt=gbp|dpm|unapplied,
+.B --quilt=baredebian
+(or its alias
+.BR --quilt=baredebian+git )
+specifies that your HEAD contains only a debian/ directory,
+with any changes to upstream files represented as
+patches in debian/patches.
+The upstream source must be available in git,
+by default, in a suitably named git tag;
+see --upstream-commitish.
+In this mode, dgit cannot check that
+all edited upstream files are properly represented as patches:
+dgit relies on
+debian/patches being correct.
+
+.B --quilt=baredebian+tarball
+is like --quilt=baredebian,
+but is used when there is no appropriate upstream git history.
+To construct the dgit view,
+dgit will import your orig tarballs' contents into git.
+In this mode, dgit cannot check that
+the upstream parts of your upload correspond to what you intend:
+dgit relies on
+the right orig tarball(s) existing, and
+debian/patches being correct.
+
+With --quilt=gbp|dpm|unapplied|baredebian*,
 dgit push (or precursors like quilt-fixup and build) will automatically
 generate a conversion of your git branch into the right form.
 dgit push will push the
@@ -931,6 +969,42 @@ for fetching (and, for dgit push, altering) a variety of information both
 in the archive and in dgit-repos.
 How to set this up is not yet documented.
 .TP
+.BR \-\-split-view=auto | always | never
+Controls whether dgit operates a split view,
+separating your own branch (as Debian maintainer)
+from that shown to users of dgit clone and dgit fetch.
+
+When split view is in operation
+dgit will not make or merge any commits onto your own branch.
+Specifically, only the dgit view will contain
+dgit's pseudomerges,
+which bring into the git history previous uploads made with dgit push,
+and any commits in debian/patches required
+to make a correct `3.0 (quilt)' source package.
+
+.B auto
+is the default, and splits the view only when needed:
+i.e., when you are working with a `3.0 (quilt)' source package
+and a splitting quilt mode:
+\-\-[quilt=]gbp, dpm, unpatched or baredebian*.
+
+.B always
+splits the view regardless of the source format and the quilt mode.
+
+.B never
+will cause dgit to fail if split view is needed.
+
+When split view is in operation, the dgit view is visible
+in your local git clone,
+but only in refs specific to dgit:
+notably
+.BI remotes/dgit/dgit/ suite
+and
+.BR archive/ \fIdistro\fR / \fIversion\fR.
+
+Note that split view does not affect dgit fetch,
+and is not compatible with dgit pull.
+.TP
 .BI \-C changesfile
 Specifies the .changes file which is to be uploaded.  By default
 dgit push looks for a single .changes file in the parent directory whose
@@ -945,6 +1019,14 @@ otherwise, the changes file is expected in that directory (by
 default, in
 .BR .. ).
 .TP
+.BI \-\-upstream-commitish= upstream
+For use with --quilt=baredebian only.
+Specifies the commit containing the upstream source.
+This commit must be identical to your .orig tarball.
+The default is to look for one of the git tags
+.IB U " v" U " upstream/" U
+(in that order), where U is the upstream version.
+.TP
 .B \-\-rm-old-changes
 When doing a build, delete any changes files matching
 .IB package _ version _*.changes
@@ -983,19 +1065,14 @@ Do not delete the destination directory if clone fails.
 Generates a DEP-14 tag (eg
 .BR debian/ \fIversion\fR)
 as well as a dgit tag (eg
-.BR archive/debian/ \fIversion\fR)
-where possible.  This is the default.
+.BR archive/debian/ \fIversion\fR).
+This is the default.
 .TP
 .BI --no-dep14tag
-Do not generate a DEP-14 tag, except in split quilt view mode.
-(On servers where only the old tag format is supported,
-the dgit tag will have the DEP-14 name.
-This option does not prevent that.)
+Do not generate a DEP-14 tag, except when split view is in operation.
 .TP
-.BI --dep14tag-always
-Insist on generating a DEP-14 tag
-as well as a dgit tag.
-If the server does not support that, dgit push will fail.
+.BI --always-dep14tag
+Obsolete alias for --dep14tag, retained for compatibility.
 .TP
 .BI -D
 Prints debugging information to stderr.  Repeating the option produces
@@ -1298,6 +1375,8 @@ to provide a single git config compatible with different dgit versions.
 One of the values for the command line \-\-quilt= option; used if
 \-\-quilt is not specified.
 .TP
+.BR dgit-distro. \fIdistro\fR .split-view
+.TP
 .BR dgit-distro. \fIdistro\fR .rm-old-changes
 Boolean, used if neither \-\-rm-old-changes nor \-\-no-rm-old-changes
 is specified.  The default is not to remove.
@@ -1402,7 +1481,7 @@ or when pushing and
 .TP
 .BI dgit-distro. distro .dgit-tag-format
 .TP
-.BR dgit-distro. \fIdistro\fR .dep14tag " " want | no | always
+.BR dgit-distro. \fIdistro\fR .dep14tag " " want | no [| always ]
 .TP
 .BI dgit-distro. distro .ssh
 .TP
diff --git a/dgit.7 b/dgit.7
index 8325c06b10aaeed869c49d41345d9e24b4ea4fd3..d1e5c7ea579309a047ac495e1cef8da3256fd80b 100644 (file)
--- a/dgit.7
+++ b/dgit.7
@@ -393,7 +393,7 @@ this problem can occur if you have provided
 Debian git tooling such as git-debrebase, git-dpm or git-buildpackage
 with upstream git commit(s) or tag(s)
 which are not 100% identical to your orig tarball(s).
-.SH SPLIT VIEW QUILT MODE
+.SH SPLIT VIEW AND SPLITTING QUILT MODES
 When working with git branches intended
 for use with the `3.0 (quilt)' source format
 dgit can automatically convert a suitable
@@ -401,21 +401,33 @@ maintainer-provided git branch
 (in one of a variety of formats)
 into a dgit branch.
 
-When a split view mode is engaged
+When a splitting quilt mode is selected
 dgit build commands and
 dgit push
 will, on each invocation,
 convert the user's HEAD into the dgit view,
 so that it can be built and/or uploaded.
 
-dgit push in split view mode will push the dgit view to the dgit
+Split view mode can also be enabled explicitly
+with
+the --split-view command line option
+and
+the .split-view access configuration key.
+
+When split view is in operation,
+regardless of the quilt mode,
+any dgit-generated pseudomerges
+and any quilt fixup commits
+will appear only in the dgit view.
+dgit push
+will push the dgit view to the dgit
 git server.
 The dgit view is always a descendant of the maintainer view.
 dgit push will also make a maintainer view tag
 according to DEP-14
 and push that to the dgit git server.
 
-Split view mode must be enabled explicitly
+Splitting quilt modes must be enabled explicitly
 (by the use of the applicable command line options,
 subcommands, or configuration).
 This is because it is not possible to reliably tell
index 71bf39dc726c1131ab5797a58be2cefaefedebba..01429fb4b4dc9cc63cd5b1575e0ff2e94549b674 100755 (executable)
@@ -355,44 +355,6 @@ sub calculate_committer_authline () {
     return $&;
 }
 
-sub rm_subdir_cached ($) {
-    my ($subdir) = @_;
-    runcmd @git, qw(rm --quiet -rf --cached --ignore-unmatch), $subdir;
-}
-
-sub read_tree_subdir ($$) {
-    my ($subdir, $new_tree_object) = @_;
-    rm_subdir_cached $subdir;
-    runcmd @git, qw(read-tree), "--prefix=$subdir/", $new_tree_object;
-}
-
-sub read_tree_debian ($) {
-    my ($treeish) = @_;
-    read_tree_subdir 'debian', "$treeish:debian";
-    rm_subdir_cached 'debian/patches';
-}
-
-sub read_tree_upstream ($;$$) {
-    my ($treeish, $keep_patches, $tree_with_debian) = @_;
-    # if $tree_with_debian is supplied, will use that for debian/
-    # otherwise will save and restore it.
-    my $debian =
-       $tree_with_debian ? "$tree_with_debian:debian"
-       : cmdoutput @git, qw(write-tree --prefix=debian/);
-    runcmd @git, qw(read-tree), $treeish;
-    read_tree_subdir 'debian', $debian;
-    rm_subdir_cached 'debian/patches' unless $keep_patches;
-};
-
-sub make_commit ($$) {
-    my ($parents, $message_paras) = @_;
-    my $tree = cmdoutput @git, qw(write-tree);
-    my @cmd = (@git, qw(commit-tree), $tree);
-    push @cmd, qw(-p), $_ foreach @$parents;
-    push @cmd, qw(-m), $_ foreach @$message_paras;
-    return cmdoutput @cmd;
-}
-
 our @snag_force_opts;
 sub snag ($$;@) {
     my ($tag,$msg) = @_; # ignores extra args, for benefit of keycommits
@@ -768,7 +730,7 @@ sub merge_series_patchqueue_convert ($$$) {
            open C, ">", "../mcommit" or confess "$!";
            print C $commit or confess "$!";
            close C or confess "$!";
-           $build = cmdoutput @git, qw(hash-object -w -t commit ../mcommit);
+           $build = hash_commit '../mcommit';
        }
        $result = $build;
        mwrecknote($wrecknotes, 'merged-result', $result);
@@ -1994,39 +1956,6 @@ sub do_stitch ($;$) {
     stitch($dangling_head, $ffq_prev, $gdrlast, $ffq_prev_commitish, $prose);
 }
 
-sub upstream_commitish_search ($$) {
-    my ($upstream_version, $tried) = @_;
-    # todo: at some point maybe use git-deborig to do this
-    foreach my $tagpfx ('', 'v', 'upstream/') {
-       my $tag = $tagpfx.(dep14_version_mangle $upstream_version);
-       my $new_upstream = git_get_ref "refs/tags/$tag";
-       push @$tried, $tag;
-       return $new_upstream if length $new_upstream;
-    }
-}
-
-sub resolve_upstream_version ($$) {
-    my ($new_upstream, $upstream_version) = @_;
-
-    my $used = $new_upstream;
-    if (!defined $new_upstream) {
-       my @tried;
-       $new_upstream = upstream_commitish_search $upstream_version, \@tried;
-       if (!length $new_upstream) {
-           fail f_
-               "Could not determine appropriate upstream commitish.\n".
-               " (Tried these tags: %s)\n".
-               " Check version, and specify upstream commitish explicitly.",
-               "@tried";
-       }
-       $used = $tried[-1];
-    }
-    $new_upstream = git_rev_parse $new_upstream;
-
-    return ($new_upstream, $used);
-    # used is a human-readable idea of what we found
-}
-
 sub cmd_new_upstream () {
     # automatically and unconditionally launders before rebasing
     # if rebase --abort is used, laundering has still been done
@@ -2733,7 +2662,7 @@ sub cmd_convert_from_dgit_view () {
        my $spec = shift @ARGV;
        my $commit = git_rev_parse "$spec^{commit}";
        push @upstreams, { Commit => $commit,
-                          Source => (f_ "%s, from command line", $ARGV[0]),
+                          Source => (f_ "%s, from command line", $spec),
                           Only => 1,
                         };
     }
index cbdf292b2812e616248c06cfb8908374680d8c06..af6e3646ad32e04b552931b2249454dbb604ca84 100644 (file)
@@ -531,7 +531,7 @@ This is provided mostly for the benefit of the test suite.
 
 Requests (more) debugging.  May be repeated.
 
-=item --experimntal-merge-resolution
+=item --experimental-merge-resolution
 
 Enable experimental code for handling general merges
 (see L<git-debrebase(5)/General merges>).
index f7e5e53b473ddef2b9932ac4e7c213200649cd92..bc370d27358ccd1af68849ce2ae3cbfc12f34cee 100755 (executable)
@@ -546,6 +546,7 @@ for (;;) {
        die unless defined $rcode;
 
        $poldbh->commit;
+       printdebug "poldbh commit\n";
     };
     last unless length $@;
     die $@ unless ref $@ eq $db_busy_exception;
index a8b9400b5a4729a2c92e0c86d318385dd1512c6c..7c7262f50fce372dcfaf89c763fa3a223f04a9d7 100755 (executable)
@@ -539,15 +539,12 @@ sub readupdates () {
     STDIN->error and die $!;
 
     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;
-    }
+    my @dtags = grep { m#^archive/# } keys %tags;
+    reject "need exactly one archive/* tag" if @dtags!=1;
+    my @mtags = grep { !m#^archive/# } keys %tags;
+    reject "pushing too many non-dgit tags" if @mtags>1;
+    ($tagname) = @dtags;
+    ($maint_tagname) = @mtags;
     $tagval = $tags{$tagname};
     $maint_tagval = $tags{$maint_tagname // ''};
 
@@ -862,8 +859,7 @@ sub checks () {
     grep { $tagname eq $_ } @expecttagnames or die;
 
     foreach my $othertag (grep { $_ ne $tagname } @expecttagnames) {
-       reject "tag $othertag (pushed with differing dgit version)".
-           " already exists -".
+       reject "tag $othertag already exists -".
            " not replacing previously-pushed version"
            if git_get_ref "refs/tags/".$othertag;
     }
index a532b526bfa56efcccaf6000016914a1eb4749e9..8ee34bec4a354ec9824d098108ccb871c8061e16 100755 (executable)
@@ -52,6 +52,9 @@ restriction-gencontrol () {
 
 gencontrol-add-deps () {
        for dep in "$@"; do
+               case " $dependencies," in
+               *" "$dep","*)   continue ;;
+               esac
                dependencies+="${dependencies:+, }$dep"
        done
 }
@@ -64,6 +67,13 @@ dependencies-gencontrol () {
                GDR) gencontrol-add-deps \
                        git-debrebase git-buildpackage
                        ;;
+               DEBORIG) gencontrol-add-deps \
+                       devscripts libdpkg-perl \
+                       libgit-wrapper-perl liblist-compare-perl \
+                       libstring-shellquote-perl libtry-tiny-perl \
+                       # NB git-deborig is not compatible with
+                       #  t-tstunt-parsechangelog
+                       ;;
                *) gencontrol-add-deps "$dep" ;;
                esac
        done
@@ -104,21 +114,29 @@ finish-gencontrol () {
 
 seddery () {
        local seddery=$1
-       sed <$t -n '
+       sed <$tf -n '
                20q;
                /^: t-enumerate-tests-end$/q;
                '"$seddery"'
        '
 }
 
-for t in $(run-parts --list tests/tests); do
-       test-begin-$mode
+allsedderies () {
+       local tf=$1
        for r in $(seddery 's/^t-restrict //p'); do
                restriction-$mode
        done
        for deps in $(seddery 's/^t-dependencies //p'); do
                dependencies-$mode
        done
+       for import in $(seddery 's/^t-setup-import //p'); do
+               allsedderies tests/setup/$import
+       done
+}
+
+for t in $(run-parts --list tests/tests); do
+       test-begin-$mode
+       allsedderies $t
        test-done-$mode
 done
 
index fa553af9d5e89af67a971ad23617ff1eedbc376e..28b08e617f51787711b9d19dcb92300a86328884 100644 (file)
--- a/tests/lib
+++ b/tests/lib
@@ -520,6 +520,14 @@ t-setup-done () {
                ' $vn "$(eval "printf '%s\n' \"\$$vn\"")"
        done
 
+       perl >&4 -"I$root" -MDebian::Dgit -we '
+               foreach my $vn (grep m/^DGIT_TEST_REAL_/, keys %ENV) {
+                       print STDERR "saving-exporting $vn\n";
+                       printf "export %s=%s\n", $vn, shellquote $ENV{$vn}
+                               or die $!;
+               }
+       '
+
        (set -e; cd $tmp; tar cf $import.tar $savedirs)
 
        printf >&4 "\n%s\n" "$importeval"
@@ -736,6 +744,7 @@ t-splitbrain-pushed-good--unpack () {
        ln -s $tmp/incoming/*.orig*.tar* . ||:
        ln -s $incoming_dsc .
        ln -s ${incoming_dsc/.dsc/.debian.tar}* .
+       ln -s ${incoming_dsc/.dsc/.tar}* . ||:
        dpkg-source "$@" -x *.dsc
        cd */.
        git init
@@ -782,16 +791,21 @@ t-splitbrain-pushed-good-end-made-dep14 () {
        cd $tmp/$p
 }
 
-t-splitbrain-rm-gitignore-patch () {
+t-splitbrain-rm-1-patch () {
+       local patchname=$1
        perl -i -pe '
-               next unless $_ eq "auto-gitignore\n";
+               next unless $_ eq "'"$patchname"'\n";
                die if $counter++;
                chomp;
-               rename "debian/patches/$_", "../t-auto-gitignore" or die $!;
+               rename "debian/patches/$_", "../t-'"$patchname"'" or die $!;
                $_ = "";
        ' debian/patches/series
 }
 
+t-splitbrain-rm-gitignore-patch () {
+       t-splitbrain-rm-1-patch auto-gitignore
+}
+
 t-gbp-pushed-good () {
        local suite=${1:-sid}
        t-splitbrain-pushed-good-start
@@ -826,18 +840,27 @@ t-gbp-pushed-good () {
 }
 
 t-unapplied-pushed-good () {
+       local suite=${1:-sid}
        t-splitbrain-pushed-good-start
        t-splitbrain-pushed-good--unpack --skip-patches
        t-splitbrain-pushed-good-end-made-dep14
 }
 
 t-dpm-pushed-good () {
+       local suite=${1:-sid}
        t-splitbrain-pushed-good-start
        t-splitbrain-pushed-good--unpack
        t-splitbrain-rm-gitignore-patch
        t-splitbrain-pushed-good-end-made-dep14
 }
 
+t-split-unchanged-pushed-good () {
+       local suite=${1:-sid}
+       t-splitbrain-pushed-good-start
+       t-splitbrain-pushed-good--unpack
+       t-splitbrain-pushed-good-end-made-dep14
+}
+
 t-commit-build-push-expect-log () {
        local msg=$1
        local mpat=$2
@@ -1092,7 +1115,6 @@ t-drs () {
  t-git-config dgit-distro.test-dummy.git-url "ext::$troot/drs-git-ext %S "
  t-git-config dgit-distro.test-dummy.git-check true
  t-git-config dgit-distro.test-dummy.git-create true
- t-git-config dgit-distro.test-dummy.dgit-tag-format new,old,maint
        cp $troot/gnupg/{dd.gpg,dm.gpg,dm.txt} $tmp/.
        cp $troot/suites $tmp/.
        cp $troot/suites $tmp/suites-master
@@ -1114,15 +1136,6 @@ t-drs () {
        ln -sf /bin/true $drs_dispatch/policy-hook
 }
 
-t-newtag () {
- export tagpfx=archive/test-dummy
- t-git-config dgit-distro.test-dummy.dgit-tag-format new,maint
-}
-t-oldtag () {
- export tagpfx=test-dummy
- t-git-config dgit-distro.test-dummy.dgit-tag-format old
-}
-
 t-dsd () {
        t-drs
  t-git-config dgit-distro.test-dummy.ssh "$troot/dsd-ssh"
diff --git a/tests/lib-baredebian b/tests/lib-baredebian
new file mode 100644 (file)
index 0000000..3ef6569
--- /dev/null
@@ -0,0 +1,93 @@
+#
+
+baredebian-test-vars () {
+       quiltmode=baredebian
+       v=1.0-1
+       suite=sid
+       uv=${v%-*}
+       uvtag=v$uv
+       origbase=${p}_${uv}.orig
+       xorigcomps=''
+}
+
+baredebian-tarball-mode () {
+       git tag -d $uvtag
+       uvtag=''
+       quiltmode=baredebian+tarball
+}
+
+baredebian-test-minimum () {
+       t-expect-fail 'contradicts clean mode git-ff' \
+       t-dgit -wgf --dgit-view-save=split.f1 --$quiltmode quilt-fixup
+
+       t-dgit -wn --dgit-view-save=split.f1 --$quiltmode quilt-fixup
+}
+
+baredebian-test-core () {
+       tar --strip-components=1 -axf ../$origbase.tar.*
+       for comp in $xorigcomps; do
+               mkdir $comp
+               cd $comp
+               tar --strip-components=1 -axf ../../$origbase-$comp.tar.*
+               cd ..
+       done
+
+       cd debian
+       git clean -xdff
+       git checkout HEAD -- .
+       cd ..
+
+       # Now we are in this insane state that people seem to expect
+
+       export QUILT_PATCHES=debian/patches
+       quilt push -a
+
+       git add -Af .
+       git reset .pc
+       git diff --cached --exit-code split.f1 -- :/ :!/debian
+       git diff --exit-code HEAD..split.f1 -- :/debian
+       git reset
+
+       quilt new made-with-quilt
+       quilt add src.c
+       echo //omg >>src.c
+       quilt refresh
+
+       git add debian/patches/.
+       t-commit 'extra patch made with quilt' 1.0-2
+
+       dpkg-buildpackage -uc -us --build=source
+       # ^ Do this by hand here not because we expect users to do this
+       #   (rather than dgit build), but so that we can check that our
+       #   output is the same as users are used to.
+
+       t-dgit -wn --quilt=$quiltmode --dgit-view-save=split.b quilt-fixup
+       t-dgit -wn --quilt=$quiltmode --dgit-view-save=split.p --new push
+
+       git merge-base --is-ancestor HEAD     split.p
+       if [ "$uvtag" ]; then
+               git merge-base --is-ancestor $uvtag split.p
+               set +e; git merge-base HEAD $uvtag; rc=$?; set -e; [ $rc = 1 ]
+       fi
+
+       git clean -xdff
+       # t-pushed-good-* wants a clean tree to start with, but this
+       #  workflow expects a mess
+
+       t-splitbrain-pushed-good-start
+       t-splitbrain-pushed-good--unpack
+
+       find . -mindepth 1 -maxdepth 1          \
+               \! -path ./debian               \
+               \! -path ./.git                 \
+               -print0                         \
+               | xargs -0r rm -rf --
+
+       t-splitbrain-pushed-good-end-made-dep14
+}
+
+baredebian-test () {
+       baredebian-test-vars
+       baredebian-test-minimum
+       baredebian-test-core
+}
diff --git a/tests/setup/baredebian b/tests/setup/baredebian
new file mode 100755 (executable)
index 0000000..bbd901f
--- /dev/null
@@ -0,0 +1,34 @@
+#!/bin/bash
+set -e
+. tests/lib
+. $troot/lib-gdr
+
+t-dependencies GDR DEBORIG quilt
+
+t-archive-none example
+t-git-none
+
+t-gdr-gbp-import-core-with-queue
+
+git-deborig
+
+for b in                       \
+       patch-queue/quilt-tip   \
+       gitish-only             \
+       quilt-tip-1.1           \
+       quilt-tip-2             \
+       indep-arch              \
+; do
+       git branch -D $b
+done
+
+git rm -rf . # yikes
+git checkout HEAD -- debian
+t=$(git write-tree)
+t=$(git commit-tree -m 'Convert to bare debian' $t)
+git reset --hard $t
+
+t-setup-done '' "$(echo $p*) git mirror aq" '
+       t-select-package example
+       t-git-next-date
+'
diff --git a/tests/tests/baredebian-multitar b/tests/tests/baredebian-multitar
new file mode 100755 (executable)
index 0000000..39b6668
--- /dev/null
@@ -0,0 +1,46 @@
+#!/bin/bash
+set -e
+. tests/lib
+. $troot/lib-baredebian
+
+t-dependencies quilt
+
+t-setup-import baredebian
+t-tstunt-parsechangelog
+
+cd $p
+baredebian-test-vars
+baredebian-tarball-mode
+
+rm -f ../example_1.0.orig.tar.*
+cp $troot/pkg-srcs/${p}_${uv}.orig*.tar.* ..
+xorigcomps=docs
+
+git tag -d $uvtag
+
+baredebian-test-minimum
+baredebian-test-core
+
+combine=$(
+       git log --pretty=format:%H \
+       --grep "Combine orig tarballs for example $uv" split.p
+)
+
+parentnum=0
+for comp in '' $xorigcomps; do
+       parentnum=$(( $parentnum + 1 ))
+       fn=${origbase}${comp:+-}${comp}.tar.gz
+
+       git checkout --orphan imp$parentnum
+       git rm -rf .
+       tar --strip-components=1 -axf ../$fn
+       git add -Af .
+
+       git commit -m P$parentnum
+       git diff --stat --exit-code $combine^$parentnum
+
+       count=$(git log $combine^$parentnum | grep -Fc $fn)
+       [ $count = 2 ]
+done
+
+t-ok
diff --git a/tests/tests/baredebian-plusgit b/tests/tests/baredebian-plusgit
new file mode 100755 (executable)
index 0000000..5c53fdd
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/bash
+set -e
+. tests/lib
+. $troot/lib-baredebian
+
+t-dependencies quilt
+
+t-setup-import baredebian
+t-tstunt-parsechangelog
+
+cd $p
+baredebian-test-vars
+quiltmode=baredebian+git
+
+baredebian-test-minimum
+
+t-ok
diff --git a/tests/tests/baredebian-push b/tests/tests/baredebian-push
new file mode 100755 (executable)
index 0000000..f095601
--- /dev/null
@@ -0,0 +1,14 @@
+#!/bin/bash
+set -e
+. tests/lib
+. $troot/lib-baredebian
+
+t-dependencies quilt
+
+t-setup-import baredebian
+t-tstunt-parsechangelog
+
+cd $p
+baredebian-test
+
+t-ok
diff --git a/tests/tests/baredebian-tarball b/tests/tests/baredebian-tarball
new file mode 100755 (executable)
index 0000000..00b2ad2
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/bash
+set -e
+. tests/lib
+. $troot/lib-baredebian
+
+t-dependencies quilt
+
+t-setup-import baredebian
+t-tstunt-parsechangelog
+
+cd $p
+baredebian-test-vars
+baredebian-tarball-mode
+
+t-expect-fail 'Could not determine appropriate upstream commitish' \
+t-dgit -wn --dgit-view-save=split.g --baredebian quilt-fixup
+
+baredebian-test-minimum
+baredebian-test-core
+
+t-ok
diff --git a/tests/tests/debpolicy-taintrm b/tests/tests/debpolicy-taintrm
new file mode 100755 (executable)
index 0000000..8655fb5
--- /dev/null
@@ -0,0 +1,58 @@
+#!/bin/bash
+set -e
+. tests/lib
+
+t-tstunt-parsechangelog
+
+t-debpolicy
+t-prep-newpackage example 1.0
+
+cd $p
+revision=1
+git tag start
+t-dgit setup-mergechangelogs
+
+
+: upload
+
+t-dgit push-source --new
+
+: cut
+
+rm $tmp/incoming/*
+t-archive-none example
+
+touch -d 'now -1 day' $tmp/git/example*
+
+t-policy-periodic # xxx maybe want test both with and without this
+
+: edit
+t-commit 'edit after cut'
+
+: push, needs --deliberately
+
+t-expect-push-fail \
+ 'all previously pushed versions were found to have been removed from NEW' \
+t-dgit push-source --new
+
+t-dgit push-source --new --deliberately-include-questionable-history
+
+t-archive-process-incoming new
+
+# : t-policy-periodic # maybe want test with this
+
+: accept
+
+mv -f $tmp/aq/package.{new,sid}.$p
+t-aq-archive-updated sid $p
+
+# : t-policy-periodic # maybe want test with this
+
+t-git-dir-time-passes
+
+: 3rd push, no deliberately
+
+t-commit 'edit after accept'
+t-dgit push-source
+
+t-ok
index afaed4cf248cb0c76f4f2bd6166379159655d089..f102d9537efc1b84d1191a827e826eb5a90c35c7 100755 (executable)
@@ -110,7 +110,7 @@ mustfail 'push is missing tag ref update' $push_spec1
 mustfail 'push is missing head ref update' +$push_spec2
 mustfail 'pushing unexpected ref' $push_spec HEAD:refs/wombat
 mustfail 'pushing multiple heads' $push_spec HEAD:refs/dgit/wombat
-mustfail E:'pushing multiple tags|pushing too many similar tags' \
+mustfail E:'pushing multiple tags|pushing too many similar tags|need exactly one archive' \
        $push_spec HEAD:refs/tags/$tagpfx/wombat
 
 prep unstable sid
@@ -162,7 +162,6 @@ git commit --allow-empty -m 'Dummy update'
 mktag
 mustfail 'not replacing previously-pushed version' +$push_spec1 +$push_spec2
 
-t-newtag
 re-prep
 mktag
 mustfail 'not replacing previously-pushed version' +$push_spec1 +$push_spec2
diff --git a/tests/tests/forcesplit-linear b/tests/tests/forcesplit-linear
new file mode 100755 (executable)
index 0000000..4856d6a
--- /dev/null
@@ -0,0 +1,45 @@
+#!/bin/bash
+set -e
+. tests/lib
+
+t-tstunt-parsechangelog
+t-buildproductsdir-config
+
+t-archive example 1.0-1
+t-select-package example
+t-git-none
+
+t-dgit clone $p
+cd $p
+
+echo '/* More patch */' >>src.c
+git add src.c
+
+t-commit 'More patch' 1.0-2
+
+t-refs-same-start
+t-ref-head
+t-dgit --split-view push-source
+t-ref-head
+
+
+t-commit 'More more patch' 1.0-3
+
+t-dgit --split-view --save-dgit-view=split.b quilt-fixup
+
+git reflog expire --expire=all refs/dgit-intern/quilt-cache
+test "x$(git reflog refs/dgit-intern/quilt-cache)" = x
+
+t-refs-same-start
+t-ref-head
+t-dgit --split-view --save-dgit-view=split.p push-source
+t-ref-head
+
+suite=sid
+
+t-splitbrain-pushed-good-start
+t-splitbrain-pushed-good--unpack
+t-splitbrain-rm-1-patch more-patch.patch
+t-splitbrain-pushed-good-end-made-dep14
+
+t-ok
diff --git a/tests/tests/forcesplit-overwrite b/tests/tests/forcesplit-overwrite
new file mode 100755 (executable)
index 0000000..9600d3a
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/bash
+set -e
+. tests/lib
+
+# This tests a native package with split view, including that the
+# pseudomerges end up in the right place.
+
+t-setup-import examplegit
+
+t-select-package example
+cd example
+
+suite=stable
+
+t-commit 'No changes, just send to stable' '' stable
+
+t-refs-same-start
+t-ref-head
+
+try () {
+       t-dgit -wgf --split-view "$@" --dgit-view-save=split.p \
+               push-source stable
+}
+
+t-expect-fail E:'maintainer view tag.*not fast forward' \
+try
+
+t-expect-fail F:'debian/changelog does not mention 1.2' \
+try --overwrite
+
+try --overwrite=1.2
+git branch -f split.b split.p
+
+t-ref-head
+t-split-unchanged-pushed-good $suite
+
+t-ok
diff --git a/tests/tests/newtag-clone-nogit b/tests/tests/newtag-clone-nogit
deleted file mode 100755 (executable)
index 915f9d3..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/bash
-set -e
-. tests/lib
-t-alt-test
diff --git a/tests/tests/oldnewtagalt b/tests/tests/oldnewtagalt
deleted file mode 100755 (executable)
index 6730918..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-set -e
-. tests/lib
-
-t-setup-import examplegit
-t-tstunt-parsechangelog
-
-cd $p
-
-test-push () {
-       t-commit "$1"
-       t-dgit -wgf build-source
-       t-dgit push
-}
-
-for count in 1 2; do
-       t-oldtag
-       test-push "oldtag $count"
-
-       t-newtag
-       test-push "newtag $count"
-done
-
-t-ok
-
diff --git a/tests/tests/oldtag-clone-nogit b/tests/tests/oldtag-clone-nogit
deleted file mode 100755 (executable)
index 915f9d3..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/bash
-set -e
-. tests/lib
-t-alt-test
index 06d1a987159dded6c3df000c1e1f197ff7164679..815c207bf999ae19e29c5cf85b2509a75790c13d 100755 (executable)
@@ -5,7 +5,6 @@ set -e
 t-tstunt-parsechangelog
 
 t-gbp-example-prep-no-ff
-t-newtag
 
 t-dgit --quilt=gbp --dgit-view-save=split.b build-source
 
index cf148f613ee6f3b6ac150828cefa86154f579e69..e502f6b2b21897efe4532233a6947a9f1402e0e0 100755 (executable)
@@ -25,11 +25,6 @@ t-gbp-example-prep
 t-expect-fail 'quilt fixup cannot be linear' \
   t-dgit build-source
 
-t-git-config dgit-distro.test-dummy.dgit-tag-format new
-t-expect-fail 'requires split view so server needs to support' \
-t-dgit -wgf --quilt=gbp build-source
-t-newtag
-
 t-dgit --quilt=gbp --dgit-view-save=split.b1 build-source
 git rev-parse split.b1
 
index 6886cf89652d6a222b0eb3b38aa36df58581af3d..f8d8091f38504324a7e57fe1cc81a389185afebe 100755 (executable)
@@ -12,8 +12,6 @@ suitespecs+=' stable'
 
 t-tstunt-parsechangelog
 
-t-newtag
-
 # Easiest way to make a patches-unapplied but not-gbp tree is
 # to take the patches-unapplied tree and by-hand commit the .gitignore
 # changes as a debian patch.
@@ -29,7 +27,7 @@ want-success () {
        t-dgit "$@" --quilt=$qmode --dgit-view-save=split.b build-source
 
        t-dgit "$@" --quilt=$qmode --dgit-view-save=split.p push
-       t-$qmode-pushed-good
+       t-$qmode-pushed-good $suite
 }
 
 
index 10df9ae4e258bbaf0ffa3e43647199fc55ea3aa2..bb49fc61c3517a2cd667c5cc08dbfc82026113e0 100755 (executable)
@@ -4,8 +4,6 @@ set -e
 
 t-tstunt-parsechangelog
 
-t-newtag
-
 t-gbp-example-prep
 
 t-dgit -wgf --quilt=gbp --dgit-view-save=split.b build-source