chiark / gitweb /
Merge branch stable into master
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 7 Jan 2018 12:39:49 +0000 (12:39 +0000)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 7 Jan 2018 14:41:18 +0000 (14:41 +0000)
Also regenerate debian/tests/control

Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
21 files changed:
Debian/Dgit.pm
debian/changelog
debian/control
debian/tests/control
dgit
dgit-maint-gbp.7.pod
dgit-maint-merge.7.pod
dgit-maint-native.7.pod
dgit-sponsorship.7.pod
dgit-user.7.pod
dgit.1
infra/dgit-repos-server
tests/lib
tests/run-all
tests/tests/dpkgsourceignores-correct [new file with mode: 0755]
tests/tests/dpkgsourceignores-docs [new file with mode: 0755]
tests/tests/gitworktree [new file with mode: 0755]
tests/tests/sbuild-gitish [new file with mode: 0755]
tests/tstunt/gpg
tests/worktrees/example_1.0.tar
using-these [new file with mode: 0755]

index dcecbd1a840e7f0c7718b398d585e2e4d4e3fe77..c4a61af1f081b62f21938ac86783a0deef000286 100644 (file)
@@ -29,6 +29,8 @@ use Config;
 use Digest::SHA;
 use Data::Dumper;
 use IPC::Open2;
+use File::Path;
+use File::Basename;
 
 BEGIN {
     use Exporter   ();
@@ -42,10 +44,10 @@ BEGIN {
                      server_branch server_ref
                       stat_exists link_ltarget
                      hashfile
-                      fail ensuredir executable_on_path
+                      fail ensuredir must_getcwd executable_on_path
                       waitstatusmsg failedcmd_waitstatus
                      failedcmd_report_cmd failedcmd
-                      cmdoutput cmdoutput_errok
+                      runcmd cmdoutput cmdoutput_errok
                       git_rev_parse git_cat_file
                      git_get_ref git_for_each_ref
                       git_for_each_tag_referring is_fast_fwd
@@ -55,10 +57,18 @@ BEGIN {
                       initdebug enabledebug enabledebuglevel
                       printdebug debugcmd
                       $debugprefix *debuglevel *DEBUG
-                      shellquote printcmd messagequote);
+                      shellquote printcmd messagequote
+                      $negate_harmful_gitattrs
+                     changedir git_slurp_config_src
+                     playtree_setup);
     # implicitly uses $main::us
-    %EXPORT_TAGS = ( policyflags => [qw(NOFFCHECK FRESHREPO NOCOMMITCHECK)] );
-    @EXPORT_OK   = @{ $EXPORT_TAGS{policyflags} };
+    %EXPORT_TAGS = ( policyflags => [qw(NOFFCHECK FRESHREPO NOCOMMITCHECK)],
+                    playground => [qw(record_maindir $maindir $local_git_cfg
+                                      $maindir_gitdir $maindir_gitcommon
+                                      fresh_playground $playground
+                                       ensure_a_playground)]);
+    @EXPORT_OK   = ( @{ $EXPORT_TAGS{policyflags} },
+                    @{ $EXPORT_TAGS{playground} } );
 }
 
 our @EXPORT_OK;
@@ -82,6 +92,8 @@ sub NOCOMMITCHECK () { return 0x8; }
 our $debugprefix;
 our $debuglevel = 0;
 
