diff options
-rw-r--r-- | Debian/Dgit.pm | 199 | ||||
-rw-r--r-- | debian/changelog | 77 | ||||
-rw-r--r-- | debian/control | 2 | ||||
-rw-r--r-- | debian/tests/control | 12 | ||||
-rwxr-xr-x | dgit | 275 | ||||
-rw-r--r-- | dgit-maint-gbp.7.pod | 7 | ||||
-rw-r--r-- | dgit-maint-merge.7.pod | 113 | ||||
-rw-r--r-- | dgit-maint-native.7.pod | 8 | ||||
-rw-r--r-- | dgit-sponsorship.7.pod | 8 | ||||
-rw-r--r-- | dgit-user.7.pod | 30 | ||||
-rw-r--r-- | dgit.1 | 22 | ||||
-rwxr-xr-x | infra/dgit-repos-server | 7 | ||||
-rw-r--r-- | tests/lib | 15 | ||||
-rwxr-xr-x | tests/run-all | 2 | ||||
-rwxr-xr-x | tests/tests/dpkgsourceignores-correct | 53 | ||||
-rwxr-xr-x | tests/tests/dpkgsourceignores-docs | 54 | ||||
-rwxr-xr-x | tests/tests/gitworktree | 29 | ||||
-rwxr-xr-x | tests/tests/sbuild-gitish | 47 | ||||
-rwxr-xr-x | tests/tstunt/gpg | 28 | ||||
-rw-r--r-- | tests/worktrees/example_1.0.tar | bin | 71680 -> 81920 bytes | |||
-rwxr-xr-x | using-these | 8 |
21 files changed, 810 insertions, 186 deletions
diff --git a/Debian/Dgit.pm b/Debian/Dgit.pm index dcecbd1..c4a61af 100644 --- a/Debian/Dgit.pm +++ b/Debian/Dgit.pm @@ -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; diff --git a/debian/changelog b/debian/changelog index 4209e71..9fd6edd 100644 --- a/debian/changelog +++ b/debian/changelog @@ -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: diff --git a/debian/control b/debian/control index 4d4c304..4405e14 100644 --- a/debian/control +++ b/debian/control @@ -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 diff --git a/debian/tests/control b/debian/tests/control index 76fe9bd..7d2a486 100644 --- a/debian/tests/control +++ b/debian/tests/control @@ -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 @@ -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"}; diff --git a/dgit-maint-gbp.7.pod b/dgit-maint-gbp.7.pod index c31dfa5..3c438ab 100644 --- a/dgit-maint-gbp.7.pod +++ b/dgit-maint-gbp.7.pod @@ -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 diff --git a/dgit-maint-merge.7.pod b/dgit-maint-merge.7.pod index 3da1b78..22be1c9 100644 --- a/dgit-maint-merge.7.pod +++ b/dgit-maint-merge.7.pod @@ -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 diff --git a/dgit-maint-native.7.pod b/dgit-maint-native.7.pod index 03aee59..34aaaff 100644 --- a/dgit-maint-native.7.pod +++ b/dgit-maint-native.7.pod @@ -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 diff --git a/dgit-sponsorship.7.pod b/dgit-sponsorship.7.pod index 8d5b72d..0808329 100644 --- a/dgit-sponsorship.7.pod +++ b/dgit-sponsorship.7.pod @@ -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 diff --git a/dgit-user.7.pod b/dgit-user.7.pod index aacdf4d..c74396a 100644 --- a/dgit-user.7.pod +++ b/dgit-user.7.pod @@ -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 @@ -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 diff --git a/infra/dgit-repos-server b/infra/dgit-repos-server index ec9b2c9..a8b9400 100755 --- a/infra/dgit-repos-server +++ b/infra/dgit-repos-server @@ -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); @@ -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 $* } diff --git a/tests/run-all b/tests/run-all index 3877c76..cfa5ce2 100755 --- a/tests/run-all +++ b/tests/run-all @@ -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 index 0000000..33de95e --- /dev/null +++ b/tests/tests/dpkgsourceignores-correct @@ -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 index 0000000..a71e7f9 --- /dev/null +++ b/tests/tests/dpkgsourceignores-docs @@ -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 index 0000000..e675a0a --- /dev/null +++ b/tests/tests/gitworktree @@ -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 index 0000000..6e497b2 --- /dev/null +++ b/tests/tests/sbuild-gitish @@ -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 diff --git a/tests/tstunt/gpg b/tests/tstunt/gpg index d71aa63..a108267 100755 --- a/tests/tstunt/gpg +++ b/tests/tstunt/gpg @@ -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 diff --git a/tests/worktrees/example_1.0.tar b/tests/worktrees/example_1.0.tar Binary files differindex 6f66a91..fe108d1 100644 --- a/tests/worktrees/example_1.0.tar +++ b/tests/worktrees/example_1.0.tar diff --git a/using-these b/using-these new file mode 100755 index 0000000..0bcb4b1 --- /dev/null +++ b/using-these @@ -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 "$@" |