+our $negate_harmful_gitattrs = "-text -eol -crlf -ident -filter";
+
 our $forkcheck_mainprocess;
 
 sub forkcheck_setup () {
@@ -216,6 +228,12 @@ sub ensuredir ($) {
     die "mkdir $dir: $!";
 }
 
+sub must_getcwd () {
+    my $d = getcwd();
+    defined $d or fail "getcwd failed: $!";
+    return $d;
+}
+
 sub executable_on_path ($) {
     my ($program) = @_;
     return 1 if $program =~ m{/};
@@ -276,6 +294,12 @@ sub failedcmd {
     fail failedcmd_waitstatus();
 }
 
+sub runcmd {
+    debugcmd "+",@_;
+    $!=0; $?=-1;
+    failedcmd @_ if system @_;
+}
+
 sub cmdoutput_errok {
     confess Dumper(\@_)." ?" if grep { !defined } @_;
     debugcmd "|",@_;
@@ -406,4 +430,169 @@ sub is_fast_fwd ($$) {
     }
 }
 
+sub changedir ($) {
+    my ($newdir) = @_;
+    printdebug "CD $newdir\n";
+    chdir $newdir or confess "chdir: $newdir: $!";
+}
+
+sub git_slurp_config_src ($) {
+    my ($src) = @_;
+    # returns $r such that $r->{KEY}[] = VALUE
+    my @cmd = (qw(git config -z --get-regexp), "--$src", qw(.*));
+    debugcmd "|",@cmd;
+
+    local ($debuglevel) = $debuglevel-2;
+    local $/="\0";
+
+    my $r = { };
+    open GITS, "-|", @cmd or die $!;
+    while (<GITS>) {
+       chomp or die;
+       printdebug "=> ", (messagequote $_), "\n";
+       m/\n/ or die "$_ ?";
+       push @{ $r->{$`} }, $'; #';
+    }
+    $!=0; $?=0;
+    close GITS
+       or ($!==0 && $?==256)
+       or failedcmd @cmd;
+    return $r;
+}
+
+# ========== playground handling ==========
+
+# terminology:
+#
+#   $maindir      user's git working tree
+#   $playground   area in .git/ where we can make files, unpack, etc. etc.
+#   playtree      git working tree sharing object store with the user's
+#                 inside playground, or identical to it
+#
+# other globals
+#
+#   $local_git_cfg    hash of arrays of values: git config from $maindir
+#
+# expected calling pattern
+#
+#  firstly
+#
+#    [record_maindir]
+#      must be run in directory containing .git
+#      assigns to $maindir if not already set
+#      also calls git_slurp_config_src to record git config
+#        in $local_git_cfg, unless it's already set
+#
+#    fresh_playground SUBDIR_PATH_COMPONENTS
+#      e.g fresh_playground 'dgit/unpack' ('.git/' is implied)
+#      default SUBDIR_PATH_COMPONENTS is $playground_subdir
+#      calls record_maindir
+#      sets up a new playground (destroying any old one)
+#      assigns to $playground and returns the same pathname
+#      caller may call multiple times with different subdir paths
+#       createing different playgrounds; but $playground global can
+#       refer only to one, obv.
+#
+#    ensure_a_playground SUBDIR_PATH_COMPONENTS
+#      like fresh_playground except:
+#      merely ensures the directory exists; does not delete an existing one
+#      never sets global $playground
+#
+#  then can use
+#
+#    changedir $playground
+#    changedir $maindir
+#
+#    playtree_setup $local_git_cfg
+#            # ^ call in some (perhaps trivial) subdir of $playground
+#
+#    rmtree $playground
+
+# ----- maindir -----
+
+# these three all go together
+our $maindir;
+our $maindir_gitdir;
+our $maindir_gitcommon;
+
+our $local_git_cfg;
+
+sub record_maindir () {
+    if (!defined $maindir) {
+       $maindir = must_getcwd();
+       if (!stat "$maindir/.git") {
+           fail "cannot stat $maindir/.git: $!";
+       }
+       if (-d _) {
+           # we fall back to this in case we have a pre-worktree
+           # git, which may not know git rev-parse --git-common-dir
+           $maindir_gitdir    = "$maindir/.git";
+           $maindir_gitcommon = "$maindir/.git";
+       } else {
+           $maindir_gitdir    = cmdoutput qw(git rev-parse --git-dir);
+           $maindir_gitcommon = cmdoutput qw(git rev-parse --git-common-dir);
+       }
+    }
+    $local_git_cfg //= git_slurp_config_src 'local';
+}
+
+# ----- playgrounds -----
+
+our $playground;
+
+sub ensure_a_playground_parent ($) {
+    my ($spc) = @_;
+    record_maindir();
+    $spc = "$maindir_gitdir/$spc";
+    my $parent = dirname $spc;
+    mkdir $parent or $!==EEXIST
+       or fail "failed to mkdir playground parent $parent: $!";
+    return $spc;
+}    
+
+sub ensure_a_playground ($) {
+    my ($spc) = @_;
+    $spc = ensure_a_playground_parent $spc;
+    mkdir $spc or $!==EEXIST or fail "failed to mkdir a playground $spc: $!";
+    return $spc;
+}    
+
+sub fresh_playground ($) {
+    my ($spc) = @_;
+    $spc = ensure_a_playground_parent $spc;
+    rmtree $spc;
+    mkdir $spc or fail "failed to mkdir the playground $spc: $!";
+    return $playground = $spc;
+}
+
+# ----- playtrees -----
+
+sub playtree_setup (;$) {
+    my ($t_local_git_cfg) = @_;
+    $t_local_git_cfg //= $local_git_cfg;
+    # for use in the playtree
+    # $maindir must be set, eg by calling record_maindir or fresh_playground
+    runcmd qw(git init -q);
+    runcmd qw(git config gc.auto 0);
+    foreach my $copy (qw(user.email user.name user.useConfigOnly
+                         core.sharedRepository
+                         core.compression core.looseCompression
+                         core.bigFileThreshold core.fsyncObjectFiles)) {
+       my $v = $t_local_git_cfg->{$copy};
+       next unless $v;
+       runcmd qw(git config), $copy, $_ foreach @$v;
+    }
+    # this is confusing: we have
+    #   .                   playtree, not a worktree, has .git/, our cwd
+    #   $maindir            might be a worktree so
+    #   $maindir_gitdir     contains our main working "dgit", HEAD, etc.
+    #   $maindir_gitcommon  the shared stuff, including .objects
+    rmtree('.git/objects');
+    symlink "$maindir_gitcommon/objects",'.git/objects' or die $!;
+    ensuredir '.git/info';
+    open GA, "> .git/info/attributes" or die $!;
+    print GA "* $negate_harmful_gitattrs\n" or die $!;
+    close GA or die $!;
+}
+
 1;
index 4209e71a43ee3ab9ac69b1943f8848806458ae4a..9fd6eddf35d3e289771e045864bda7066e0be471 100644 (file)
@@ -1,3 +1,34 @@
+dgit (4.2~) experimental; urgency=medium
+
+  Documentation improvements:
+  * dgit(1): Add a bit more rationale (polemic, even).  Closes:#874221.
+  * Recommend mk-build-deps rather than apt-get build-dep.
+    Suggestion from Nikolaus Rath.  Closes:#863361.
+  * dgit-maint-merge(7): many updates.  [Sean Whitton]
+    Closes:#864873,#878433.
+  * dgit-*(7): Mention first upload trick.  [Andrew Shadura,
+    Sean Whitton]  Closes:#856402.
+
+  Minor fixes:
+  * When source discrepancy involves file mode changes, report them
+    specially.  Closes:#886442.
+  * In split brain mode, with unexpected diffs, print dgit view
+    commitid in suggested diff rune.  (HEAD is wrong.)  Closes:#886443.
+  * Fix message about missing quilt cache entry to refer to
+    HEAD rather than tree, since dgit needs a commit.  Closes:#884646.
+  * Fix grammar error in 4.1 changelog entry. [Sean Whitton]
+  * Remove some whitespace "errors". [Sean Whitton]
+
+  Packaging:
+  * Remove dependency alternative on realpath (package last existed in
+    Debian wheezy).  Closes:#877552.
+
+  Test suite:
+  * dpkgsourceignores-docs: Correct restriction (so autopkgtest
+    won't try to run it).
+
+ --
+
 dgit (3.13) unstable; urgency=high
 
   Important bugfixes to dgit:
@@ -9,6 +40,52 @@ dgit (3.13) unstable; urgency=high
 
  -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Sun, 22 Oct 2017 17:51:12 +0100
 
+dgit (4.1) experimental; urgency=medium
+
+  Important improvements to dgit:
+  * Support for `git worktree' worktrees.  There may still be
+    bugs; the tests for this are not very comprehensive.  And
+    worktrees on different filesystems may not work; that's a
+    matter for the future.  Closes:#868515.
+  * Change the dpkg-source -i argument to exclude exactly the right
+    set of things.  (Sadly this is not a simple rune.)
+
+  Other improvements to dgit:
+  * New print-dpkg-source-ignores option to print the big rune
+    you need to pass to dpkg-source to make it work exactly right.
+  * Properly shell-quote the --git-builder argument to gbp.
+
+  Documentation:
+  * dgit-user(7): Provide information about how to use sbuild.
+    Quite ugly due to #868527.  Closes:#868526.
+  * dgit-user(7): Fixed example rune to use curl (which prints
+    to stdout, as the rune expects).  [reported by Simon Tatham]
+
+  Minor improvements:
+  * Do not leave many clog-* files in .git/dgit.
+
+  Internal changes:
+  * using-these: New script to help with ad-hoc-testing.
+  * Refactoring in preparation for push-source [Sean Whitton].
+
+  Test suite:
+  * sbuild-gitish: New test case to check running sbuild from git
+  * Work around gnupg agent connection races by having our stunt
+    gpg wrapper simply try running gpg again, once, if it exits 2.
+    This does not fully suppress the bug but it does significantly reduce
+    the probability.
+  * Other tests for new features.
+  * Various refactoring.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Mon, 14 Aug 2017 09:31:03 +0100
+
+dgit (4.0) experimental; urgency=low
+
+  * dgit: --deliberately-not-fast-forward works properly in
+    split view quilt modes (suppressing the pseudomerge).
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Sun, 12 Feb 2017 22:22:31 +0000
+
 dgit (3.12) unstable; urgency=high
 
   Important bugfixes to dgit:
index 4d4c3042c5316642ad448fb372ee4146bcd96bef..4405e141009b8dc147c024642f398189a9c7a8aa 100644 (file)
@@ -11,7 +11,7 @@ Vcs-Browser: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git/dgit.git/
 Package: dgit
 Depends: perl, libwww-perl, libdpkg-perl, git-core, devscripts, dpkg-dev,
          ${misc:Depends}, git-buildpackage, liblist-moreutils-perl,
-         coreutils (>= 8.23-1~) | realpath,
+         coreutils (>= 8.23-1~),
          libdigest-sha-perl, dput, curl, apt,
          libjson-perl, ca-certificates,
          libtext-iconv-perl, libtext-glob-perl
index 76fe9bd5298f8ffebd331fe9e48bc3f422ebcb18..7d2a4867f34559f933331195639ef4af56a19a12 100644 (file)
@@ -6,6 +6,11 @@ Tests: clone-reprepro downstream-gitless
 Tests-Directory: tests/tests
 Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, reprepro
 
+Tests: dpkgsourceignores-docs
+Tests-Directory: tests/tests
+Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin
+Restrictions: x-dgit-intree-only
+
 Tests: defdistro-dsd-clone-drs dsd-clone-drs
 Tests-Directory: tests/tests
 Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin
@@ -24,12 +29,17 @@ Tests-Directory: tests/tests
 Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, sbuild
 Restrictions: x-dgit-schroot-build
 
+Tests: sbuild-gitish
+Tests-Directory: tests/tests
+Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, sbuild, man-db
+Restrictions: x-dgit-schroot-build
+
 Tests: spelling
 Tests-Directory: tests/tests
 Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin
 Restrictions: x-dgit-git-only
 
-Tests: absurd-gitapply badcommit-rewrite build-modes build-modes-asplit build-modes-gbp-asplit clone-clogsigpipe clone-gitnosuite clone-nogit debpolicy-dbretry debpolicy-newreject debpolicy-quilt-gbp defdistro-rpush defdistro-setup distropatches-reject drs-clone-nogit drs-push-masterupdate drs-push-rejects dsd-clone-nogit dsd-divert fetch-localgitonly fetch-somegit-notlast gbp-orig gitconfig import-dsc 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 protocol-compat push-buildproductsdir push-newpackage push-nextdgit quilt quilt-gbp quilt-gbp-build-modes quilt-singlepatch quilt-splitbrains quilt-useremail rpush tag-updates test-list-uptodate trustingpolicy-replay unrepresentable version-opt
+Tests: absurd-gitapply badcommit-rewrite build-modes build-modes-asplit build-modes-gbp-asplit 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-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 protocol-compat push-buildproductsdir push-newpackage push-nextdgit quilt quilt-gbp quilt-gbp-build-modes quilt-singlepatch quilt-splitbrains quilt-useremail rpush tag-updates test-list-uptodate trustingpolicy-replay unrepresentable version-opt
 Tests-Directory: tests/tests
 Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin
 
diff --git a/dgit b/dgit
index afe209e2f16fa61cf84828b689fc9021c6ab8672..31e6bd581b7453d23519d9aa01983e5b21061e4a 100755 (executable)
--- a/dgit
+++ b/dgit
@@ -19,7 +19,7 @@
 
 use strict;
 
-use Debian::Dgit;
+use Debian::Dgit qw(:DEFAULT :playground);
 setup_sigwarn();
 
 use IO::Handle;
@@ -49,6 +49,8 @@ our $absurdity = undef; ###substituted###
 our @rpushprotovsn_support = qw(4 3 2); # 4 is new tag format
 our $protovsn;
 
+our $cmd;
+our $subcommand;
 our $isuite;
 our $idistro;
 our $package;
@@ -100,6 +102,8 @@ our $git_authline_re = '^([^<>]+) \<(\S+)\> (\d+ [-+]\d+)$';
 our $splitbraincache = 'dgit-intern/quilt-cache';
 our $rewritemap = 'dgit-rewrite/map';
 
+our @dpkg_source_ignores = qw(-i(?:^|/)\.git(?:/|$) -I.git);
+
 our (@git) = qw(git);
 our (@dget) = qw(dget);
 our (@curl) = (qw(curl --proto-redir), '-all,http,https', qw(-L));
@@ -111,8 +115,8 @@ our (@ssh) = 'ssh';
 our (@dgit) = qw(dgit);
 our (@aptget) = qw(apt-get);
 our (@aptcache) = qw(apt-cache);
-our (@dpkgbuildpackage) = qw(dpkg-buildpackage -i\.git/ -I.git);
-our (@dpkgsource) = qw(dpkg-source -i\.git/ -I.git);
+our (@dpkgbuildpackage) = (qw(dpkg-buildpackage), @dpkg_source_ignores);
+our (@dpkgsource) = (qw(dpkg-source), @dpkg_source_ignores);
 our (@dpkggenchanges) = qw(dpkg-genchanges);
 our (@mergechanges) = qw(mergechanges -f);
 our (@gbp_build) = ('');
@@ -251,12 +255,6 @@ sub no_such_package () {
     exit 4;
 }
 
-sub changedir ($) {
-    my ($newdir) = @_;
-    printdebug "CD $newdir\n";
-    chdir $newdir or confess "chdir: $newdir: $!";
-}
-
 sub deliberately ($) {
     my ($enquiry) = @_;
     return !!grep { $_ eq "--deliberately-$enquiry" } @deliberatelies;
@@ -283,6 +281,10 @@ sub gbp_pq {
     return opts_opt_multi_cmd @gbp_pq;
 }
 
+sub dgit_privdir () {
+    our $dgit_privdir_made //= ensure_a_playground 'dgit';
+}
+
 #---------- remote protocol support, common ----------
 
 # remote push initiator/responder protocol:
@@ -495,12 +497,6 @@ sub url_get {
 
 our ($dscdata,$dscurl,$dsc,$dsc_checked,$skew_warning_vsn);
 
-sub runcmd {
-    debugcmd "+",@_;
-    $!=0; $?=-1;
-    failedcmd @_ if system @_;
-}
-
 sub act_local () { return $dryrun_level <= 1; }
 sub act_scary () { return !$dryrun_level; }
 
@@ -569,7 +565,7 @@ sub nextarg {
 }
 
 sub pre_help () {
-    no_local_git_cfg();
+    not_necessarily_a_tree();
 }
 sub cmd_help () {
     print $helpmsg or die $!;
@@ -647,32 +643,17 @@ our %defcfg = ('dgit.default.distro' => 'debian',
 
 our %gitcfgs;
 our @gitcfgsources = qw(cmdline local global system);
+our $invoked_in_git_tree = 1;
 
 sub git_slurp_config () {
-    local ($debuglevel) = $debuglevel-2;
-    local $/="\0";
-
     # This algoritm is a bit subtle, but this is needed so that for
     # options which we want to be single-valued, we allow the
     # different config sources to override properly.  See #835858.
     foreach my $src (@gitcfgsources) {
        next if $src eq 'cmdline';
        # we do this ourselves since git doesn't handle it
-       
-       my @cmd = (@git, qw(config -z --get-regexp), "--$src", qw(.*));
-       debugcmd "|",@cmd;
 
-       open GITS, "-|", @cmd or die $!;
-       while (<GITS>) {
-           chomp or die;
-           printdebug "=> ", (messagequote $_), "\n";
-           m/\n/ or die "$_ ?";
-           push @{ $gitcfgs{$src}{$`} }, $'; #';
-       }
-       $!=0; $?=0;
-       close GITS
-           or ($!==0 && $?==256)
-           or failedcmd @cmd;
+       $gitcfgs{$src} = git_slurp_config_src $src;
     }
 }
 
@@ -709,9 +690,10 @@ sub cfg {
        "$us: distro or suite appears not to be (properly) supported";
 }
 
-sub no_local_git_cfg () {
+sub not_necessarily_a_tree () {
     # needs to be called from pre_*
     @gitcfgsources = grep { $_ ne 'local' } @gitcfgsources;
+    $invoked_in_git_tree = 0;
 }
 
 sub access_basedistro__noalias () {
@@ -1006,19 +988,13 @@ sub commit_getclogp ($) {
     our %commit_getclogp_memo;
     my $memo = $commit_getclogp_memo{$objid};
     return $memo if $memo;
-    mkpath '.git/dgit';
-    my $mclog = ".git/dgit/clog-$objid";
+
+    my $mclog = dgit_privdir()."clog";
     runcmd shell_cmd "exec >$mclog", @git, qw(cat-file blob),
        "$objid:debian/changelog";
     $commit_getclogp_memo{$objid} = parsechangelog("-l$mclog");
 }
 
-sub must_getcwd () {
-    my $d = getcwd();
-    defined $d or fail "getcwd failed: $!";
-    return $d;
-}
-
 sub parse_dscdata () {
     my $dscfh = new IO::File \$dscdata, '<' or die $!;
     printdebug Dumper($dscdata) if $debuglevel>1;
@@ -1702,30 +1678,14 @@ sub create_remote_git_repo () {
 our ($dsc_hash,$lastpush_mergeinput);
 our ($dsc_distro, $dsc_hint_tag, $dsc_hint_url);
 
-our $ud = '.git/dgit/unpack';
 
-sub prep_ud (;$) {
-    my ($d) = @_;
-    $d //= $ud;
-    rmtree($d);
-    mkpath '.git/dgit';
-    mkdir $d or die $!;
+sub prep_ud () {
+    dgit_privdir(); # ensures that $dgit_privdir_made is based on $maindir
+    fresh_playground 'dgit/unpack';
 }
 
 sub mktree_in_ud_here () {
-    runcmd qw(git init -q);
-    runcmd qw(git config gc.auto 0);
-    foreach my $copy (qw(user.email user.name user.useConfigOnly
-                         core.sharedRepository
-                         core.compression core.looseCompression
-                         core.bigFileThreshold core.fsyncObjectFiles)) {
-       my $v = $gitcfgs{local}{$copy};
-       next unless $v;
-       runcmd qw(git config), $copy, $_ foreach @$v;
-    }
-    rmtree('.git/objects');
-    symlink '../../../../objects','.git/objects' or die $!;
-    setup_gitattrs(1);
+    playtree_setup $gitcfgs{local};
 }
 
 sub git_write_tree () {
@@ -1758,8 +1718,8 @@ sub remove_stray_gits ($) {
 
 sub mktree_in_ud_from_only_subdir ($;$) {
     my ($what,$raw) = @_;
-
     # changes into the subdir
+
     my (@dirs) = <*/.>;
     die "expected one subdir but found @dirs ?" unless @dirs==1;
     $dirs[0] =~ m#^([^/]+)/\.$# or die;
@@ -2100,13 +2060,13 @@ sub generate_commits_from_dsc () {
     # See big comment in fetch_from_archive, below.
     # See also README.dsc-import.
     prep_ud();
-    changedir $ud;
+    changedir $playground;
 
     my @dfi = dsc_files_info();
     foreach my $fi (@dfi) {
        my $f = $fi->{Filename};
        die "$f ?" if $f =~ m#/|^\.|\.dsc$|\.tmp$#;
-       my $upper_f = "../../../../$f";
+       my $upper_f = "$maindir/../$f";
 
        printdebug "considering reusing $f: ";
 
@@ -2426,6 +2386,10 @@ END
 
        my $path = $ENV{PATH} or die;
 
+       # we use ../../gbp-pq-output, which (given that we are in
+       # $playground/PLAYTREE, and $playground is .git/dgit/unpack,
+       # is .git/dgit.
+
        foreach my $use_absurd (qw(0 1)) {
            runcmd @git, qw(checkout -q unpa);
            runcmd @git, qw(update-ref -d refs/heads/patch-queue/unpa);
@@ -2510,8 +2474,8 @@ END
            @output = $lastpush_mergeinput;
        }
     }
-    changedir '../../../..';
-    rmtree($ud);
+    changedir $maindir;
+    rmtree $playground;
     return @output;
 }
 
@@ -3208,7 +3172,7 @@ END
        my $author = clogp_authline $useclogp;
        my $cversion = getfield $useclogp, 'Version';
 
-       my $mcf = ".git/dgit/mergecommit";
+       my $mcf = dgit_privdir()."/mergecommit";
        open MC, ">", $mcf or die "$mcf $!";
        print MC <<END or die $!;
 tree $tree
@@ -3268,7 +3232,6 @@ END
     fetch_from_archive_record_1($hash);
 
     if (defined $skew_warning_vsn) {
-       mkpath '.git/dgit';
        printdebug "SKEW CHECK WANT $skew_warning_vsn\n";
        my $gotclogp = commit_getclogp($hash);
        my $got_vsn = getfield $gotclogp, 'Version';
@@ -3307,8 +3270,9 @@ sub setup_mergechangelogs (;$) {
 
     my $driver = 'dpkg-mergechangelogs';
     my $cb = "merge.$driver";
-    my $attrs = '.git/info/attributes';
-    ensuredir '.git/info';
+    confess unless defined $maindir;
+    my $attrs = "$maindir_gitcommon/info/attributes";
+    ensuredir "$maindir_gitcommon/info";
 
     open NATTRS, ">", "$attrs.new" or die "$attrs.new $!";
     if (!open ATTRS, "<", $attrs) {
@@ -3353,15 +3317,16 @@ sub ensure_setup_existing_tree () {
     set_local_git_config $k, 'true';
 }
 
-sub open_gitattrs () {
-    my $gai = new IO::File ".git/info/attributes"
+sub open_main_gitattrs () {
+    confess 'internal error no maindir' unless defined $maindir;
+    my $gai = new IO::File "$maindir_gitcommon/info/attributes"
        or $!==ENOENT
-       or die "open .git/info/attributes: $!";
+       or die "open $maindir_gitcommon/info/attributes: $!";
     return $gai;
 }
 
 sub is_gitattrs_setup () {
-    my $gai = open_gitattrs();
+    my $gai = open_main_gitattrs();
     return 0 unless $gai;
     while (<$gai>) {
        return 1 if m{^\[attr\]dgit-defuse-attrs\s};
@@ -3381,15 +3346,15 @@ sub setup_gitattrs (;$) {
 END
        return;
     }
-    my $af = ".git/info/attributes";
-    ensuredir '.git/info';
+    my $af = "$maindir_gitcommon/info/attributes";
+    ensuredir "$maindir_gitcommon/info";
     open GAO, "> $af.new" or die $!;
     print GAO <<END or die $!;
 *      dgit-defuse-attrs
-[attr]dgit-defuse-attrs        -text -eol -crlf -ident -filter
+[attr]dgit-defuse-attrs        $negate_harmful_gitattrs
 # ^ see GITATTRIBUTES in dgit(7) and dgit setup-new-tree in dgit(1)
 END
-    my $gai = open_gitattrs();
+    my $gai = open_main_gitattrs();
     if ($gai) {
        while (<$gai>) {
            chomp;
@@ -3625,6 +3590,7 @@ sub clone ($) {
     my $multi_fetched = fork_for_multisuite(sub {
         printdebug "multi clone before fetch merge\n";
         changedir $dstdir;
+       record_maindir();
     });
     if ($multi_fetched) {
         printdebug "multi clone after fetch merge\n";
@@ -3639,6 +3605,7 @@ sub clone ($) {
     mkdir $dstdir or fail "create \`$dstdir': $!";
     changedir $dstdir;
     runcmd @git, qw(init -q);
+    record_maindir();
     setup_new_tree();
     clone_set_head();
     my $giturl = access_giturl(1);
@@ -3778,7 +3745,7 @@ sub maybe_split_brain_save ($$$) {
     my ($headref, $dgitview, $msg) = @_;
     # => message fragment "$saved" describing disposition of $dgitview
     return "commit id $dgitview" unless defined $split_brain_save;
-    my @cmd = (shell_cmd "cd ../../../..",
+    my @cmd = (shell_cmd 'cd "$1"; shift', $maindir,
               @git, qw(update-ref -m),
               "dgit --dgit-view-save $msg HEAD=$headref",
               $split_brain_save, $dgitview);
@@ -3879,8 +3846,7 @@ sub pseudomerge_make_commit ($$$$ $$) {
        : !length  $overwrite_version ? " --overwrite"
        : " --overwrite=".$overwrite_version;
 
-    mkpath '.git/dgit';
-    my $pmf = ".git/dgit/pseudomerge";
+    my $pmf = dgit_privdir()."/pseudomerge";
     open MC, ">", $pmf or die "$pmf $!";
     print MC <<END or die $!;
 tree $tree
@@ -3915,6 +3881,7 @@ sub splitbrain_pseudomerge ($$$$) {
     #
 
     return $dgitview unless defined $archive_hash;
+    return $dgitview if deliberately_not_fast_forward();
 
     printdebug "splitbrain_pseudomerge...\n";
 
@@ -4187,7 +4154,7 @@ END
     rpush_handle_protovsn_bothends() if $we_are_initiator;
     select_tagformat();
 
-    my $clogpfn = ".git/dgit/changelog.822.tmp";
+    my $clogpfn = dgit_privdir()."/changelog.822.tmp";
     runcmd shell_cmd "exec >$clogpfn", qw(dpkg-parsechangelog);
 
     responder_send_file('parsed-changelog', $clogpfn);
@@ -4216,20 +4183,20 @@ END
     if (madformat_wantfixup($format)) {
        # user might have not used dgit build, so maybe do this now:
        if (quiltmode_splitbrain()) {
-           changedir $ud;
+           changedir $playground;
            quilt_make_fake_dsc($upstreamversion);
            my $cachekey;
            ($dgithead, $cachekey) =
                quilt_check_splitbrain_cache($actualhead, $upstreamversion);
            $dgithead or fail
  "--quilt=$quilt_mode but no cached dgit view:
- perhaps tree changed since dgit build[-source] ?";
+ perhaps HEAD changed since dgit build[-source] ?";
            $split_brain = 1;
            $dgithead = splitbrain_pseudomerge($clogp,
                                               $actualhead, $dgithead,
                                               $archive_hash);
            $maintviewhead = $actualhead;
-           changedir '../../../..';
+           changedir $maindir;
            prep_ud(); # so _only_subdir() works, below
        } else {
            commit_quilty_patch();
@@ -4260,26 +4227,55 @@ END
        }
     }
 
-    changedir $ud;
+    changedir $playground;
     progress "checking that $dscfn corresponds to HEAD";
     runcmd qw(dpkg-source -x --),
-        $dscpath =~ m#^/# ? $dscpath : "../../../$dscpath";
+        $dscpath =~ m#^/# ? $dscpath : "$maindir/$dscpath";
     my ($tree,$dir) = mktree_in_ud_from_only_subdir("source package");
     check_for_vendor_patches() if madformat($dsc->{format});
-    changedir '../../../..';
+    changedir $maindir;
     my @diffcmd = (@git, qw(diff --quiet), $tree, $dgithead);
     debugcmd "+",@diffcmd;
     $!=0; $?=-1;
     my $r = system @diffcmd;
     if ($r) {
        if ($r==256) {
+           my $referent = $split_brain ? $dgithead : 'HEAD';
            my $diffs = cmdoutput @git, qw(diff --stat), $tree, $dgithead;
-           fail <<END
+
+           my @mode_changes;
+           my $raw = cmdoutput @git,
+               qw(diff --no-renames -z -r --raw), $tree, $dgithead;
+           my $changed;
+           foreach (split /\0/, $raw) {
+               if (defined $changed) {
+                   push @mode_changes, "$changed: $_\n" if $changed;
+                   $changed = undef;
+                   next;
+               } elsif (m/^:0+ 0+ /) {
+                   $changed = '';
+               } elsif (m/^:(?:10*)?(\d+) (?:10*)?(\d+) /) {
+                   $changed = "Mode change from $1 to $2"
+               } else {
+                   die "$_ ?";
+               }
+           }
+           if (@mode_changes) {
+               fail <<END.(join '', @mode_changes).<<END;
+HEAD specifies a different tree to $dscfn:
+$diffs
+END
+There is a problem with your source tree (see dgit(7) for some hints).
+To see a full diff, run git diff $tree $referent
+END
+           }
+
+           fail <<END;
 HEAD specifies a different tree to $dscfn:
 $diffs
 Perhaps you forgot to build.  Or perhaps there is a problem with your
  source tree (see dgit(7) for some hints).  To see a full diff, run
-   git diff $tree HEAD
+   git diff $tree $referent
 END
        } else {
            failedcmd @diffcmd;
@@ -4338,7 +4334,7 @@ END
     }
 
     my @tagwants = push_tagwants($cversion, $dgithead, $maintviewhead,
-                                ".git/dgit/tag");
+                                dgit_privdir()."/tag");
     my @tagobjfns;
 
     supplementary_message(<<'END');
@@ -4428,7 +4424,7 @@ END
 }
 
 sub pre_clone () {
-    no_local_git_cfg();
+    not_necessarily_a_tree();
 }
 sub cmd_clone {
     parseopts();
@@ -4531,21 +4527,18 @@ END
     pull();
 }
 
-sub cmd_push {
+sub prep_push () {
     parseopts();
-    badusage "-p is not allowed with dgit push" if defined $package;
+    build_or_push_prep_early();
+    pushing();
     check_not_dirty();
-    my $clogp = parsechangelog();
-    $package = getfield $clogp, 'Source';
     my $specsuite;
     if (@ARGV==0) {
     } elsif (@ARGV==1) {
        ($specsuite) = (@ARGV);
     } else {
-       badusage "incorrect arguments to dgit push";
+       badusage "incorrect arguments to dgit $subcommand";
     }
-    $isuite = getfield $clogp, 'Distribution';
-    pushing();
     if ($new_package) {
        local ($package) = $existing_package; # this is a hack
        canonicalise_suite();
@@ -4555,9 +4548,13 @@ sub cmd_push {
     if (defined $specsuite &&
        $specsuite ne $isuite &&
        $specsuite ne $csuite) {
-           fail "dgit push: changelog specifies $isuite ($csuite)".
+           fail "dgit $subcommand: changelog specifies $isuite ($csuite)".
                " but command line specifies $specsuite";
     }
+}
+
+sub cmd_push {
+    prep_push();
     dopush();
 }
 
@@ -4641,7 +4638,7 @@ sub i_method {
 }
 
 sub pre_rpush () {
-    no_local_git_cfg();
+    not_necessarily_a_tree();
 }
 sub cmd_rpush {
     my $host = nextarg;
@@ -4861,7 +4858,7 @@ sub quiltify_dpkg_commit ($$$;$) {
     my ($patchname,$author,$msg, $xinfo) = @_;
     $xinfo //= '';
 
-    mkpath '.git/dgit';
+    mkpath '.git/dgit'; # we are in playtree
     my $descfn = ".git/dgit/quilt-description.tmp";
     open O, '>', $descfn or die "$descfn: $!";
     $msg =~ s/\n+/\n\n/;
@@ -5054,11 +5051,11 @@ END
 
     my $dgitview = git_rev_parse 'HEAD';
 
-    changedir '../../../..';
+    changedir $maindir;
     # When we no longer need to support squeeze, use --create-reflog
     # instead of this:
-    ensuredir ".git/logs/refs/dgit-intern";
-    my $makelogfh = new IO::File ".git/logs/refs/$splitbraincache", '>>'
+    ensuredir "$maindir_gitcommon/logs/refs/dgit-intern";
+    my $makelogfh = new IO::File "$maindir_gitcommon/logs/refs/$splitbraincache", '>>'
       or die $!;
 
     my $oldcache = git_get_ref "refs/$splitbraincache";
@@ -5079,7 +5076,7 @@ END
     runcmd @git, qw(update-ref -m), $cachekey, "refs/$splitbraincache",
        $dgitview;
 
-    changedir '.git/dgit/unpack/work';
+    changedir "$playground/work";
 
     my $saved = maybe_split_brain_save $headref, $dgitview, "converted";
     progress "dgit view: created ($saved)";
@@ -5378,7 +5375,7 @@ END
     my $headref = git_rev_parse('HEAD');
 
     prep_ud();
-    changedir $ud;
+    changedir $playground;
 
     my $upstreamversion = upstreamversion $version;
 
@@ -5390,9 +5387,9 @@ END
 
     die 'bug' if $split_brain && !$need_split_build_invocation;
 
-    changedir '../../../..';
+    changedir $maindir;
     runcmd_ordryrun_local
-        @git, qw(pull --ff-only -q .git/dgit/unpack/work master);
+        @git, qw(pull --ff-only -q), "$playground/work", qw(master);
 }
 
 sub quilt_fixup_mkwork ($) {
@@ -5408,7 +5405,7 @@ sub quilt_fixup_linkorigs ($$) {
     my ($upstreamversion, $fn) = @_;
     # calls $fn->($leafname);
 
-    foreach my $f (<../../../../*>) { #/){
+    foreach my $f (<$maindir/../*>) { #/){
        my $b=$f; $b =~ s{.*/}{};
        {
            local ($debuglevel) = $debuglevel-1;
@@ -5487,12 +5484,12 @@ END
                  debian/control debian/changelog);
     foreach my $maybe (qw(debian/patches debian/source/options
                           debian/tests/control)) {
-        next unless stat_exists "../../../$maybe";
+        next unless stat_exists "$maindir/$maybe";
         push @files, $maybe;
     }
 
     my $debtar= srcfn $fakeversion,'.debian.tar.gz';
-    runcmd qw(env GZIP=-1n tar -zcf), "./$debtar", qw(-C ../../..), @files;
+    runcmd qw(env GZIP=-1n tar -zcf), "./$debtar", qw(-C), $maindir, @files;
 
     $dscaddfile->($debtar);
     close $fakedsc or die $!;
@@ -5501,7 +5498,7 @@ END
 sub quilt_check_splitbrain_cache ($$) {
     my ($headref, $upstreamversion) = @_;
     # Called only if we are in (potentially) split brain mode.
-    # Called in $ud.
+    # Called in playground.
     # Computes the cache key and looks in the cache.
     # Returns ($dgit_view_commitid, $cachekey) or (undef, $cachekey)
 
@@ -5535,8 +5532,8 @@ sub quilt_check_splitbrain_cache ($$) {
     debugcmd "|(probably)",@cmd;
     my $child = open GC, "-|";  defined $child or die $!;
     if (!$child) {
-       chdir '../../..' or die $!;
-       if (!stat ".git/logs/refs/$splitbraincache") {
+       chdir $maindir or die $!;
+       if (!stat "$maindir_gitcommon/logs/refs/$splitbraincache") {
            $! == ENOENT or die $!;
            printdebug ">(no reflog)\n";
            exit 0;
@@ -5857,14 +5854,18 @@ sub cmd_clean () {
     maybe_unapply_patches_again();
 }
 
-sub build_prep_early () {
-    our $build_prep_early_done //= 0;
-    return if $build_prep_early_done++;
-    badusage "-p is not allowed when building" if defined $package;
+sub build_or_push_prep_early () {
+    our $build_or_push_prep_early_done //= 0;
+    return if $build_or_push_prep_early_done++;
+    badusage "-p is not allowed with dgit $subcommand" if defined $package;
     my $clogp = parsechangelog();
     $isuite = getfield $clogp, 'Distribution';
     $package = getfield $clogp, 'Source';
     $version = getfield $clogp, 'Version';
+}
+
+sub build_prep_early () {
+    build_or_push_prep_early();
     notpushing();
     check_not_dirty();
 }
@@ -6102,15 +6103,17 @@ sub cmd_gbp_build {
     }
     my @cmd = opts_opt_multi_cmd @gbp_build;
 
-    push @cmd, (qw(-us -uc --git-no-sign-tags), "--git-builder=@dbp");
+    push @cmd, (qw(-us -uc --git-no-sign-tags),
+               "--git-builder=".(shellquote @dbp));
 
     if ($gbp_make_orig) {
-       ensuredir '.git/dgit';
-       my $ok = '.git/dgit/origs-gen-ok';
+       my $priv = dgit_privdir();
+       my $ok = "$priv/origs-gen-ok";
        unlink $ok or $!==&ENOENT or die $!;
        my @origs_cmd = @cmd;
        push @origs_cmd, qw(--git-cleaner=true);
-       push @origs_cmd, "--git-prebuild=touch $ok .git/dgit/no-such-dir/ok";
+       push @origs_cmd, "--git-prebuild=".
+            "touch ".(shellquote $ok)." ".(shellquote "$priv/no-such-dir/ok");
        push @origs_cmd, @ARGV;
        if (act_local()) {
            debugcmd @origs_cmd;
@@ -6173,10 +6176,10 @@ sub build_source {
     } else {
        my @cmd = (@dpkgsource, qw(-b --));
        if ($split_brain) {
-           changedir $ud;
+           changedir $playground;
            runcmd_ordryrun_local @cmd, "work";
            my @udfiles = <${package}_*>;
-           changedir "../../..";
+           changedir $maindir;
            foreach my $f (@udfiles) {
                printdebug "source copy, found $f\n";
                next unless
@@ -6184,7 +6187,7 @@ sub build_source {
                    ($f =~ m/\.debian\.tar(?:\.\w+)$/ &&
                     $f eq srcfn($version, $&));
                printdebug "source copy, found $f - renaming\n";
-               rename "$ud/$f", "../$f" or $!==ENOENT
+               rename "$playground/$f", "../$f" or $!==ENOENT
                    or fail "put in place new source file ($f): $!";
            }
        } else {
@@ -6414,7 +6417,7 @@ END
 }
 
 sub pre_archive_api_query () {
-    no_local_git_cfg();
+    not_necessarily_a_tree();
 }
 sub cmd_archive_api_query {
     badusage "need only 1 subpath argument" unless @ARGV==1;
@@ -6433,7 +6436,7 @@ sub repos_server_url () {
 }    
 
 sub pre_clone_dgit_repos_server () {
-    no_local_git_cfg();
+    not_necessarily_a_tree();
 }
 sub cmd_clone_dgit_repos_server {
     badusage "need destination argument" unless @ARGV==1;
@@ -6445,7 +6448,7 @@ sub cmd_clone_dgit_repos_server {
 }
 
 sub pre_print_dgit_repos_server_source_url () {
-    no_local_git_cfg();
+    not_necessarily_a_tree();
 }
 sub cmd_print_dgit_repos_server_source_url {
     badusage "no arguments allowed to dgit print-dgit-repos-server-source-url"
@@ -6454,6 +6457,15 @@ sub cmd_print_dgit_repos_server_source_url {
     print $url, "\n" or die $!;
 }
 
+sub pre_print_dpkg_source_ignores {
+    not_necessarily_a_tree();
+}
+sub cmd_print_dpkg_source_ignores {
+    badusage "no arguments allowed to dgit print-dpkg-source-ignores"
+       if @ARGV;
+    print "@dpkg_source_ignores\n" or die $!;
+}
+
 sub cmd_setup_mergechangelogs {
     badusage "no arguments allowed to dgit setup-mergechangelogs" if @ARGV;
     local $isuite = 'DGIT-SETUP-TREE';
@@ -6836,12 +6848,13 @@ if (!@ARGV) {
     print STDERR $helpmsg or die $!;
     exit 8;
 }
-my $cmd = shift @ARGV;
+$cmd = $subcommand = shift @ARGV;
 $cmd =~ y/-/_/;
 
 my $pre_fn = ${*::}{"pre_$cmd"};
 $pre_fn->() if $pre_fn;
 
+record_maindir if $invoked_in_git_tree;
 git_slurp_config();
 
 my $fn = ${*::}{"cmd_$cmd"};
index c31dfa5956dbfe48e01bea5b8cd2b393eac39e7e..3c438ab101b41f069ed9cbfa29b46b12c01c3b02 100644 (file)
@@ -111,6 +111,13 @@ want to follow it up with a push to alioth.
 You will need to pass I<--overwrite> if the previous upload was not
 performed with dgit.
 
+If this is first ever dgit push of the package, consider passing
+I<--deliberately-not-fast-forward> instead of I<--overwrite>.  This
+avoids introducing a new origin commit into the dgit view of your git
+history.  (This origin commit would represent the most recent non-dgit
+upload of the package, but this should already be represented in your
+git history.)
+
 =head1 INCORPORATING NMUS
 
 B<dgit pull> can't yet incorporate NMUs into patches-unapplied gbp
index 3da1b78538cd5eba22e805e033d98cde4ce47926..22be1c9e2199244acf697bb747d1ed4c3fff1d43 100644 (file)
@@ -146,23 +146,34 @@ Now create I<debian/gbp.conf>:
     pristine-tar = False
     pristine-tar-commit = False
 
+    [import-orig]
+    merge-mode = merge
+
 =back
 
-Then we can import the upstream version:
+gbp-import-orig(1) requires a pre-existing upstream branch:
 
 =over 4
 
     % git add debian/gbp.conf && git commit -m "create gbp.conf"
-    % gbp import-orig ../foo_1.2.2.orig.tar.xz
+    % git checkout --orphan upstream
+    % git rm -rf .
+    % git commit --allow-empty -m "initial, empty branch for upstream source"
+    % git checkout -f master
 
 =back
 
-You are now ready to proceed as above, making commits to both the
-upstream source and the I<debian/> directory.
+Then we can import the upstream version:
+
+=over 4
+
+    % gbp import-orig --merge-mode=replace ../foo_1.2.2.orig.tar.xz
 
-If you want to maintain a copy of your repository on
-B<alioth.debian.org>, you should push both the origin and the upstream
-branches:
+=back
+
+Our upstream branch cannot be pushed to B<dgit-repos>, but since we
+will need it whenever we import a new upstream version, we must push
+it somewhere.  The usual choice is B<alioth.debian.org>:
 
 =over 4
 
@@ -171,6 +182,9 @@ branches:
 
 =back
 
+You are now ready to proceed as above, making commits to both the
+upstream source and the I<debian/> directory.
+
 =head1 CONVERTING AN EXISTING PACKAGE
 
 This section explains how to convert an existing Debian package to
@@ -188,7 +202,16 @@ this workflow.  It should be skipped when debianising a new package.
 
 =head2 Existing git history using another workflow
 
-First, dump any existing patch queue:
+First, if you don't already have the git history locally, clone it,
+and obtain the corresponding orig.tar from the archive:
+
+=over 4
+
+    % git clone git.debian.org:collab-maint/foo
+    % cd foo
+    % origtargz
+
+Now dump any existing patch queue:
 
 =over 4
 
@@ -205,15 +228,25 @@ Then make new upstream tags available:
 
 =back
 
+=for dgit-test dpkg-source-ignores begin
+
 Now you simply need to ensure that your git HEAD is dgit-compatible,
-i.e., it is exactly what you would get if you ran B<dpkg-buildpackage
--i\.git/ -I.git -S> and then unpacked the resultant source package.
+i.e., it is exactly what you would get if you ran
+B<dpkg-buildpackage -i'(?:^|/)\.git(?:/|$)' -I.git -S>
+and then unpacked the resultant source package.
+
+=for dgit-test dpkg-source-ignores end
 
 To achieve this, you might need to delete
 I<debian/source/local-options>.  One way to have dgit check your
 progress is to run B<dgit build-source>.
 
-The first dgit push will require I<--overwrite>.
+The first dgit push will require I<--overwrite>.  If this is the first
+ever dgit push of the package, consider passing
+I<--deliberately-not-fast-forward> instead of I<--overwrite>.  This
+avoids introducing a new origin commit into your git history.  (This
+origin commit would represent the most recent non-dgit upload of the
+package, but this should already be represented in your git history.)
 
 =head1 SOURCE PACKAGE CONFIGURATION
 
@@ -290,58 +323,76 @@ to git), you can just run dpkg-buildpackage(1) or debuild(1) instead.
 
 =head1 NEW UPSTREAM RELEASES
 
-=head2 When upstream tags releases in git
+=head2 Obtaining the release
 
-It's a good idea to preview the merge of the new upstream release.
-First, just check for any new or deleted files that may need
-accounting for in your copyright file:
+=head3 When upstream tags releases in git
 
 =over 4
 
     % git remote update
-    % git diff --stat master..1.2.3 -- . ':!debian'
 
 =back
 
-You can then review the full merge diff:
+=head3 When upstream releases only tarballs
+
+You will need the I<debian/gbp.conf> from "When upstream releases only
+tarballs", above.  You will also need your upstream branch.  Above, we
+pushed this to B<alioth.debian.org>.  You will need to clone or fetch
+from there, instead of relying on B<dgit clone>/B<dgit fetch> alone.
+
+Then, either
 
 =over 4
 
-    % git merge-tree `git merge-base master 1.2.3` master 1.2.3 | $PAGER
+    % gbp import-orig --no-merge ../foo_1.2.3.orig.tar.xz
 
 =back
 
-Once you're satisfied with what will be merged, update your package:
+or if you have a working watch file
 
 =over 4
 
-    % git merge 1.2.3
-    % dch -v1.2.3-1 New upstream release.
-    % git add debian/changelog && git commit -m changelog
-    % git deborig
+    % gbp import-orig --no-merge --uscan
 
 =back
 
-and you are ready to try a build.
+=head2 Reviewing & merging the release
 
-=head2 When upstream releases only tarballs
+It's a good idea to preview the merge of the new upstream release.
+First, just check for any new or deleted files that may need
+accounting for in your copyright file:
 
-You will need the I<debian/gbp.conf> from "When upstream releases only
-tarballs", above.
+=over 4
 
-Then, either
+    % git diff --stat master..1.2.3 -- . ':!debian'
+
+=back
+
+You can then review the full merge diff:
 
 =over 4
 
-    % gbp import-orig ../foo_1.2.2.orig.tar.xz
+    % git merge-tree `git merge-base master 1.2.3` master 1.2.3 | $PAGER
 
 =back
 
-or if you have a working watch file
+Once you're satisfied with what will be merged, update your package:
 
 =over 4
 
-    % gbp import-orig --uscan
+    % git merge 1.2.3
+    % dch -v1.2.3-1 New upstream release.
+    % git add debian/changelog && git commit -m changelog
+
+=back
+
+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
+a tarball:
+
+=over 4
+
+    % git deborig
 
 =back
 
index 03aee59b361ab3365fd3d9f95a850f00f35c35e4..34aaafff4f09ab69cf9b8ec55018888ccbe181c6 100644 (file)
@@ -78,6 +78,14 @@ so that your history,
 which will be pushed to the dgit git server,
 is fast forward from the dgit archive view.
 
+Alternatively,
+if this was the first ever dgit push of the package,
+you can avoid this merge commit by
+passing C<--deliberately-not-fast-forward>.
+instead of C<--overwrite>.
+This avoids introducing a new origin commit into
+your git history.
+
 =head1 SUBSEQUENT PUSHES
 
 =over 4
index 8d5b72daa2de1e42b11820c132cda72c260d3ce9..08083290c1139e14bd83cc7b41733ee1b4a89816 100644 (file)
@@ -257,6 +257,14 @@ you may need to pass
 C<--overwrite>
 to dgit.
 
+Alternatively,
+if this was the first ever dgit push of the package,
+you can pass C<--deliberately-not-fast-forward>
+instead of C<--overwrite>.
+This avoids introducing a new origin commit
+into the dgit view of
+the sponsee's git history
+which is unnecessary and could be confusing.
 
 =head1 SPONSORING A NON-GIT-USING SPONSEE
 
index aacdf4d4482dfa0a67d13422999ffc9b46cd00f1..c74396a56954d2ff176c2032cb022b1d71447921 100644 (file)
@@ -32,10 +32,10 @@ or L<dgit(1)> and L<dgit(7)>.
 
     % dgit clone glibc jessie,-security
     % cd glibc
-    % wget 'https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=28250;mbox=yes;msg=89' | patch -p1 -u
+    % curl 'https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=28250;mbox=yes;msg=89' | patch -p1 -u
     % git commit -a -m 'Fix libc lost output bug'
     % gbp dch -S --since=dgit/dgit/sid --ignore-branch --commit
-    % sudo apt-get build-dep glibc
+    % mk-build-deps --root-cmd=sudo --install
     % dpkg-buildpackage -uc -b
     % sudo dpkg -i ../libc6_*.deb
 
@@ -288,21 +288,37 @@ a complete treatment is beyond the scope of this tutorial.
 
 =over 4
 
-    % sudo apt-get build-dep glibc
+    % mk-build-deps --root-cmd=sudo --install
     % dpkg-buildpackage -uc -b
 
 =back
 
-apt-get build-dep installs the build dependencies according to the
-official package, not your modified one.  So if you've changed the
-build dependencies you might have to install some of them by hand.
-
 dpkg-buildpackage is the primary tool for building a Debian source
 package.
 C<-uc> means not to pgp-sign the results.
 C<-b> means build all binary packages,
 but not to build a source package.
 
+=head2 Using sbuild
+
+You can build in an schroot chroot, with sbuild, instead of in your
+main environment.  (sbuild is used by the Debian build daemons.)
+
+=over 4
+
+    % git clean -xdf
+    % sbuild -c jessie -A --no-clean-source \
+             --dpkg-source-opts='-Zgzip -z1 --format=1.0 -sn'
+
+=back
+
+Note that this will seem to leave a "source package"
+(.dsc and .tar.gz)
+in the parent directory,
+but that source package should not be used.
+It is likely to be broken.
+For more information see Debian bug #868527.
+
 =head1 INSTALLING
 
 =head2 Debian Jessie or older
diff --git a/dgit.1 b/dgit.1
index e5b4bb050b6145a30a72299733622b768677c125..1bf4a2c767345d6a2036379993edccc5947e69ce 100644 (file)
--- a/dgit.1
+++ b/dgit.1
@@ -30,6 +30,10 @@ dgit \- git integration with the Debian archive
 .B dgit
 allows you to treat the Debian archive as if it were a git
 repository.
+Conversely,
+it allows Debian to publish the source of its packages
+as git branches, in a format which is directly useable
+by ordinary people.
 
 This is the command line reference.
 Please read the tutorial(s):
@@ -343,6 +347,13 @@ as actually being used on the dgit git server, as a git tree.
 Prints the url used by dgit clone-dgit-repos-server.
 This is hopefully suitable for use as a git remote url.
 It may not be useable in a browser.
+.TP
+.BI "dgit print-dpkg-source-ignores"
+Prints the -i and -I arguments which must be passed to dpkg-souce
+to cause it to exclude exactly the .git diredcory
+and nothing else.
+The separate arguments are unquoted, separated by spaces,
+and do not contain spaces.
 .SH OPTIONS
 .TP
 .BR --dry-run " | " -n
@@ -554,6 +565,17 @@ Declare that you are deliberately rewinding history.  When pushing to
 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,
+this also prevents the construction by dgit of a pseudomerge
+to make the dgit view fast forwarding.
+Normally only one of
+--overwrite (which creates a suitable pseudomerge)
+and
+--deliberately-not-fast-forward
+(which suppresses the pseudomerge and the fast forward checks)
+should be needed;
+--overwrite is usually better.
 .TP
 .BR --deliberately-include-questionable-history
 Declare that you are deliberately including, in the git history of
index ec9b2c9ae186e8972a4c5bc216aadb2d199a2288..a8b9400b5a4729a2c92e0c86d318385dd1512c6c 100755 (executable)
@@ -303,13 +303,6 @@ sub reject ($) {
     die "\ndgit-repos-server: reject: $why\n\n";
 }
 
-sub runcmd {
-    debugcmd '+',@_;
-    $!=0; $?=0;
-    my $r = system @_;
-    die (shellquote @_)." $? $!" if $r;
-}
-
 sub policyhook {
     my ($policyallowbits, @polargs) = @_;
     # => ($exitstatuspolicybitmap);
index 3c04155a2ddbe398ba480030186b1ab8add23186..8fd35147431f884637c195ddde9ba41b34cfd58f 100644 (file)
--- a/tests/lib
+++ b/tests/lib
@@ -389,7 +389,7 @@ t-fscks () {
        (
        shopt -s nullglob
        for d in $tmp/*/.git $tmp/git/*.git; do
-               cd "$d"
+               cd "${d%/.git}"
                t-git-fsck
        done
        )
@@ -407,7 +407,7 @@ t-rm-dput-dropping () {
 
 t-dgit () {
        local dgit=${DGIT_TEST-dgit}
-       pwd
+       pwd >&2
        : '
 {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{'
        $dgit --dgit=$dgit --dget:-u --dput:--config=$tmp/dput.cf \
@@ -419,6 +419,17 @@ t-dgit () {
 '
 }
 
+t-dgit-manpage () {
+       local section=$1
+       local page=$2
+       (export LC_ALL=C.UTF-8
+        if [ "$DGIT_TEST_INTREE" ]; then
+               make -C $DGIT_TEST_INTREE $page.$section.view
+        else
+               man $section $page
+        fi)
+}
+
 t-diff-nogit () {
        diff --exclude=.git --exclude=.pc -ruN $*
 }
index 3877c760a0aca1b40d3e756cedabd3ec28b0fef2..cfa5ce2ff1ac7c260ca04fd088547dd15b5aef84 100755 (executable)
@@ -15,6 +15,8 @@ fi
 
 mkdir -p tests/tmp
 
+export DGIT_GNUPG_STUNT_ERRLOG=$( tty -s ||: )
+
 (
  set -x
  exec make $jcpus -k -f tests/Makefile "$@"
diff --git a/tests/tests/dpkgsourceignores-correct b/tests/tests/dpkgsourceignores-correct
new file mode 100755 (executable)
index 0000000..33de95e
--- /dev/null
@@ -0,0 +1,53 @@
+#!/bin/bash
+set -e
+. tests/lib
+
+t-tstunt-parsechangelog
+
+t-archive example 1.0-1
+t-git-none
+
+t-dgit --no-rm-on-error clone $p
+
+cd $p
+
+bad-dpkg-source () {
+       t-expect-fail E:"dpkg-source:.*unexpected upstream changes" \
+       t-dgit --quilt=nofix -wgf build-source
+       find * -name .git -print0 | xargs -0r rm --
+}
+
+ignores=$(t-dgit print-dpkg-source-ignores)
+
+spurious-git-must-be-excluded () {
+       dpkg-source $ignores -b .
+       mkdir check
+       cd check
+       dpkg-source -x ../../${p}_${v}.dsc
+       cd ${p}-${v%-*}
+       find -name .git >../bad
+       diff /dev/null ../bad
+       cd ../..
+       find * -name .git -print0 | xargs -0r rm -rf --
+       git clean -xdff
+}
+
+mkdir docs/.git
+echo hi >docs/.git/ho
+spurious-git-must-be-excluded
+
+echo hi >docs/.git
+spurious-git-must-be-excluded
+
+mkdir not-really.git
+echo fee >not-really.git/something
+echo fi >not-really.gitfoo
+echo fo >some.git
+echo fum >some.gitfoo
+git add .
+git commit -m 'want these'
+
+t-dgit --quilt=smash -wgf build-source
+t-dgit -wgf push
+
+t-ok
diff --git a/tests/tests/dpkgsourceignores-docs b/tests/tests/dpkgsourceignores-docs
new file mode 100755 (executable)
index 0000000..a71e7f9
--- /dev/null
@@ -0,0 +1,54 @@
+#!/bin/bash
+set -e
+. tests/lib
+
+t-restrict x-dgit-intree-only
+# we need the .pod source, which is not shipped
+
+cd $tmp
+
+: ----- extract args from document -----
+
+perl -ne '
+       BEGIN { print "\n=head1 dgit-test-title\n\n"; }
+       next unless
+               m/^=for dgit-test dpkg-source-ignores begin/..
+               m/^=for dgit-test dpkg-source-ignores end/;
+       next unless m/dpkg-buildpackage.*-i.*-I/;
+       s/\s*dpkg-buildpackage\s+//;
+       s/\s+-S\s*//;
+       print;
+' $root/dgit-maint-merge.7.pod >doc.pod
+
+pod2text doc.pod >doc.txt
+
+perl -ne '
+       next unless m/\S/;
+       next if m/dgit-test-title/;
+       print "for arg in ";
+       print;
+       print " do\n";
+' doc.txt >doc.sh
+
+cat >>doc.sh <<'END'
+       printf "%s\n" "$arg"
+done
+END
+
+chmod +x doc.sh
+./doc.sh >doc.args
+
+: ----- extract args from dgit -----
+
+args=$( t-dgit print-dpkg-source-ignores )
+
+>dgit.args
+for arg in $args; do
+       printf >>dgit.args "%s\n" "$arg"
+done
+
+: ----- compare -----
+
+diff -u dgit.args doc.args
+
+t-ok
diff --git a/tests/tests/gitworktree b/tests/tests/gitworktree
new file mode 100755 (executable)
index 0000000..e675a0a
--- /dev/null
@@ -0,0 +1,29 @@
+#!/bin/bash
+set -e
+. tests/lib
+
+t-archive example 1.0-1
+t-git-none
+
+t-dgit --no-rm-on-error clone $p
+
+mv $p maintree
+cd maintree
+git branch -m maintree-head
+git worktree add ../$p
+cd ../$p
+
+t-dgit fetch
+
+t-dgit setup-new-tree
+
+echo hi >>modification
+git add modification
+git commit -m 'want this'
+t-dgit -wgf quilt-fixup
+
+t-dgit -wgf --quilt=nofix build
+
+t-dgit -wgf push
+
+t-ok
diff --git a/tests/tests/sbuild-gitish b/tests/tests/sbuild-gitish
new file mode 100755 (executable)
index 0000000..6e497b2
--- /dev/null
@@ -0,0 +1,47 @@
+#!/bin/bash
+set -e
+. tests/lib
+
+t-dependencies sbuild man-db
+t-restrict x-dgit-schroot-build
+
+t-tstunt-parsechangelog
+
+t-prep-newpackage example 1.1
+
+buildrune=$(
+       t-dgit-manpage 7 dgit-user | \
+       perl -ne '
+               next unless m/^ +Using sbuild$/ .. 0;
+               next unless m/^ +\%/ .. 0;
+               next if !m/\S/ .. 0;
+               s/^ +\%//;
+               $fixchr += s/(\s-c\s*)jessie(\s|$)/$1build$2/;
+               print or die $!;
+               END { $fixchr == 1 or die $fixchr; }
+       '
+)
+
+cd $p
+
+build () {
+       eval "$buildrune"
+}
+
+git checkout quilt-tip-1.1~0
+
+build
+
+git checkout gitish-only~0
+
+cat <<'END' >clean-target-hook
+#!/bin/sh
+set -ex
+test "$SCHROOT_SESSION_ID"
+END
+git add clean-target-hook
+git commit -m 'insist on schroot'
+
+build
+
+t-ok
index d71aa63f359d601081ef215188ef69d5cc09f120..a108267d3ce568edc4abaaa1711ac1d3a8050778 100755 (executable)
@@ -1,6 +1,32 @@
 #!/bin/sh
 set -e
-exec                                                   \
+
+for attempt in '' exec; do
+
+set +e
+$attempt                                               \
 $DGIT_TEST_REAL_GPG                                    \
        --agent-program=$DGIT_STUNT_AGENT               \
        "$@"
+rc=$?
+set -e
+
+if [ $rc != 2 ]; then exit $rc; fi
+
+echo >&2 "WARNING - GNUPG FAILED $rc - STUNT GNUPG $attempt $*"
+
+sh -ec '
+       if [ "x$DGIT_GNUPG_STUNT_ERRLOG" != x ]; then
+               exec >"$DGIT_GNUPG_STUNT_ERRLOG"
+       else
+               exec 2>/dev/null
+       fi
+       exec >/dev/tty
+       printf "%s\n" "$*"
+' x "GNUPG WRAPPER - TROUBLE - $HOME $GNUPGHOME - FAILED $rc $attempt $*"
+
+sleep 10
+
+done
+
+exit 127
index 6f66a915eb6082dfb3d20b85727710cadc51ab3e..fe108d11aafef68cbd24ac543a6644adfececcae 100644 (file)
Binary files a/tests/worktrees/example_1.0.tar and b/tests/worktrees/example_1.0.tar differ
diff --git a/using-these b/using-these
new file mode 100755 (executable)
index 0000000..0bcb4b1
--- /dev/null
@@ -0,0 +1,8 @@
+#!/bin/bash
+set -e
+d="${0%/*}"
+PERLLIB=$d${PERLLIB:+:}${PERLLIB}
+export PERLLIB
+PATH=$d:$d/infra${PATH:+:}${PATH}
+export PATH
+exec "$@"