summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Debian/Dgit.pm121
-rw-r--r--Makefile6
-rw-r--r--debian/changelog135
-rw-r--r--debian/control2
-rw-r--r--debian/tests/control40
-rwxr-xr-xdgit252
-rw-r--r--dgit-maint-debrebase.7.pod117
-rw-r--r--dgit.19
-rw-r--r--dgit.716
-rwxr-xr-xgit-debrebase1299
-rw-r--r--git-debrebase.1.pod40
-rw-r--r--git-debrebase.5.pod38
-rwxr-xr-xlocal-pod-man13
-rw-r--r--tests/Makefile4
-rwxr-xr-xtests/enumerate-tests6
-rw-r--r--tests/lib43
-rw-r--r--tests/lib-build-modes5
-rw-r--r--tests/lib-core3
-rw-r--r--tests/lib-gdr126
-rwxr-xr-xtests/run-all34
-rwxr-xr-xtests/setup/gdr-convert-gbp36
-rwxr-xr-xtests/tests/build-modes14
-rwxr-xr-xtests/tests/build-modes-long36
-rwxr-xr-xtests/tests/build-modes-source17
-rwxr-xr-xtests/tests/gdr-diverge-nmu-dgit2
-rwxr-xr-xtests/tests/gdr-edits7
-rwxr-xr-xtests/tests/gdr-fresh45
-rwxr-xr-xtests/tests/gdr-import-dgitview3
-rwxr-xr-xtests/tests/gdr-import-nostitch30
-rwxr-xr-xtests/tests/gdr-makepatches72
-rwxr-xr-xtests/tests/gdr-merge65
-rwxr-xr-xtests/tests/gdr-merge-conflicts150
-rwxr-xr-xtests/tests/gdr-newupstream65
-rwxr-xr-xtests/tests/gdr-subcommands38
-rwxr-xr-xtests/tests/gdr-unprocessable2
-rwxr-xr-xtests/tests/gdr-unprocessable-hints33
-rwxr-xr-xtests/tests/gitattributes2
-rwxr-xr-xtests/tests/sbuild-gitish1
-rwxr-xr-xtests/tests/test-list-uptodate3
-rwxr-xr-xtests/tstunt/debchange17
-rwxr-xr-xtests/tstunt/dpkg-deb4
-rw-r--r--[l---------]tests/worktrees/example_1.1.tarbin15 -> 81920 bytes
42 files changed, 2408 insertions, 473 deletions
diff --git a/Debian/Dgit.pm b/Debian/Dgit.pm
index 1cd765d..91d4c71 100644
--- a/Debian/Dgit.pm
+++ b/Debian/Dgit.pm
@@ -32,6 +32,7 @@ use IPC::Open2;
use File::Path;
use File::Basename;
use Dpkg::Control::Hash;
+use Debian::Dgit::ExitStatus;
BEGIN {
use Exporter ();
@@ -57,6 +58,8 @@ BEGIN {
git_for_each_tag_referring is_fast_fwd
git_check_unmodified
git_reflog_action_msg git_update_ref_cmd
+ make_commit_text
+ reflog_cache_insert reflog_cache_lookup
$package_re $component_re $deliberately_re
$distro_re $versiontag_re $series_filename_re
$orig_f_comp_re $orig_f_sig_re $orig_f_tail_re
@@ -66,6 +69,7 @@ BEGIN {
$ffq_refprefix $gdrlast_refprefix
initdebug enabledebug enabledebuglevel
printdebug debugcmd
+ $printdebug_when_debuglevel $debugcmd_when_debuglevel
$debugprefix *debuglevel *DEBUG
shellquote printcmd messagequote
$negate_harmful_gitattrs
@@ -100,6 +104,13 @@ our $orig_f_tail_re = "$orig_f_comp_re\\.tar(?:\\.\\w+)?(?:$orig_f_sig_re)?";
our $git_null_obj = '0' x 40;
our $ffq_refprefix = 'ffq-prev';
our $gdrlast_refprefix = 'debrebase-last';
+our $printdebug_when_debuglevel = 1;
+our $debugcmd_when_debuglevel = 1;
+
+# these three all go together, only valid after record_maindir
+our $maindir;
+our $maindir_gitdir;
+our $maindir_gitcommon;
# policy hook exit status bits
# see dgit-repos-server head comment for documentation
@@ -156,7 +167,21 @@ sub enabledebuglevel ($) {
}
sub printdebug {
- print DEBUG $debugprefix, @_ or die $! if $debuglevel>0;
+ # Prints a prefix, and @_, to DEBUG. @_ should normally contain
+ # a trailing \n.
+
+ # With no (or only empty) arguments just prints the prefix and
+ # leaves the caller to do more with DEBUG. The caller should make
+ # sure then to call printdebug with something ending in "\n" to
+ # get the prefix right in subsequent calls.
+
+ return unless $debuglevel >= $printdebug_when_debuglevel;
+ our $printdebug_noprefix;
+ print DEBUG $debugprefix unless $printdebug_noprefix;
+ pop @_ while @_ and !length $_[-1];
+ return unless @_;
+ print DEBUG @_ or die $!;
+ $printdebug_noprefix = $_[-1] !~ m{\n$};
}
sub messagequote ($) {
@@ -195,7 +220,8 @@ sub printcmd {
sub debugcmd {
my $extraprefix = shift @_;
- printcmd(\*DEBUG,$debugprefix.$extraprefix,@_) if $debuglevel>0;
+ printcmd(\*DEBUG,$debugprefix.$extraprefix,@_)
+ if $debuglevel >= $debugcmd_when_debuglevel;
}
sub dep14_version_mangle ($) {
@@ -266,7 +292,7 @@ sub _us () {
sub failmsg {
my $s = "error: @_\n";
- $s =~ s/\n\n$/\n/;
+ $s =~ s/\n\n$/\n/g;
my $prefix = _us().": ";
$s =~ s/^/$prefix/gm;
return "\n".$s;
@@ -362,6 +388,7 @@ sub shell_cmd {
sub cmdoutput_errok {
confess Dumper(\@_)." ?" if grep { !defined } @_;
+ local $printdebug_when_debuglevel = $debugcmd_when_debuglevel;
debugcmd "|",@_;
open P, "-|", @_ or die "$_[0] $!";
my $d;
@@ -414,6 +441,7 @@ sub git_cat_file ($;$) {
# in scalar context, just the data
# if $etype defined, dies unless type is $etype or in @$etype
our ($gcf_pid, $gcf_i, $gcf_o);
+ local $printdebug_when_debuglevel = $debugcmd_when_debuglevel;
my $chk = sub {
my ($gtype, $data) = @_;
if ($etype) {
@@ -667,6 +695,88 @@ sub parsechangelog_loop ($$$) {
close CLOGS or $?==SIGPIPE or failedcmd @$clogcmd;
}
+sub make_commit_text ($) {
+ my ($text) = @_;
+ my ($out, $in);
+ my @cmd = (qw(git hash-object -w -t commit --stdin));
+ debugcmd "|",@cmd;
+ print Dumper($text) if $debuglevel > 1;
+ my $child = open2($out, $in, @cmd) or die $!;
+ my $h;
+ eval {
+ print $in $text or die $!;
+ close $in or die $!;
+ $h = <$out>;
+ $h =~ m/^\w+$/ or die;
+ $h = $&;
+ printdebug "=> $h\n";
+ };
+ close $out;
+ waitpid $child, 0 == $child or die "$child $!";
+ $? and failedcmd @cmd;
+ return $h;
+}
+
+sub reflog_cache_insert ($$$) {
+ my ($ref, $cachekey, $value) = @_;
+ # you must call this in $maindir
+ # you must have called record_maindir
+
+ # When we no longer need to support squeeze, use --create-reflog
+ # instead of this:
+ my $parent = $ref; $parent =~ s{/[^/]+$}{};
+ ensuredir "$maindir_gitcommon/logs/$parent";
+ my $makelogfh = new IO::File "$maindir_gitcommon/logs/$ref", '>>'
+ or die $!;
+
+ my $oldcache = git_get_ref $ref;
+
+ if ($oldcache eq $value) {
+ my $tree = cmdoutput qw(git rev-parse), "$value:";
+ # git update-ref doesn't always update, in this case. *sigh*
+ my $authline = (ucfirst _us()).
+ ' <'._us().'@example.com> 1000000000 +0000';
+ my $dummy = make_commit_text <<END;
+tree $tree
+parent $value
+author $authline
+committer $authline
+
+Dummy commit - do not use
+END
+ runcmd qw(git update-ref -m), _us()." - dummy", $ref, $dummy;
+ }
+ runcmd qw(git update-ref -m), $cachekey, $ref, $value;
+}
+
+sub reflog_cache_lookup ($$) {
+ my ($ref, $cachekey) = @_;
+ # you may call this in $maindir or in a playtree
+ # you must have called record_maindir
+ my @cmd = (qw(git log -g), '--pretty=format:%H %gs', $ref);
+ debugcmd "|(probably)",@cmd;
+ my $child = open GC, "-|"; defined $child or die $!;
+ if (!$child) {
+ chdir $maindir or die $!;
+ if (!stat "$maindir_gitcommon/logs/$ref") {
+ $! == ENOENT or die $!;
+ printdebug ">(no reflog)\n";
+ finish 0;
+ }
+ exec @cmd; die $!;
+ }
+ while (<GC>) {
+ chomp;
+ printdebug ">| ", $_, "\n" if $debuglevel > 1;
+ next unless m/^(\w+) (\S.*\S)$/ && $2 eq $cachekey;
+ close GC;
+ return $1;
+ }
+ die $! if GC->error;
+ failedcmd unless close GC;
+ return undef;
+}
+
# ========== playground handling ==========
# terminology:
@@ -715,11 +825,6 @@ sub parsechangelog_loop ($$$) {
# ----- maindir -----
-# these three all go together
-our $maindir;
-our $maindir_gitdir;
-our $maindir_gitcommon;
-
our $local_git_cfg;
sub record_maindir () {
diff --git a/Makefile b/Makefile
index 0ea7a7c..934ead1 100644
--- a/Makefile
+++ b/Makefile
@@ -131,9 +131,9 @@ clean distclean mostlyclean maintainer-clean:
test -e $$m.pod && rm -f $$m; \
done
-%.7: %.7.pod
- pod2man --section=7 --date="Debian Project" --center="dgit" \
- --name=$(subst .7,,$@) \
+dgit%: dgit%.pod
+ m=$@; pod2man --section=$${m##*.} --date="Debian Project" \
+ --center="dgit" --name=$${m%.*} \
$^ $@
git-debrebase.%: git-debrebase.%.pod
diff --git a/debian/changelog b/debian/changelog
index 116daf8..9878956 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,136 @@
+dgit (6.11) unstable; urgency=medium
+
+ * dgit-maint-debrebase(7): move and improve the section
+ "Inspecting the history". [Sean Whitton]
+ * Makefile: Adjust scope of dgit(7) pod rule.
+ * local-pod-man: developer script, obsoleteed by `make %.view': drop it.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk> Sun, 26 Aug 2018 14:59:32 +0100
+
+dgit (6.10) unstable; urgency=medium
+
+ git-debrebase bugfixes:
+ * Patches in subdirectories: fix malfunctions. Closes:#907202,#907206.
+ * new-upstream changelog entry: Use debchange. Closes:#905888.
+ * Empty patch queues: Fix some malfunctions and infelicities.
+ * convert-to-gbp: Actually base the result on the breakwater, not HEAD.
+
+ dgit bugfixes:
+ * *build*: Cope with new-style --build= specifications
+ * Pass --no-source to sbuild (always). Closes:#904966.
+ * build: Squash $buildproductsdir. Closes:#906786.
+
+ dgit improvements for git-debrebase compatibility:
+ * Do not try split brain git-debrebase make-patches. Closes:#906908.
+ * Do not abandon quilt fixup at git-debrebase split commits.
+ * Check for git-debrebase with a history walker, not debrebase-last.
+ This can avoids using dpkg-source --commit. Closes:#907208.
+
+ git-debrebase improvements:
+ * convert-from-*: snag on discarding comments in series. Closes:#907198.
+ * forget-was-ever-debrebase: New subcommand.
+ * Make all commit message annotations have a COMMIT-TYPE.
+
+ git-debrebase documentation:
+ * dgit-maint-debrebase(7): Add runes for inspecting. Closes:#907190.
+ * git-debrebase(5): Warn against renaming branch while unstitched
+ * git-debrebase(5): Document new understanding of debrebase-last
+
+ test suite behavioural changes for ad-hoc runs:
+ * run-all: Without --progressive, rm and recreate tests/tmp
+ * run-all: Honour DGIT_TESTS_TMPDIR
+ * run-all: Understand `:' specially
+
+ test suite:
+ * Tests for the bugfixes and improvements.
+ * lib-gdr: Be more defensive about unexpected states/args
+ * lib-gdr: Check that we made patches with git-debrebase
+ * Honour DGIT_TEST_RUN_PFX env var.
+ * Test dgit calling git-debrebase on new debianisation.
+ * gdr-new-upstream: Check changelog is exactly right.
+ * debchange: Widespread better handling of the time seen by dch.
+ Freeze time. Work around faketime TZ bug (#907264).
+ * test-list-uptodate: Drop imports and dependencies
+ * git-debrebase: gdr-merge-conflicts: Call git merge --no-edit
+ * build-modes-*: Provide stunt dpkg-deb to pass -Znone, for speed.
+ * build-products-dir: Check nothing in ../
+ * Work if $tmp is on a different filesystem.
+ * Internal changes and refactoring to support other changes.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk> Sun, 26 Aug 2018 14:58:18 +0100
+
+dgit (6.9) unstable; urgency=medium
+
+ * dgit: do not crash on push of a new gdr package. Closes:#906784.
+ * dgit: Remove unsubstituted $changesfile from message Closes:906787.
+ * dgit-maint-debrebase(7): improve "Converting an existing package",
+ and refer to "ILLEGAL OPERATIONS" in git-debrebase(5).
+ Closes:#905573. [ Sean Whitton ]
+ * test suite: Update debian/tests/control following dependency fix.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk> Tue, 21 Aug 2018 14:36:36 +0100
+
+dgit (6.8) unstable; urgency=medium
+
+ * test suite: Fix dependencies of new gdr-merge-conflicts test.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk> Mon, 20 Aug 2018 14:52:03 +0100
+
+dgit (6.7) unstable; urgency=medium
+
+ git-debrebase, bugfixes:
+ * make-patches: Do not bail if there aren't any.
+ * scrap: works properly when it does only rebase --abort.
+ * On rebase: always save ffq-prev even if we were both stitched and
+ laundered. Closes:#905975.
+
+ git-debrebase, improvements:
+ * Speed: improve laundry performance by a factor of ~55:1,
+ and analysis performance by factor of ~4.2:1. Closes:#905995.
+ * prepush: this is now a silent no-op if the branch is
+ unstitched. This is more friendly.
+ * convert-from-*: Snag on patches in d/patches which are not in series,
+ because they will be deleted. Closes:#904997.
+ * Highly experimental merge resolution support, enabled only with
+ special command line option.
+ * Lots of internal changes to support merge, and other work.
+ * convert-from-*: Check whether ffq-prev or debrebase-last indicate that
+ we are already in gdr format.
+ * convert-from-*: leave debrebase-last refs to hint to everyone that
+ this is now a gdr branch.
+
+ git-debrebase, improved messages:
+ * Improve ffq head recording message.
+ * Better (less copious by default) debug output.
+ * convert-from-gbp: Improve messages. Closes:#906641.
+ * Provide hints for unprocessable commits, depending on the apparent
+ branch ffq state, including possible suggestion to use convert-from-*.
+ Closes:#905005. Closes:#905279.
+
+ dgit, improved messages:
+ * Mention bad origs as possible cause of quilt fixup failure,
+ in both dgit(7) and in error messages. No longer suggest
+ --quilt=smash or dpkg-source --commit in the error message.
+ Closes:906196.
+ * Do not suggest --quilt modes if quilt fixup "stopped at"
+ a commit made by git-debrebase. Closes:#906197.
+ * Mention gitattributes as a potential problem in quilt linearisation
+ failure, when appropriate. Closes:#906199.
+
+ dgit, documentation:
+ * dgit(1): Encourage --overwrite rather than --overwrite=version.
+ * Document that we do not suppress attributes which affect git-archive.
+ This is related to #906199.
+
+ test suite:
+ * test suite: Set DEBFULLNAME
+ * test suite: unset GIT_EDITOR, so it works if user has that set.
+
+ Packaging:
+ * changelog: Add close note for #905400 to changelog entry for 6.5.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk> Mon, 20 Aug 2018 02:30:06 +0100
+
dgit (6.6~bpo9+1) stretch-backports; urgency=medium
* Rebuild for stretch-backports.
@@ -19,7 +152,7 @@ dgit (6.5) unstable; urgency=medium
* git-debrebase: New subcommand `scrap'. Closes:#905063.
git-debrebase error handling improvements:
- * git-debrebase: Properly reject bare dgit dsc imports
+ * git-debrebase: Properly reject bare dgit dsc imports. Closes:905400.
* git-debrebase: Improve some error message formatting.
* git-debrebase: Check for git-rebase in progress and abort most operations.
diff --git a/debian/control b/debian/control
index a36b357..5bc2a65 100644
--- a/debian/control
+++ b/debian/control
@@ -27,7 +27,7 @@ Description: git interoperability with the Debian archive
dgit clone and dgit fetch construct git commits from uploads.
Package: git-debrebase
-Depends: perl, git-core, libdpkg-perl, libfile-fnmatch-perl,
+Depends: perl, git-core, libdpkg-perl, libfile-fnmatch-perl, devscripts,
${misc:Depends}
Recommends: dgit, git-buildpackage
Architecture: all
diff --git a/debian/tests/control b/debian/tests/control
index e3274f2..1d29b2f 100644
--- a/debian/tests/control
+++ b/debian/tests/control
@@ -1,32 +1,36 @@
Tests: build-modes-gbp
Tests-Directory: tests/tests
-Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, git-buildpackage
+Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, faketime, git-buildpackage
Tests: clone-reprepro downstream-gitless
Tests-Directory: tests/tests
-Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, reprepro
+Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, faketime, reprepro
Tests: dpkgsourceignores-docs
Tests-Directory: tests/tests
-Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc
+Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, faketime
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, bc
+Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, faketime
Restrictions: x-dgit-intree-only x-dgit-git-only
-Tests: gdr-diverge-nmu gdr-diverge-nmu-dgit gdr-edits gdr-import-dgit gdr-import-dgitview gdr-makepatches7 gdr-subcommands gdr-unprocessable
+Tests: gdr-merge-conflicts
Tests-Directory: tests/tests
-Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, git-debrebase, git-buildpackage, faketime
+Depends: chiark-utils-bin, faketime, git-debrebase, git-buildpackage, quilt
+
+Tests: gdr-diverge-nmu gdr-diverge-nmu-dgit gdr-edits gdr-fresh gdr-import-dgit gdr-import-dgitview gdr-import-nostitch gdr-makepatches7 gdr-merge gdr-subcommands gdr-unprocessable gdr-unprocessable-hints
+Tests-Directory: tests/tests
+Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, faketime, git-debrebase, git-buildpackage
Tests: gdr-newupstream gdr-viagit
Tests-Directory: tests/tests
-Depends: chiark-utils-bin, git-debrebase, git-buildpackage, faketime
+Depends: chiark-utils-bin, faketime, git-debrebase, git-buildpackage
Tests: gitattributes
Tests-Directory: tests/tests
-Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, bsdgames, man-db, git-man
+Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, faketime, bsdgames, man-db, git-man
Tests: hint-testsuite-triggers
Tests-Directory: tests/tests
@@ -35,32 +39,36 @@ Restrictions: hint-testsuite-triggers
Tests: manpages-format
Tests-Directory: tests/tests
-Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, man-db, make, groff, git-debrebase
+Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, faketime, man-db, make, groff, git-debrebase
Tests: defdistro-mirror mirror mirror-debnewgit mirror-private
Tests-Directory: tests/tests
-Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, rsync
+Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, faketime, rsync
Tests: build-modes-sbuild quilt-gbp-build-modes-sbuild
Tests-Directory: tests/tests
-Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, sbuild
+Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, faketime, 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, bc, sbuild, man-db
+Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, faketime, 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, bc
+Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, faketime
Restrictions: x-dgit-git-only
+Tests: test-list-uptodate
+Tests-Directory: tests/tests
+Depends: git
+
Tests: trustingpolicy-replay
Tests-Directory: tests/tests
-Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, dput-ng
+Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, faketime, dput-ng
-Tests: absurd-gitapply badcommit-rewrite build-modes checkout clone-clogsigpipe clone-gitnosuite clone-nogit debpolicy-dbretry debpolicy-newreject debpolicy-quilt-gbp defdistro-rpush defdistro-setup distropatches-reject dpkgsourceignores-correct drs-clone-nogit drs-push-masterupdate drs-push-rejects dsd-clone-nogit dsd-divert fetch-localgitonly fetch-somegit-notlast gbp-orig gitconfig gitworktree import-dsc import-maintmangle import-native import-nonnative import-tarbomb inarchivecopy mismatches-contents mismatches-dscchanges multisuite newtag-clone-nogit oldnewtagalt oldtag-clone-nogit orig-include-exclude orig-include-exclude-chkquery overwrite-chkclog overwrite-junk overwrite-splitbrains overwrite-version pbuilder protocol-compat push-buildproductsdir push-newpackage push-newrepeat push-nextdgit push-source push-source-with-changes quilt quilt-gbp quilt-gbp-build-modes quilt-singlepatch quilt-splitbrains quilt-useremail rpush sourceonlypolicy tag-updates test-list-uptodate unrepresentable version-opt
+Tests: absurd-gitapply badcommit-rewrite build-modes build-modes-long build-modes-source checkout clone-clogsigpipe clone-gitnosuite clone-nogit debpolicy-dbretry debpolicy-newreject debpolicy-quilt-gbp 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 pbuilder protocol-compat push-buildproductsdir push-newpackage push-newrepeat push-nextdgit push-source push-source-with-changes quilt quilt-gbp quilt-gbp-build-modes quilt-singlepatch quilt-splitbrains quilt-useremail rpush sourceonlypolicy tag-updates unrepresentable version-opt
Tests-Directory: tests/tests
-Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc
+Depends: dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, faketime
diff --git a/dgit b/dgit
index dbb23f3..ea74cad 100755
--- a/dgit
+++ b/dgit
@@ -113,7 +113,7 @@ our (@curl) = (qw(curl --proto-redir), '-all,http,https', qw(-L));
our (@dput) = qw(dput);
our (@debsign) = qw(debsign);
our (@gpg) = qw(gpg);
-our (@sbuild) = qw(sbuild);
+our (@sbuild) = (qw(sbuild --no-source));
our (@ssh) = 'ssh';
our (@dgit) = qw(dgit);
our (@git_debrebase) = qw(git-debrebase);
@@ -289,6 +289,14 @@ sub bpd_abs () {
return $r;
}
+sub get_tree_of_commit ($) {
+ my ($commitish) = @_;
+ my $cdata = cmdoutput @git, qw(cat-file commit), $commitish;
+ $cdata =~ m/\n\n/; $cdata = $`;
+ $cdata =~ m/^tree (\w+)$/m or confess "cdata $cdata ?";
+ return $1;
+}
+
sub branch_gdr_info ($$) {
my ($symref, $head) = @_;
my ($status, $msg, $current, $ffq_prev, $gdrlast) =
@@ -300,21 +308,91 @@ sub branch_gdr_info ($$) {
return ($ffq_prev, $gdrlast);
}
-sub branch_is_gdr ($$) {
- my ($symref, $head) = @_;
- my ($ffq_prev, $gdrlast) = branch_gdr_info($symref, $head);
- return 0 unless $ffq_prev || $gdrlast;
- return 1;
-}
-
sub branch_is_gdr_unstitched_ff ($$$) {
my ($symref, $head, $ancestor) = @_;
my ($ffq_prev, $gdrlast) = branch_gdr_info($symref, $head);
return 0 unless $ffq_prev;
- return 0 unless is_fast_fwd $ancestor, $ffq_prev;
+ return 0 unless !defined $ancestor or is_fast_fwd $ancestor, $ffq_prev;
return 1;
}
+sub branch_is_gdr ($) {
+ my ($head) = @_;
+ # This is quite like git-debrebase's keycommits.
+ # We have our own implementation because:
+ # - our algorighm can do fewer tests so is faster
+ # - it saves testing to see if gdr is installed
+
+ # NB we use this jsut for deciding whether to run gdr make-patches
+ # Before reusing this algorithm for somthing else, its
+ # suitability should be reconsidered.
+
+ my $walk = $head;
+ local $Debian::Dgit::debugcmd_when_debuglevel = 3;
+ printdebug "branch_is_gdr $head...\n";
+ my $get_patches = sub {
+ my $t = git_cat_file "$_[0]:debian/patches", [qw(missing tree)];
+ return $t // '';
+ };
+ my $tip_patches = $get_patches->($head);
+ WALK:
+ for (;;) {
+ my $cdata = git_cat_file $walk, 'commit';
+ my ($hdrs,$msg) = $cdata =~ m{\n\n} ? ($`,$') : ($cdata,'');
+ if ($msg =~ m{^\[git-debrebase\ (
+ anchor | changelog | make-patches |
+ merged-breakwater | pseudomerge
+ ) [: ] }mx) {
+ # no need to analyse this - it's sufficient
+ # (gdr classifications: Anchor, MergedBreakwaters)
+ # (made by gdr: Pseudomerge, Changelog)
+ printdebug "branch_is_gdr $walk gdr $1 YES\n";
+ return 1;
+ }
+ my @parents = ($hdrs =~ m/^parent (\w+)$/gm);
+ if (@parents==2) {
+ my $walk_tree = get_tree_of_commit $walk;
+ foreach my $p (@parents) {
+ my $p_tree = get_tree_of_commit $p;
+ if ($p_tree eq $walk_tree) { # pseudomerge contriburor
+ # (gdr classification: Pseudomerge; not made by gdr)
+ printdebug "branch_is_gdr $walk unmarked pseudomerge\n"
+ if $debuglevel >= 2;
+ $walk = $p;
+ next WALK;
+ }
+ }
+ # some other non-gdr merge
+ # (gdr classification: VanillaMerge, DgitImportUnpatched, ?)
+ printdebug "branch_is_gdr $walk ?-2-merge NO\n";
+ return 0;
+ }
+ if (@parents>2) {
+ # (gdr classification: ?)
+ printdebug "branch_is_gdr $walk ?-octopus NO\n";
+ return 0;
+ }
+ if ($get_patches->($walk) ne $tip_patches) {
+ # Our parent added, removed, or edited patches, and wasn't
+ # a gdr make-patches commit. gdr make-patches probably
+ # won't do that well, then.
+ # (gdr classification of parent: AddPatches or ?)
+ printdebug "branch_is_gdr $walk ?-patches NO\n";
+ return 0;
+ }
+ if ($tip_patches eq '' and
+ !defined git_cat_file "$walk:debian") {
+ # (gdr classification of parent: BreakwaterStart
+ printdebug "branch_is_gdr $walk unmarked BreakwaterStart YES\n";
+ return 1;
+ }
+ # (gdr classification: Upstream Packaging Mixed Changelog)
+ printdebug "branch_is_gdr $walk plain\n"
+ if $debuglevel >= 2;
+ $walk = $parents[0];
+ }
+}
+
#---------- remote protocol support, common ----------
# remote push initiator/responder protocol:
@@ -1991,28 +2069,6 @@ sub make_commit ($) {
return cmdoutput @git, qw(hash-object -w -t commit), $file;
}
-sub make_commit_text ($) {
- my ($text) = @_;
- my ($out, $in);
- my @cmd = (@git, qw(hash-object -w -t commit --stdin));
- debugcmd "|",@cmd;
- print Dumper($text) if $debuglevel > 1;
- my $child = open2($out, $in, @cmd) or die $!;
- my $h;
- eval {
- print $in $text or die $!;
- close $in or die $!;
- $h = <$out>;
- $h =~ m/^\w+$/ or die;
- $h = $&;
- printdebug "=> $h\n";
- };
- close $out;
- waitpid $child, 0 == $child or die "$child $!";
- $? and failedcmd @cmd;
- return $h;
-}
-
sub clogp_authline ($) {
my ($clogp) = @_;
my $author = getfield $clogp, 'Maintainer';
@@ -3186,10 +3242,7 @@ END
# here we go, then:
my $tree_commit = $mergeinputs[0]{Commit};
- my $tree = cmdoutput @git, qw(cat-file commit), $tree_commit;
- $tree =~ m/\n\n/; $tree = $`;
- $tree =~ m/^tree (\w+)$/m or die "$dsc_hash tree ?";
- $tree = $1;
+ my $tree = get_tree_of_commit $tree_commit;;
# We use the changelog author of the package in question the
# author of this pseudo-merge. This is (roughly) correct if
@@ -3457,7 +3510,7 @@ END
sub multisuite_suite_child ($$$) {
- my ($tsuite, $merginputs, $fn) = @_;
+ my ($tsuite, $mergeinputs, $fn) = @_;
# in child, sets things up, calls $fn->(), and returns undef
# in parent, returns canonical suite name for $tsuite
my $canonsuitefh = IO::File::new_tmpfile;
@@ -3484,7 +3537,7 @@ sub multisuite_suite_child ($$$) {
return $csuite;
}
printdebug "multisuite $tsuite ok (canon=$csuite)\n";
- push @$merginputs, {
+ push @$mergeinputs, {
Ref => lrref,
Info => $csuite,
};
@@ -3528,7 +3581,6 @@ sub fork_for_multisuite ($) {
fetch_one();
finish 0;
});
- # xxx collecte the ref here
$csubsuite =~ s/^\Q$cbasesuite\E-/-/;
push @csuites, $csubsuite;
@@ -4258,6 +4310,15 @@ END
my $actualhead = git_rev_parse('HEAD');
if (branch_is_gdr_unstitched_ff($symref, $actualhead, $archive_hash)) {
+ if (quiltmode_splitbrain()) {
+ my ($ffq_prev, $gdrlast) = branch_gdr_info($symref, $actualhead);
+ fail <<END;
+Branch is managed by git-debrebase ($ffq_prev
+exists), but quilt mode ($quilt_mode) implies a split view.
+Pass the right --quilt option or adjust your git config.
+Or, maybe, run git-debrebase forget-was-ever-debrebase.
+END
+ }
runcmd_ordryrun_local @git_debrebase, 'stitch';
$actualhead = git_rev_parse('HEAD');
}
@@ -4499,9 +4560,8 @@ END
supplementary_message(<<'END');
Push failed, while obtaining signatures on the .changes and .dsc.
If it was just that the signature failed, you may try again by using
-debsign by hand to sign the changes
- $changesfile
-and then dput to complete the upload.
+debsign by hand to sign the changes file (see the command dgit tried,
+above), and then dput that changes file to complete the upload.
If you need to change the package, you must use a new version number.
END
if ($we_are_responder) {
@@ -5251,29 +5311,7 @@ END
my $dgitview = git_rev_parse 'HEAD';
changedir $maindir;
- # When we no longer need to support squeeze, use --create-reflog
- # instead of this:
- 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";
- if ($oldcache eq $dgitview) {
- my $tree = cmdoutput qw(git rev-parse), "$dgitview:";
- # git update-ref doesn't always update, in this case. *sigh*
- my $dummy = make_commit_text <<END;
-tree $tree
-parent $dgitview
-author Dgit <dgit\@example.com> 1000000000 +0000
-committer Dgit <dgit\@example.com> 1000000000 +0000
-
-Dummy commit - do not use
-END
- runcmd @git, qw(update-ref -m), "dgit $our_version - dummy",
- "refs/$splitbraincache", $dummy;
- }
- runcmd @git, qw(update-ref -m), $cachekey, "refs/$splitbraincache",
- $dgitview;
+ reflog_cache_insert "refs/$splitbraincache", $cachekey, $dgitview;
changedir "$playground/work";
@@ -5413,13 +5451,20 @@ sub quiltify ($$$$) {
};
if ($quilt_mode eq 'linear') {
print STDERR "\n$us: error: quilt fixup cannot be linear. Stopped at:\n";
+ my $all_gdr = !!@nots;
foreach my $notp (@nots) {
print STDERR "$us: ", $reportnot->($notp), "\n";
+ $all_gdr &&= $notp->{Child} &&
+ (git_cat_file $notp->{Child}{Commit}, 'commit')
+ =~ m{^\[git-debrebase(?! split[: ]).*\]$}m;
}
- print STDERR "$us: $_\n" foreach @$failsuggestion;
+ print STDERR "\n";
+ $failsuggestion =
+ [ grep { $_->[0] ne 'quilt-mode' } @$failsuggestion ]
+ if $all_gdr;
+ print STDERR "$us: $_->[1]\n" foreach @$failsuggestion;
fail
- "quilt history linearisation failed. Search \`quilt fixup' in dgit(7).\n".
- "Use dpkg-source --commit by hand; or, --quilt=smash for one ugly patch";
+ "quilt history linearisation failed. Search \`quilt fixup' in dgit(7).\n";
} elsif ($quilt_mode eq 'smash') {
} elsif ($quilt_mode eq 'auto') {
progress "quilt fixup cannot be linear, smashing...";
@@ -5573,7 +5618,7 @@ END
if ($quilt_mode eq 'linear'
&& !$fopts->{'single-debian-patch'}
- && branch_is_gdr($symref, $headref)) {
+ && branch_is_gdr($headref)) {
# This is much faster. It also makes patches that gdr
# likes better for future updates without laundering.
#
@@ -5776,26 +5821,12 @@ sub quilt_check_splitbrain_cache ($$) {
push @cachekey, $srcshash->hexdigest();
$splitbrain_cachekey = "@cachekey";
- my @cmd = (@git, qw(log -g), '--pretty=format:%H %gs',
- $splitbraincache);
printdebug "splitbrain cachekey $splitbrain_cachekey\n";
- debugcmd "|(probably)",@cmd;
- my $child = open GC, "-|"; defined $child or die $!;
- if (!$child) {
- chdir $maindir or die $!;
- if (!stat "$maindir_gitcommon/logs/refs/$splitbraincache") {
- $! == ENOENT or die $!;
- printdebug ">(no reflog)\n";
- finish 0;
- }
- exec @cmd; die $!;
- }
- while (<GC>) {
- chomp;
- printdebug ">| ", $_, "\n" if $debuglevel > 1;
- next unless m/^(\w+) (\S.*\S)$/ && $2 eq $splitbrain_cachekey;
-
- my $cachehit = $1;
+
+ my $cachehit = reflog_cache_lookup
+ "refs/$splitbraincache", $splitbrain_cachekey;
+
+ if ($cachehit) {
unpack_playtree_mkwork($headref);
my $saved = maybe_split_brain_save $headref, $cachehit, "cache-hit";
if ($cachehit ne $headref) {
@@ -5807,8 +5838,6 @@ sub quilt_check_splitbrain_cache ($$) {
progress "dgit view: found cached, no changes required";
return ($headref, $splitbrain_cachekey);
}
- die $! if GC->error;
- failedcmd unless close GC;
printdebug "splitbrain cache miss\n";
return (undef, $splitbrain_cachekey);
@@ -5976,12 +6005,21 @@ END
my @failsuggestion;
if (!($diffbits->{O2H} & $diffbits->{O2A})) {
- push @failsuggestion, "This might be a patches-unapplied branch.";
- } elsif (!($diffbits->{H2A} & $diffbits->{O2A})) {
- push @failsuggestion, "This might be a patches-applied branch.";
+ push @failsuggestion, [ 'unapplied',
+ "This might be a patches-unapplied branch." ];
+ } elsif (!($diffbits->{H2A} & $diffbits->{O2A})) {
+ push @failsuggestion, [ 'applied',
+ "This might be a patches-applied branch." ];
}
- push @failsuggestion, "Maybe you need to specify one of".
- " --[quilt=]gbp --[quilt=]dpm --quilt=unapplied ?";
+ push @failsuggestion, [ 'quilt-mode',
+ "Maybe you need one of --[quilt=]gbp --[quilt=]dpm --quilt=unapplied ?" ];
+
+ push @failsuggestion, [ 'gitattrs',
+ "Warning: Tree has .gitattributes. See GITATTRIBUTES in dgit(7)." ]
+ if stat_exists '.gitattributes';
+
+ push @failsuggestion, [ 'origs',
+ "Maybe orig tarball(s) are not identical to git representation?" ];
if (quiltmode_splitbrain()) {
quiltify_splitbrain($clogp, $unapplied, $headref, $oldtiptree,
@@ -6188,16 +6226,27 @@ sub massage_dbp_args ($;$) {
my $dmode = '-F';
foreach my $l ($cmd, $xargs) {
next unless $l;
- @$l = grep { !(m/^-[SgGFABb]$/s and $dmode=$_) } @$l;
+ @$l = grep { !(m/^-[SgGFABb]$|^--build=/s and $dmode=$_) } @$l;
}
push @$cmd, '-nc';
#print STDERR "MASS1 ",Dumper($cmd, $xargs, $dmode);
my $r = WANTSRC_BUILDER;
printdebug "massage split $dmode.\n";
- $r = $dmode =~ m/[S]/ ? WANTSRC_SOURCE :
- $dmode =~ y/gGF/ABb/ ? WANTSRC_SOURCE | WANTSRC_BUILDER :
- $dmode =~ m/[ABb]/ ? WANTSRC_BUILDER :
- die "$dmode ?";
+ if ($dmode =~ s/^--build=//) {
+ $r = 0;
+ my @d = split /,/, $dmode;
+ $r |= WANTSRC_SOURCE if grep { s/^full$/binary/ } @d;
+ $r |= WANTSRC_SOURCE if grep { s/^source$// } @d;
+ $r |= WANTSRC_BUILDER if grep { m/./ } @d;
+ fail "Wanted to build nothing!" unless $r;
+ $dmode = '--build='. join ',', grep m/./, @d;
+ } else {
+ $r =
+ $dmode =~ m/[S]/ ? WANTSRC_SOURCE :
+ $dmode =~ y/gGF/ABb/ ? WANTSRC_SOURCE | WANTSRC_BUILDER :
+ $dmode =~ m/[ABb]/ ? WANTSRC_BUILDER :
+ die "$dmode ?";
+ }
printdebug "massage done $r $dmode.\n";
push @$cmd, $dmode;
#print STDERR "MASS2 ",Dumper($cmd, $xargs, $r);
@@ -6286,9 +6335,10 @@ sub postbuild_mergechanges_vanilla ($) {
sub cmd_build {
build_prep_early();
$buildproductsdir eq '..' or print STDERR <<END;
-$us: warning: build-products-dir set, but not supported by dgit build
-$us: warning: things may go wrong or files may go to the wrong place
+$us: warning: build-products-dir set, but not supported by dpkg-buildpackage
+$us: warning: build-products-dir will be ignored; files will go to ..
END
+ $buildproductsdir = '..';
my @dbp = (@dpkgbuildpackage, qw(-us -uc), changesopts_initial(), @ARGV);
my $wantsrc = massage_dbp_args \@dbp;
build_prep($wantsrc);
diff --git a/dgit-maint-debrebase.7.pod b/dgit-maint-debrebase.7.pod
index 0f2f892..e5abe17 100644
--- a/dgit-maint-debrebase.7.pod
+++ b/dgit-maint-debrebase.7.pod
@@ -209,47 +209,79 @@ patching the upstream source.
This section explains how to convert an existing Debian package to
this workflow. It should be skipped when debianising a new package.
-=head2 No existing git history
+If you have an existing git history that you have pushed to an
+ordinary git server like B<salsa.debian.org>, we start with that. If
+you don't already have it locally, you'll need to clone it, and obtain
+the corresponding orig.tar from the archive:
+
+=over 4
+
+ % git clone salsa.debian.org:Debian/foo
+ % cd foo
+ % dgit setup-new-tree
+ % origtargz
+
+=back
+
+If you don't have any existing git history, or you have history only
+on the special B<dgit-repos> server, we start with B<dgit clone>:
=over 4
% dgit clone foo
% cd foo
+
+=back
+
+Then we make new upstream tags available:
+
+=over 4
+
% git remote add -f upstream https://some.upstream/foo.git
=back
-=head2 Existing git history using another workflow
+We now use a B<git debrebase convert-from-*> command to convert your
+existing history to the git-debrebase(5) data model. Which command
+you should use depends on some facts about your repository:
+
+=over 4
+
+=item (A) There is no delta queue.
-First, if you don't already have the git history locally, clone it,
-and obtain the corresponding orig.tar from the archive:
+If there do not exist any Debian patches, use
=over 4
- % git clone salsa.debian.org:Debian/foo
- % cd foo
- % origtargz
+ % git debrebase convert-from-gbp
=back
-If your tree is patches-unapplied, some conversion work is needed.
-You can use
+=item (B) There is a delta queue, and patches are unapplied.
+
+This is the standard git-buildpackage(1) workflow: there are Debian
+patches, but the upstream source is committed to git without those
+patches applied. Use
=over 4
- git debrebase convert-from-gbp
+ % git debrebase convert-from-gbp
=back
-Then make new upstream tags available:
+=item (C) There is a delta queue, and patches are applied.
+
+Use
=over 4
- % git remote add -f upstream https://some.upstream/foo.git
+ % git debrebase convert-from-dgit-view
=back
-Now you simply need to ensure that your git HEAD is dgit-compatible,
+=back
+
+Finally, you need to ensure that your git HEAD is dgit-compatible,
i.e., it is exactly what you would get if you deleted .git, invoked
B<dpkg-buildpackage -S>, and then unpacked the resultant source
package.
@@ -258,8 +290,6 @@ 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>.
-
=head1 GIT CONFIGURATION
git-debrebase(1) does not yet support using B<git merge> to merge
@@ -593,6 +623,62 @@ 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.)
+=head2 Inspecting the history
+
+The git history made by git-debrebase can seem complicated.
+Here are some suggestions for helpful invocations of gitk and git.
+They can be adapted for other tools like tig(1), git-log(1), magit, etc.
+
+History of package in Debian, disregarding history from upstream:
+
+=over
+
+ % gitk --first-parent
+
+In a laundered branch, the delta queue is at the top.
+
+=back
+
+History of the packaging, excluding the delta queue:
+
+ % gitk :/debian :!/debian/patches
+
+Just the delta queue (i.e. Debian's changes to upstream):
+
+ % gitk --first-parent -- :/ :!/debian
+
+Full history including old versions of the delta queue:
+
+=over
+
+ % gitk --date-order
+
+The "Declare fast forward" commits you see have an older history
+(usually, an older delta queue) as one parent,
+and a newer history as the other.
+--date-order makes gitk show the delta queues in the right order.
+
+=back
+
+Complete diff since the last upload:
+
+=over
+
+ % git diff dgit/dgit/sid..HEAD -- :/ :!/debian/patches
+
+This includes changes to upstream files.
+
+=back
+
+Interdiff of delta queue since last upload, if you really want it:
+
+ % git debrebase make-patches
+ % git diff dgit/dgit/sid..HEAD -- debian/patches
+
+And of course there is:
+
+ % git debrebase status
+
=head2 Alternative ways to start a debrebase
Above we started an interactive debrebase by invoking git-debrebase(1)
@@ -631,6 +717,7 @@ git-rebase without a base argument will often
start the rebase too early,
and should be avoided.
Run git-debrebase instead.
+See also "ILLEGAL OPERATIONS" in git-debrebase(5).
=head1 SEE ALSO
diff --git a/dgit.1 b/dgit.1
index a21f212..16c0a01 100644
--- a/dgit.1
+++ b/dgit.1
@@ -346,6 +346,8 @@ For why, see
.B GITATTRIBUTES
in
.BR dgit(7) .
+Note that only attributes affecting the working tree are suppressed.
+git-archive may remain exciting.
If there is an existing macro attribute line
.B [attr]dgit-defuse-attrs
@@ -604,6 +606,13 @@ your git branch is not a descendant
of the version in the archive
according to the git revision history.
+It is safer not to specify
+.IR previous-version ,
+and usually it's not needed.
+Just say
+.BR --overwrite ,
+unless you know what you are doing.
+
This option is useful if you are the maintainer, and you have
incorporated NMU changes into your own git workflow in a way that
doesn't make your branch a fast forward from the NMU.
diff --git a/dgit.7 b/dgit.7
index b623d8c..c150921 100644
--- a/dgit.7
+++ b/dgit.7
@@ -249,6 +249,16 @@ See
and
.B dgit setup-gitattributes
in dgit(1).
+
+Note that dgit does not disable gitattributes
+unless they would actually interfere with your work on dgit branches.
+In particular, gitattributes which affect
+.B git archive
+are not disabled,
+so .origs you generate by hand can be wrong.
+You should consider using
+.B git-deborig (1)
+which gets this right, suppressing the attributes.
.SH PACKAGE SOURCE FORMATS
If you are not the maintainer, you do not need to worry about the
source format of the package. You can just make changes as you like
@@ -377,6 +387,12 @@ that are not representable by dpkg-source (such as some mode changes).
Or maybe you just forgot a necessary
.B --quilt=
option.
+
+Finally,
+this problem can occur if you have provided
+Debian git tooling such as git-debrebase, git-dpm or git-buildpackage
+with upstream git commit(s) or tag(s)
+which are not 100% identical to your orig tarball(s).
.SH SPLIT VIEW QUILT MODE
When working with git branches intended
for use with the `3.0 (quilt)' source format
diff --git a/git-debrebase b/git-debrebase
index 24be35e..3d1f2a8 100755
--- a/git-debrebase
+++ b/git-debrebase
@@ -36,6 +36,8 @@ use Dpkg::Version;
use File::FnMatch qw(:fnmatch);
use File::Copy;
+$debugcmd_when_debuglevel = 2;
+
our ($usage_message) = <<'END';
usages:
git-debrebase [<options>] [--|-i <git rebase options...>]
@@ -48,11 +50,14 @@ usages:
See git-debrebase(1), git-debrebase(5), dgit-maint-debrebase(7) (in dgit).
END
-our ($opt_force, $opt_noop_ok, @opt_anchors);
+our ($opt_force, $opt_noop_ok, $opt_merges, @opt_anchors);
our ($opt_defaultcmd_interactive);
our $us = qw(git-debrebase);
+our $wrecknoteprefix = 'refs/debrebase/wreckage';
+our $merge_cache_ref = 'refs/debrebase/merge-resolutions';
+
$|=1;
sub badusage ($) {
@@ -105,8 +110,8 @@ sub get_commit ($) {
sub D_UPS () { 0x02; } # upstream files
sub D_PAT_ADD () { 0x04; } # debian/patches/ extra patches at end
sub D_PAT_OTH () { 0x08; } # debian/patches other changes
-sub D_DEB_CLOG () { 0x10; } # debian/ (not patches/ or changelog)
-sub D_DEB_OTH () { 0x20; } # debian/changelog
+sub D_DEB_CLOG () { 0x10; } # debian/changelog
+sub D_DEB_OTH () { 0x20; } # debian/ (not patches/ or changelog)
sub DS_DEB () { D_DEB_CLOG | D_DEB_OTH; } # debian/ (not patches/)
our $playprefix = 'debrebase';
@@ -124,17 +129,42 @@ sub in_workarea ($) {
die $@ if $@;
}
-sub fresh_workarea () {
- $workarea = fresh_playground "$playprefix/work";
+sub fresh_workarea (;$) {
+ my ($subdir) = @_;
+ $subdir //= 'work';
+ $workarea = fresh_playground "$playprefix/$subdir";
in_workarea sub { playtree_setup };
}
+sub run_ref_updates_now ($$) {
+ my ($mrest, $updates) = @_;
+ # @$updates is a list of lines for git-update-ref, without \ns
+
+ my @upd_cmd = (git_update_ref_cmd "debrebase: $mrest", qw(--stdin));
+ debugcmd '>|', @upd_cmd;
+ open U, "|-", @upd_cmd or die $!;
+ foreach (@$updates) {
+ printdebug ">= ", $_, "\n";
+ print U $_, "\n" or die $!;
+ }
+ printdebug ">\$\n";
+ close U or failedcmd @upd_cmd;
+}
+
our $snags_forced = 0;
our $snags_tripped = 0;
our $snags_summarised = 0;
our @deferred_updates;
our @deferred_update_messages;
+sub merge_wreckage_cleaning ($) {
+ my ($updates) = @_;
+ git_for_each_ref("$wrecknoteprefix/*", sub {
+ my ($objid,$objtype,$fullrefname,$reftail) = @_;
+ push @$updates, "delete $fullrefname";
+ });
+}
+
sub all_snags_summarised () {
$snags_forced + $snags_tripped == $snags_summarised;
}
@@ -143,90 +173,168 @@ sub run_deferred_updates ($) {
confess 'dangerous internal error' unless all_snags_summarised();
- my @upd_cmd = (git_update_ref_cmd "debrebase: $mrest", qw(--stdin));
- debugcmd '>|', @upd_cmd;
- open U, "|-", @upd_cmd or die $!;
- foreach (@deferred_updates) {
- printdebug ">= ", $_, "\n";
- print U $_, "\n" or die $!;
- }
- printdebug ">\$\n";
- close U or failedcmd @upd_cmd;
-
+ merge_wreckage_cleaning \@deferred_updates;
+ run_ref_updates_now $mrest, \@deferred_updates;
print $_, "\n" foreach @deferred_update_messages;
@deferred_updates = ();
@deferred_update_messages = ();
}
+sub get_tree ($;$$) {
+ # tree object name => ([ $name, $info ], ...)
+ # where $name is the sort key, ie has / at end for subtrees
+ # $info is the LHS from git-ls-tree (<mode> <type> <hash>)
+ # without $precheck, will crash if $x does not exist, so don't do that;
+ # instead pass '' to get ().
+ my ($x, $precheck, $recurse) = @_;
+
+ return () if !length $x;
+
+ if ($precheck) {
+ my ($type, $dummy) = git_cat_file $x, [qw(tree missing)];
+ return () if $type eq 'missing';
+ }
+
+ $recurse = !!$recurse;
+
+ confess "get_tree needs object not $x ?" unless $x =~ m{^[0-9a-f]+\:};
+
+ our (@get_tree_memo, %get_tree_memo);
+ my $memo = $get_tree_memo{$recurse,$x};
+ return @$memo if $memo;
+
+ local $Debian::Dgit::debugcmd_when_debuglevel = 3;
+ my @l;
+ my @cmd = (qw(git ls-tree -z --full-tree));
+ push @cmd, qw(-r) if $recurse;
+ push @cmd, qw(--), $x;
+ my $o = cmdoutput @cmd;
+ $o =~ s/\0$//s;
+ my $last = '';
+ foreach my $l (split /\0/, $o) {
+ my ($i, $n) = split /\t/, $l, 2;
+ $n .= '/' if $i =~ m/^\d+ tree /;
+ push @l, [ $n, $i ];
+ confess "$x need $last < $n ?" unless $last lt $n;
+ }
+ $get_tree_memo{$recurse,$x} = \@l;
+ push @get_tree_memo, $x;
+ if (@get_tree_memo > 10) {
+ delete $get_tree_memo{ shift @get_tree_memo };
+ }
+ return @l;
+}
+
+sub trees_diff_walk ($$$;$) {
+ # trees_diff_walk [{..opts...},] $x, $y, sub {... }
+ # calls sub->($name, $ix, $iy) for each difference
+ # $x and $y are as for get_tree
+ # where $name, $ix, $iy are $name and $info from get_tree
+ # opts are all call even for names same in both
+ # recurse call even for names same in both
+ my $opts = shift @_ if @_>=4;
+ my ($x,$y,$call) = @_;
+ my $all = $opts->{all};
+ return if !$all and $x eq $y;
+ my @x = get_tree $x, 0, $opts->{recurse};
+ my @y = get_tree $y, 0, $opts->{recurse};
+ printdebug "trees_diff_walk(..$x,$y..) ".Dumper(\@x,\@y)
+ if $debuglevel >= 3;
+ while (@x || @y) {
+ my $cmp = !@x <=> !@y # eg @y empty? $cmp=-1, use x
+ || $x[0][0] cmp $y[0][0]; # eg, x lt y ? $cmp=-1, use x
+ my ($n, $ix, $iy); # all same? $cmp=0, use both
+ $ix=$iy='';
+ printdebug "trees_diff_walk $cmp : @{ $x[0]//[] } | @{ $y[0]//[] }\n"
+ if $debuglevel >= 3;
+ ($n, $ix) = @{ shift @x } if $cmp <= 0;
+ ($n, $iy) = @{ shift @y } if $cmp >= 0;
+ next if !$all and $ix eq $iy;
+ printdebug sprintf
+ "trees_diff_walk(%d,'%s','%s') call('%s','%s','%s')\n",
+ !!$all,$x,$y, $n,$ix,$iy
+ if $debuglevel >= 2;
+ $call->($n, $ix, $iy);
+ }
+}
+
sub get_differs ($$) {
my ($x,$y) = @_;
- # This resembles quiltify_trees_differ, in dgit, a bit.
+ # This does a similar job to quiltify_trees_differ, in dgit, a bit.
# But we don't care about modes, or dpkg-source-unrepresentable
# changes, and we don't need the plethora of different modes.
# Conversely we need to distinguish different kinds of changes to
# debian/ and debian/patches/.
+ # Also, here we have, and want to use, trees_diff_walk, because
+ # we may be calling this an awful lot and we want it to be fast.
my $differs = 0;
+ my @debian_info;
- my $rundiff = sub {
- my ($opts, $limits, $fn) = @_;
- my @cmd = (@git, qw(diff-tree -z --no-renames));
- push @cmd, @$opts;
- push @cmd, "$_:" foreach $x, $y;
- push @cmd, '--', @$limits;
- my $diffs = cmdoutput @cmd;
- foreach (split /\0/, $diffs) { $fn->(); }
- };
+ no warnings qw(exiting);
- $rundiff->([qw(--name-only)], [], sub {
- $differs |= $_ eq 'debian' ? DS_DEB : D_UPS;
- });
+ my $plain = sub { $_[0] =~ m{^(100|0*)644 blob }s; };
- if ($differs & DS_DEB) {
- $differs &= ~DS_DEB;
- $rundiff->([qw(--name-only -r)], [qw(debian)], sub {
- $differs |=
- m{^debian/patches/} ? D_PAT_OTH :
- $_ eq 'debian/changelog' ? D_DEB_CLOG :
- D_DEB_OTH;
- });
- die "mysterious debian changes $x..$y"
- unless $differs & (D_PAT_OTH|DS_DEB);
- }
-
- if ($differs & D_PAT_OTH) {
- my $mode;
- $differs &= ~D_PAT_OTH;
- my $pat_oth = sub {
- $differs |= D_PAT_OTH;
- no warnings qw(exiting); last;
- };
- $rundiff->([qw(--name-status -r)], [qw(debian/patches/)], sub {
- no warnings qw(exiting);
- if (!defined $mode) {
- $mode = $_; next;
+ trees_diff_walk "$x:", "$y:", sub {
+ my ($n,$ix,$iy) = @_;
+
+ # analyse difference at the toplevel
+
+ if ($n ne 'debian/') {
+ $differs |= D_UPS;
+ next;
+ }
+ if ($n eq 'debian') {
+ # one side has a non-tree for ./debian !
+ $differs |= D_DEB_OTH;
+ next;
+ }
+
+ my $xd = $ix && "$x:debian";
+ my $yd = $iy && "$y:debian";
+ trees_diff_walk $xd, $yd, sub {
+ my ($n,$ix,$iy) = @_;
+
+ # analyse difference in debian/
+
+ if ($n eq 'changelog' && (!$ix || $plain->($ix))
+ && $plain->($iy) ) {
+ $differs |= D_DEB_CLOG;
+ next;
}
- die unless s{^debian/patches/}{};
- my $ok;
- if ($mode eq 'A' && !m/\.series$/s) {
- $ok = 1;
- } elsif ($mode eq 'M' && $_ eq 'series') {
- my $x_s = (git_cat_file "$x:debian/patches/series", 'blob');
- my $y_s = (git_cat_file "$y:debian/patches/series", 'blob');
- chomp $x_s; $x_s .= "\n";
- $ok = $x_s eq substr($y_s, 0, length $x_s);
- } else {
- # nope
+ if ($n ne 'patches/') {
+ $differs |= D_DEB_OTH;
+ next;
}
- $mode = undef;
- $differs |= $ok ? D_PAT_ADD : D_PAT_OTH;
- });
- die "mysterious debian/patches changes $x..$y"
- unless $differs & (D_PAT_ADD|D_PAT_OTH);
- }
- printdebug sprintf "get_differs %s, %s = %#x\n", $x, $y, $differs;
+ my $xp = $ix && "$xd/patches";
+ my $yp = $iy && "$yd/patches";
+ trees_diff_walk { recurse=>1 }, $xp, $yp, sub {
+ my ($n,$ix,$iy) = @_;
+
+ # analyse difference in debian/patches
+
+ my $ok;
+ if ($n =~ m{/$}s) {
+ # we are recursing; directories may appear and disappear
+ $ok = 1;
+ } elsif ($n !~ m/\.series$/s && !$ix && $plain->($iy)) {
+ $ok = 1;
+ } elsif ($n eq 'series' && $plain->($ix) && $plain->($iy)) {
+ my $x_s = (git_cat_file "$xp/series", 'blob');
+ my $y_s = (git_cat_file "$yp/series", 'blob');
+ chomp $x_s; $x_s .= "\n";
+ $ok = $x_s eq substr($y_s, 0, length $x_s);
+ } else {
+ # nope
+ }
+ $differs |= $ok ? D_PAT_ADD : D_PAT_OTH;
+ };
+ };
+ };
+
+ printdebug sprintf "get_differs %s %s = %#x\n", $x, $y, $differs;
return $differs;
}
@@ -255,6 +363,24 @@ sub read_tree_subdir ($$) {
runcmd @git, qw(read-tree), "--prefix=$subdir/", $new_tree_object;
}
+sub read_tree_debian ($) {
+ my ($treeish) = @_;
+ read_tree_subdir 'debian', "$treeish:debian";
+ rm_subdir_cached 'debian/patches';
+}
+
+sub read_tree_upstream ($;$$) {
+ my ($treeish, $keep_patches, $tree_with_debian) = @_;
+ # if $tree_with_debian is supplied, will use that for debian/
+ # otherwise will save and restore it.
+ my $debian =
+ $tree_with_debian ? "$tree_with_debian:debian"
+ : cmdoutput @git, qw(write-tree --prefix=debian/);
+ runcmd @git, qw(read-tree), $treeish;
+ read_tree_subdir 'debian', $debian;
+ rm_subdir_cached 'debian/patches' unless $keep_patches;
+};
+
sub make_commit ($$) {
my ($parents, $message_paras) = @_;
my $tree = cmdoutput @git, qw(write-tree);
@@ -310,6 +436,354 @@ sub any_snags () {
return $snags_forced || $snags_tripped;
}
+sub ffq_prev_branchinfo () {
+ my $current = git_get_symref();
+ return gdr_ffq_prev_branchinfo($current);
+}
+
+sub record_gdrlast ($$;$) {
+ my ($gdrlast, $newvalue, $oldvalue) = @_;
+ $oldvalue ||= $git_null_obj;
+ push @deferred_updates, "update $gdrlast $newvalue $oldvalue";
+}
+
+sub fail_unprocessable ($) {
+ my ($msg) = @_;
+ changedir $maindir;
+ my ($ffqs, $ffqm, $symref, $ffq_prev, $gdrlast) = ffq_prev_branchinfo();
+
+ my $mangled = <<END;
+Branch/history seems mangled - no longer in gdr format.
+See ILLEGAL OPERATIONS in git-debrebase(5).
+END
+ chomp $mangled;
+
+ if (defined $ffqm) {
+ fail <<END;
+$msg
+Is this meant to be a gdr branch? $ffqm
+END
+ } elsif (git_get_ref $ffq_prev) {
+ fail <<END;
+$msg
+$mangled
+Consider git-debrebase scrap, to throw away your recent work.
+END
+ } elsif (!git_get_ref $gdrlast) {
+ fail <<END;
+$msg
+Branch does not seem to be meant to be a git-debrebase branch?
+Wrong branch, or maybe you needed git-debrebase convert-from-*.
+END
+ } elsif (is_fast_fwd $gdrlast, git_rev_parse 'HEAD') {
+ fail <<END;
+$msg
+$mangled
+END
+ } else {
+ fail <<END;
+$msg
+Branch/history mangled, and diverged since last git-debrebase.
+Maybe you reset to, or rebased from, somewhere inappropriate.
+END
+ }
+};
+
+sub gbp_pq_export ($$$) {
+ my ($bname, $base, $tip) = @_;
+ # must be run in a workarea. $bname and patch-queue/$bname
+ # ought not to exist. Leaves you on patch-queue/$bname with
+ # the patches staged but not committed.
+ # returns 1 if there were any patches
+ printdebug "gbp_pq_export $bname $base $tip\n";
+ runcmd @git, qw(checkout -q -b), $bname, $base;
+ runcmd @git, qw(checkout -q -b), "patch-queue/$bname", $tip;
+ my @gbp_cmd = (qw(gbp pq export));
+ my $r = system shell_cmd 'exec >../gbp-pq-err 2>&1', @gbp_cmd;
+ if ($r) {
+ { local ($!,$?); copy('../gbp-pq-err', \*STDERR); }
+ failedcmd @gbp_cmd;
+ }
+ return 0 unless stat_exists 'debian/patches';
+ runcmd @git, qw(add -f debian/patches);
+ return 1;
+}
+
+
+# MERGE-TODO allow merge resolution separately from laundering, before git merge
+
+# later/rework?
+# use git-format-patch?
+# our own patch identification algorithm?
+# this is an alternative strategy
+
+sub merge_failed ($$;@) {
+ my ($wrecknotes, $emsg, @xmsgs) = @_;
+ my @m;
+ push @m, "Merge resolution failed: $emsg";
+ push @m, @xmsgs;
+
+ changedir $maindir;
+
+ my @updates;
+ merge_wreckage_cleaning \@updates;
+ run_ref_updates_now "merge failed", \@updates;
+
+ @updates = ();
+ keys %$wrecknotes;
+ while (my ($k,$v) = each %$wrecknotes) {
+ push @updates, "create $wrecknoteprefix/$k $v";
+ }
+ run_ref_updates_now "merge failed", \@updates;
+ push @m, "Wreckage left in $wrecknoteprefix/*.";
+
+ push @m, "See git-debrebase(1) section FAILED MERGES for suggestions.";
+
+ # use finish rather than fail, in case we are within an eval
+ # (that can happen inside walk!)
+ print STDERR "\n";
+ print STDERR "$us: $_\n" foreach @m;
+ finish 15;
+}
+
+sub mwrecknote ($$$) {
+ my ($wrecknotes, $reftail, $commitish) = @_;
+ confess unless defined $commitish;
+ printdebug "mwrecknote $reftail $commitish\n";
+ $wrecknotes->{$reftail} = $commitish;
+}
+
+sub merge_attempt_cmd {
+ my $wrecknotes = shift @_;
+ debugcmd '+', @_;
+ $!=0; $?=-1;
+ if (system @_) {
+ merge_failed $wrecknotes,
+ failedcmd_waitstatus(),
+ "failed command: @_";
+ }
+}
+
+sub merge_series_patchqueue_convert ($$$);
+
+sub merge_series ($$$;@) {
+ my ($newbase, $wrecknotes, $base_q, @input_qs) = @_;
+ # $base_q{SeriesBase} $input_qs[]{SeriesBase}
+ # $base_q{SeriesTip} $input_qs[]{SeriesTip}
+ # ^ specifies several patch series (currently we only support exactly 2)
+ # return value is a commit which is the result of
+ # merging the two versions of the same topic branch
+ # $input_q[0] and $input_q[1]
+ # with respect to the old version
+ # $base_q
+ # all onto $newbase.
+
+ # Creates, in *_q, a key MR for its private use
+
+ printdebug "merge_series newbase=$newbase\n";
+
+ $input_qs[$_]{MR}{S} = $_ foreach (0..$#input_qs);
+ $base_q->{MR}{S} = 'base';
+
+ my %prereq;
+ # $prereq{<patch filename>}{<possible prereq}{<S>} = 1 or absent
+ # $prereq{<patch filename>}{<possible prereq} exists or not (later)
+ # $prereq{<patch filename>} exists or not (even later)
+
+ my $merged_pq;
+
+ my $mwrecknote = sub { &mwrecknote($wrecknotes, @_); };
+
+ my $attempt_cmd = sub { &merge_attempt_cmd($wrecknotes, @_); };
+
+ local $workarea;
+ fresh_workarea "merge";
+ my $seriesfile = "debian/patches/series";
+ in_workarea sub {
+ playtree_setup();
+ foreach my $q ($base_q, reverse @input_qs) {
+ my $s = $q->{MR}{S};
+ my $any = gbp_pq_export "p-$s", $q->{SeriesBase}, $q->{SeriesTip};
+ my @earlier;
+ if ($any) {
+ open S, $seriesfile or die "$seriesfile $!";
+ while (my $patch = <S>) {
+ chomp $patch or die $!;
+ $prereq{$patch} //= {};
+ foreach my $earlier (@earlier) {
+ $prereq{$patch}{$earlier}{$s}++ and die;
+ }
+ push @earlier, $patch;
+ stat "debian/patches/$patch" or die "$patch ?";
+ }
+ S->error and die "$seriesfile $!";
+ close S;
+ }
+ read_tree_upstream $newbase, 1;
+ my $pec = make_commit [ grep { defined } $base_q->{MR}{PEC} ], [
+ "Convert $s to patch queue for merging",
+ "[git-debrebase merge-innards patch-queue import:".
+ " $q->{SeriesTip}]"
+ ];
+ printdebug "merge_series pec $pec ";
+ runcmd @git, qw(rm -q --ignore-unmatch --cached), $seriesfile;
+ $pec = make_commit [ $pec ], [
+ "Drop series file from $s to avoid merge trouble",
+ "[git-debrebase merge-innards patch-queue prep:".
+ " $q->{SeriesTip}]"
+ ];
+
+ read_tree_debian $newbase;
+ if (@earlier) {
+ read_tree_subdir 'debian/patches', "$pec:debian/patches";
+ } else {
+ rm_subdir_cached 'debian/patches';
+ }
+ $pec = make_commit [ $pec ], [
+ "Update debian/ (excluding patches) to final to avoid re-merging",
+ "debian/ was already merged and we need to just take that.",
+ "[git-debrebase merge-innards patch-queue packaging:".
+ " $q->{SeriesTip}]"
+ ];
+
+ printdebug "pec' $pec\n";
+ runcmd @git, qw(reset -q --hard), $pec;
+ $q->{MR}{PEC} = $pec;
+ $mwrecknote->("$q->{LeftRight}-patchqueue", $pec);
+ }
+ # now, because of reverse, we are on $input_q->{MR}{OQC}
+ runcmd @git, qw(checkout -q -b merge);
+ printdebug "merge_series merging...\n";
+ my @mergecmd = (@git, qw(merge --quiet --no-edit), "p-1");
+
+ $attempt_cmd->(@mergecmd);
+
+ printdebug "merge_series merge ok, series...\n";
+ # We need to construct a new series file
+ # Firstly, resolve prereq
+ foreach my $f (sort keys %prereq) {
+ printdebug "merge_series patch\t$f\t";
+ if (!stat_exists "debian/patches/$f") {
+ print DEBUG " drop\n" if $debuglevel;
+ # git merge deleted it; that's how we tell it's not wanted
+ delete $prereq{$f};
+ next;
+ }
+ print DEBUG " keep\n" if $debuglevel;
+ foreach my $g (sort keys %{ $prereq{$f} }) {
+ my $gfp = $prereq{$f}{$g};
+ printdebug "merge_series prereq\t$f\t-> $g\t";
+ if (!!$gfp->{0} == !!$gfp->{1}
+ ? $gfp->{0}
+ : !$gfp->{base}) {
+ print DEBUG "\tkeep\n" if $debuglevel;
+ } else {
+ print DEBUG "\tdrop\n" if $debuglevel;
+ delete $prereq{$f}{$g};
+ }
+ }
+ }
+
+ my $unsat = sub {
+ my ($f) = @_;
+ return scalar keys %{ $prereq{$f} };
+ };
+
+ my $nodate = time + 1;
+ my %authordate;
+ # $authordate{<patch filename>};
+ my $authordate = sub {
+ my ($f) = @_;
+ $authordate{$f} //= do {
+ open PF, "<", "debian/patches/$f" or die "$f $!";
+ while (<PF>) {
+ return $nodate if m/^$/;
+ last if s{^Date: }{};
+ }
+ chomp;
+ return cmdoutput qw(date +%s -d), $_;
+ };
+ };
+
+ open NS, '>', $seriesfile or die $!;
+
+ while (keys %prereq) {
+ my $best;
+ foreach my $try (sort keys %prereq) {
+ if ($best) {
+ next if (
+ $unsat->($try) <=> $unsat->($best) or
+ $authordate->($try) <=> $authordate->($best) or
+ $try cmp $best
+ ) >= 0;
+ }
+ $best = $try;
+ }
+ printdebug "merge_series series next $best\n";
+ print NS "$best\n" or die $!;
+ delete $prereq{$best};
+ foreach my $gp (values %prereq) {
+ delete $gp->{$best};
+ }
+ }
+
+ runcmd @git, qw(add), $seriesfile;
+ runcmd @git, qw(commit --quiet -m), 'Merged patch queue form';
+ $merged_pq = git_rev_parse 'HEAD';
+ $mwrecknote->('merged-patchqueue', $merged_pq);
+ };
+ return merge_series_patchqueue_convert
+ $wrecknotes, $newbase, $merged_pq;
+}
+
+sub merge_series_patchqueue_convert ($$$) {
+ my ($wrecknotes, $newbase, $merged_pq) = @_;
+
+ my $result;
+ in_workarea sub {
+ playtree_setup();
+ printdebug "merge_series series gbp pq import\n";
+ runcmd @git, qw(checkout -q -b mergec), $merged_pq;
+
+ merge_attempt_cmd($wrecknotes, qw(gbp pq import));
+ # MERGE-TODO consider git-format-patch etc. instead,
+ # since gbp pq doesn't always round-trip :-/
+
+ # OK now we are on patch-queue/merge, and we need to rebase
+ # onto the intended parent and drop the patches from each one
+
+ printdebug "merge_series series ok, building...\n";
+ my $build = $newbase;
+ my @lcmd = (@git, qw(rev-list --reverse mergec..patch-queue/mergec));
+ foreach my $c (grep /./, split /\n/, cmdoutput @lcmd) {
+ my $commit = git_cat_file $c, 'commit';
+ printdebug "merge_series series ok, building $c\n";
+ read_tree_upstream $c, 0, $newbase;
+ my $tree = cmdoutput @git, qw(write-tree);
+ $commit =~ s{^parent (\S+)$}{parent $build}m or confess;
+ $commit =~ s{^tree (\S+)$}{tree $tree}m or confess;
+ open C, ">", "../mcommit" or die $!;
+ print C $commit or die $!;
+ close C or die $!;
+ $build = cmdoutput @git, qw(hash-object -w -t commit ../mcommit);
+ }
+ $result = $build;
+ mwrecknote($wrecknotes, 'merged-result', $result);
+
+ runcmd @git, qw(update-ref refs/heads/result), $result;
+
+ runcmd @git, qw(checkout -q -b debug);
+ runcmd @git, qw(commit --allow-empty -q -m M-INDEX);
+ runcmd @git, qw(add .);
+ runcmd @git, qw(commit --allow-empty -q -m M-WORKTREE);
+ my $mdebug = git_rev_parse 'HEAD';
+ printdebug sprintf "merge_series done debug=%s\n", $mdebug;
+ mwrecknote($wrecknotes, 'merged-debug', $mdebug);
+ };
+ printdebug "merge_series returns $result\n";
+ return $result;
+}
+
# classify returns an info hash like this
# CommitId => $objid
# Hdr => # commit headers, including 1 final newline
@@ -567,16 +1041,47 @@ sub classify ($) {
OrigParents => \@orig_ps);
}
- return $unknown->("complex merge");
+ if (@p == 2 and
+ $r->{Msg} =~ m{^\[git-debrebase merged-breakwater.*\]$}m) {
+ return $classify->("MergedBreakwaters");
+ }
+ if ($r->{Msg} =~ m{^\[(git-debrebase|dgit)[: ].*\]$}m) {
+ return $unknown->("unknown kind of merge from $1");
+ }
+ if (@p > 2) {
+ return $unknown->("octopus merge");
+ }
+
+ if (!$opt_merges) {
+ return $unknown->("general two-parent merge");
+ }
+
+ return $classify->("VanillaMerge");
}
-sub keycommits ($;$$$$) {
- my ($head, $furniture, $unclean, $trouble, $fatal) = @_;
+sub keycommits ($;$$$$$);
+
+sub mergedbreakwaters_anchor ($) {
+ my ($cl) = @_;
+ my $best_anchor;
+ foreach my $p (@{ $cl->{Parents} }) {
+ my ($panchor, $pbw) = keycommits $p->{CommitId},
+ undef,undef,undef,undef, 1;
+ $best_anchor = $panchor
+ if !defined $best_anchor
+ or is_fast_fwd $best_anchor, $panchor;
+ fail "inconsistent anchors in merged-breakwaters $p->{CommitId}"
+ unless is_fast_fwd $panchor, $best_anchor;
+ }
+ return $best_anchor;
+}
+
+sub keycommits ($;$$$$$) {
+ my ($head, $furniture, $unclean, $trouble, $fatal, $claimed_bw) = @_;
# => ($anchor, $breakwater)
- # $unclean->("unclean-$tagsfx", $msg, $cl)
# $furniture->("unclean-$tagsfx", $msg, $cl)
- # $dgitimport->("unclean-$tagsfx", $msg, $cl))
+ # $unclean->("unclean-$tagsfx", $msg, $cl)
# is callled for each situation or commit that
# wouldn't be found in a laundered branch
# $furniture is for furniture commits such as might be found on an
@@ -587,6 +1092,8 @@ sub keycommits ($;$$$$) {
# $fatal is for unprocessable commits, and should normally cause
# a failure. If ignored, agaion, (undef, undef) is returned.
#
+ # If $claimed_bw, this is supposed to be a breakwater commit.
+ #
# If a callback is undef, fail is called instead.
# If a callback is defined but false, the situation is ignored.
# Callbacks may say:
@@ -594,10 +1101,11 @@ sub keycommits ($;$$$$) {
# if the answer is no longer wanted.
my ($anchor, $breakwater);
+ $breakwater = $head if $claimed_bw;
my $clogonly;
my $cl;
my $found_pm;
- $fatal //= sub { fail $_[1]; };
+ $fatal //= sub { fail_unprocessable $_[1]; };
my $x = sub {
my ($cb, $tagsfx, $mainwhy, $xwhy) = @_;
my $why = $mainwhy.$xwhy;
@@ -606,6 +1114,13 @@ sub keycommits ($;$$$$) {
return unless $cb;
$cb->("unclean-$tagsfx", $why, $cl, $mainwhy);
};
+ my $found_anchor = sub {
+ ($anchor) = @_;
+ $breakwater //= $clogonly;
+ $breakwater //= $head;
+ no warnings qw(exiting);
+ last;
+ };
for (;;) {
$cl = classify $head;
my $ty = $cl->{Type};
@@ -619,10 +1134,7 @@ sub keycommits ($;$$$$) {
} elsif ($ty eq 'Anchor' or
$ty eq 'TreatAsAnchor' or
$ty eq 'BreakwaterStart') {
- $anchor = $head;
- $breakwater //= $clogonly;
- $breakwater //= $head;
- last;
+ $found_anchor->($head);
} elsif ($ty eq 'Upstream') {
$x->($unclean, 'ordering',
"packaging change ($breakwater) follows upstream change"," (eg $head)")
@@ -650,6 +1162,12 @@ sub keycommits ($;$$$$) {
" ($head)");
return (undef,undef);
}
+ } elsif ($ty eq 'VanillaMerge') {
+ $x->($trouble, 'vanillamerge',
+ "found vanilla merge"," ($head)");
+ return (undef,undef);
+ } elsif ($ty eq 'MergedBreakwaters') {
+ $found_anchor->(mergedbreakwaters_anchor $cl);
} else {
$x->($fatal, 'unprocessable',
"found unprocessable commit, cannot cope: $cl->{Why}",
@@ -661,14 +1179,15 @@ sub keycommits ($;$$$$) {
return ($anchor, $breakwater);
}
-sub walk ($;$$);
-sub walk ($;$$) {
+sub walk ($;$$$);
+sub walk ($;$$$) {
my ($input,
- $nogenerate,$report) = @_;
+ $nogenerate,$report, $report_lprefix) = @_;
# => ($tip, $breakwater_tip, $last_anchor)
# (or nothing, if $nogenerate)
printdebug "*** WALK $input ".($nogenerate//0)." ".($report//'-')."\n";
+ $report_lprefix //= '';
# go through commits backwards
# we generate two lists of commits to apply:
@@ -683,8 +1202,8 @@ sub walk ($;$$) {
my ($prose, $info) = @_;
my $ms = $cl->{Msg};
chomp $ms;
- $info //= '';
- $ms .= "\n\n[git-debrebase$info: $prose]\n";
+ confess unless defined $info;
+ $ms .= "\n\n[git-debrebase $info: $prose]\n";
return (Msg => $ms);
};
my $rewrite_from_here = sub {
@@ -700,7 +1219,7 @@ sub walk ($;$$) {
my $prline = sub {
return unless $report;
- print $report $prdelim, @_;
+ print $report $prdelim, $report_lprefix, @_;
$prdelim = "\n";
};
@@ -711,7 +1230,7 @@ sub walk ($;$$) {
if ($nogenerate) {
return (undef,undef);
}
- fail "found unprocessable commit, cannot cope".
+ fail_unprocessable "found unprocessable commit, cannot cope".
(defined $cl->{Why} ? "; $cl->{Why}:": ':').
" (commit $cur) (d.".
(join ' ', map { sprintf "%#x", $_->{Differs} }
@@ -729,10 +1248,18 @@ sub walk ($;$$) {
no warnings qw(exiting); last;
};
+ my $nomerge = sub {
+ my ($emsg) = @_;
+ merge_failed $cl->{MergeWreckNotes}, $emsg;
+ };
+
+ my $mwrecknote = sub { &mwrecknote($cl->{MergeWreckNotes}, @_); };
+
my $last_anchor;
for (;;) {
$cl = classify $cur;
+ $cl->{MergeWreckNotes} //= {};
my $ty = $cl->{Type};
my $st = $cl->{SubType};
$prline->("$cl->{CommitId} $cl->{Type}");
@@ -757,7 +1284,7 @@ sub walk ($;$$) {
} elsif ($ty eq 'Mixed') {
my $queue = sub {
my ($q, $wh) = @_;
- my $cls = { %$cl, $xmsg->("split mixed commit: $wh part") };
+ my $cls = { %$cl, $xmsg->("mixed commit: $wh part",'split') };
push @$q, $cls;
};
$queue->(\@brw_cl, "debian");
@@ -810,12 +1337,12 @@ sub walk ($;$$) {
push @brw_cl, {
%$cl,
SpecialMethod => 'DgitImportDebianUpdate',
- $xmsg->("convert dgit import: debian changes")
+ $xmsg->("debian changes", 'convert dgit import')
}, {
%$cl,
SpecialMethod => 'DgitImportUpstreamUpdate',
$xmsg->("convert dgit import: upstream update",
- " anchor")
+ "anchor")
};
$prline->(" Import");
$rewrite_from_here->(\@brw_cl);
@@ -832,6 +1359,189 @@ sub walk ($;$$) {
return $bomb->();
}
die "$ty ?";
+ } elsif ($ty eq 'MergedBreakwaters') {
+ $last_anchor = mergedbreakwaters_anchor $cl;
+ $build_start->(' MergedBreakwaters', $cur);
+ last;
+ } elsif ($ty eq 'VanillaMerge') {
+ # User may have merged unstitched branch(es). We will
+ # have now lost what ffq-prev was then (since the later
+ # pseudomerge may introduce further changes). The effect
+ # of resolving such a merge is that we may have to go back
+ # further in history to find a merge base, since the one
+ # which was reachable via ffq-prev is no longer findable.
+ # This is suboptimal, but if it all works we'll have done
+ # the right thing.
+ # MERGE-TODO we should warn the user in the docs about this
+
+ my $ok=1;
+ my $best_anchor;
+ # We expect to find a dominating anchor amongst the
+ # inputs' anchors. That will be the new anchor.
+ #
+ # More complicated is finding a merge base for the
+ # breakwaters. We need a merge base that is a breakwater
+ # commit. The ancestors of breakwater commits are more
+ # breakwater commits and possibly upstream commits and the
+ # ancestors of those upstream. Upstreams might have
+ # arbitrary ancestors. But any upstream commit U is
+ # either included in both anchors, in which case the
+ # earlier anchor is a better merge base than any of U's
+ # ancestors; or U is not included in the older anchor, in
+ # which case U is not an ancestor of the vanilla merge at
+ # all. So no upstream commit, nor any ancestor thereof,
+ # is a best merge base. As for non-breakwater Debian
+ # commits: these are never ancestors of any breakwater.
+ #
+ # So any best merge base as found by git-merge-base
+ # is a suitable breakwater anchor. Usually there will
+ # be only one.
+
+ printdebug "*** MERGE\n";
+
+ my @bwbcmd = (@git, qw(merge-base));
+ my @ibcmd = (@git, qw(merge-base --all));
+ my $might_be_in_bw = 1;
+
+ my $ps = $cl->{Parents};
+
+ $mwrecknote->('vanilla-merge', $cl->{CommitId});
+
+ foreach my $p (@$ps) {
+ $prline->(" VanillaMerge ".$p->{Ix});
+ $prprdelim->();
+ my ($ptip, $pbw, $panchor) =
+ walk $p->{CommitId}, 0, $report,
+ $report_lprefix.' ';
+ $p->{Laundered} = $p->{SeriesTip} = $ptip;
+ $p->{Breakwater} = $p->{SeriesBase} = $pbw;
+ $p->{Anchor} = $panchor;
+
+ my $lr = $p->{LeftRight} = (qw(left right))[$p->{Ix}];
+ $mwrecknote->("$lr-input", $p->{CommitId});
+
+ my $mwrecknote_parent = sub {
+ my ($which) = @_;
+ $mwrecknote->("$lr-".(lc $which), $p->{$which});
+ };
+ $mwrecknote_parent->('Laundered');
+ $mwrecknote_parent->('Breakwater');
+ $mwrecknote_parent->('Anchor');
+
+ $best_anchor = $panchor if
+ !defined $best_anchor or
+ is_fast_fwd $best_anchor, $panchor;
+
+ printdebug " MERGE BA best=".($best_anchor//'-').
+ " p=$panchor\n";
+ }
+
+ $mwrecknote->('result-anchor', $best_anchor);
+
+ foreach my $p (@$ps) {
+ $prline->(" VanillaMerge ".$p->{Ix});
+ if (!is_fast_fwd $p->{Anchor}, $best_anchor) {
+ $nomerge->('divergent anchors');
+ } elsif ($p->{Anchor} eq $best_anchor) {
+ print $report " SameAnchor" if $report;
+ } else {
+ print $report " SupersededAnchor" if $report;
+ }
+ if ($p->{Breakwater} eq $p->{CommitId}) {
+ # this parent commit was its own breakwater,
+ # ie it is part of the breakwater
+ print $report " Breakwater" if $report;
+ } else {
+ $might_be_in_bw = 0;
+ }
+ push @bwbcmd, $p->{Breakwater};
+ push @ibcmd, $p->{CommitId};
+ }
+
+ if ($ok && $might_be_in_bw) {
+ # We could rewrite this to contaion the metadata
+ # declaring it to be MergedBreakwaters, but
+ # unnecessarily rewriting a merge seems unhelpful.
+ $prline->(" VanillaMerge MergedBreakwaters");
+ $last_anchor = $best_anchor;
+ $build_start->('MergedBreakwaters', $cur);
+ }
+
+ my $bwb = cmdoutput @bwbcmd;
+
+ # OK, now we have a breakwater base, but we need the merge
+ # base for the interchange branch because we need the delta
+ # queue.
+ #
+ # This a the best merge base of our inputs which has the
+ # breakwater merge base as an ancestor.
+
+ my @ibs =
+ grep /./,
+ split /\n/,
+ cmdoutput @ibcmd;
+
+ @ibs or confess 'internal error, expected anchor at least ?';
+
+ my $ib;
+ my $ibleaf;
+ foreach my $tibix (0..$#ibs) {
+ my $tib = $ibs[$tibix];
+ my $ff = is_fast_fwd $bwb, $tib;
+ my $ok = !$ff ? 'rej' : $ib ? 'extra' : 'ok';
+ my $tibleaf = "interchange-mbcand-$ok-$tibix";
+ $mwrecknote->($tibleaf, $tib);
+ next unless $ff;
+ next if $ib;
+ $ib = $tib;
+ $ibleaf = $tibleaf;
+ }
+
+ $ib or $nomerge->("no suitable interchange merge base");
+
+ $prline->(" VanillaMerge Base");
+ $prprdelim->();
+ my ($btip, $bbw, $banchor) = eval {
+ walk $ib, 0, $report, $report_lprefix.' ';
+ };
+ $nomerge->("walking interchange branch merge base ($ibleaf):\n".
+ $@)
+ if length $@;
+
+ $mwrecknote->("mergebase-laundered", $btip);
+ $mwrecknote->("mergebase-breakwater", $bbw);
+ $mwrecknote->("mergebase-anchor", $banchor);
+
+ my $ibinfo = { SeriesTip => $btip,
+ SeriesBase => $bbw,
+ Anchor => $banchor,
+ LeftRight => 'mergebase' };
+
+ $bbw eq $bwb
+ or $nomerge->("interchange merge-base ($ib)'s".
+ " breakwater ($bbw)".
+ " != breakwaters' merge-base ($bwb)");
+
+ grep { $_->{Anchor} eq $ibinfo->{Anchor} } @$ps
+ or $nomerge->("interchange merge-base ($ib)'s".
+ " anchor ($ibinfo->{SeriesBase})".
+ " != any merge input's anchor (".
+ (join ' ', map { $_->{Anchor} } @$ps).
+ ")");
+
+ $cl->{MergeInterchangeBaseInfo} = $ibinfo;
+ $cl->{MergeBestAnchor} = $best_anchor;
+ push @brw_cl, {
+ %$cl,
+ SpecialMethod => 'MergeCreateMergedBreakwaters',
+ $xmsg->('constructed from vanilla merge',
+ 'merged-breakwater'),
+ };
+ push @upp_cl, {
+ %$cl,
+ SpecialMethod => 'MergeMergeSeries',
+ };
+ $build_start->('MergeBreakwaters', $cur);
} else {
printdebug "*** WALK BOMB unrecognised\n";
return $bomb->();
@@ -850,17 +1560,6 @@ sub walk ($;$$) {
my $rewriting = 0;
- my $read_tree_debian = sub {
- my ($treeish) = @_;
- read_tree_subdir 'debian', "$treeish:debian";
- rm_subdir_cached 'debian/patches';
- };
- my $read_tree_upstream = sub {
- my ($treeish) = @_;
- runcmd @git, qw(read-tree), $treeish;
- $read_tree_debian->($build);
- };
-
$#upp_cl = $upp_limit if defined $upp_limit;
my $committer_authline = calculate_committer_authline();
@@ -872,7 +1571,12 @@ sub walk ($;$$) {
in_workarea sub {
mkdir $rd or $!==EEXIST or die $!;
my $current_method;
- runcmd @git, qw(read-tree), $build;
+ my $want_debian = $build;
+ my $want_upstream = $build;
+
+ my $read_tree_upstream = sub { ($want_upstream) = @_; };
+ my $read_tree_debian = sub { ($want_debian) = @_; };
+
foreach my $cl (qw(Debian), (reverse @brw_cl),
{ SpecialMethod => 'RecordBreakwaterTip' },
qw(Upstream), (reverse @upp_cl)) {
@@ -903,6 +1607,53 @@ sub walk ($;$$) {
next unless $differs & D_UPS;
$read_tree_upstream->($cltree);
push @parents, map { $_->{CommitId} } @{ $cl->{OrigParents} };
+ } elsif ($method eq 'MergeCreateMergedBreakwaters') {
+ print "Found a general merge, will try to tidy it up.\n";
+ $rewriting = 1;
+ $read_tree_upstream->($cl->{MergeBestAnchor});
+ $read_tree_debian->($cltree);
+ @parents = map { $_->{Breakwater} } @{ $cl->{Parents} };
+ } elsif ($method eq 'MergeMergeSeries') {
+ my $cachehit = reflog_cache_lookup
+ $merge_cache_ref, "vanilla-merge $cl->{CommitId}";
+ if ($cachehit) {
+ print "Using supplied resolution for $cl->{CommitId}...\n";
+ $build = $cachehit;
+ $mwrecknote->('cached-resolution', $build);
+ } else {
+ print "Running merge resolution for $cl->{CommitId}...\n";
+ $mwrecknote->('new-base', $build);
+ $build = merge_series
+ $build, $cl->{MergeWreckNotes},
+ $cl->{MergeInterchangeBaseInfo},
+ @{ $cl->{Parents} };
+ }
+ $last_anchor = $cl->{MergeBestAnchor};
+
+ # Check for mismerges:
+ my $check = sub {
+ my ($against, $allow, $what) = @_;
+ my $differs = get_differs $build, $against;
+ $nomerge->(sprintf
+ "merge misresolved: %s are not the same (%s %s d.%#x)",
+ $what, $against, $build, $differs)
+ if $differs & ~($allow | D_PAT_ADD);
+ };
+
+ # Breakwater changes which were in each side of the
+ # merge will have been incorporated into the
+ # MergeCreateMergedBreakwaters output. Because the
+ # upstream series was rebased onto the new breakwater,
+ # so should all of the packaging changes which were in
+ # the input.
+ $check->($input, D_UPS, 'debian files');
+
+ # Upstream files are merge_series, which ought to
+ # have been identical to the original merge.
+ $check->($cl->{CommitId}, DS_DEB, 'upstream files');
+
+ print "Merge resolution successful.\n";
+ next;
} else {
confess "$method ?";
}
@@ -913,27 +1664,46 @@ sub walk ($;$$) {
printdebug "WALK REWRITING NOW cl=$cl procd=$procd\n";
}
}
- my $newtree = cmdoutput @git, qw(write-tree);
- my $ch = $cl->{Hdr};
- $ch =~ s{^tree .*}{tree $newtree}m or confess "$ch ?";
- $ch =~ s{^parent .*\n}{}mg;
- $ch =~ s{(?=^author)}{
- join '', map { "parent $_\n" } @parents
- }me or confess "$ch ?";
if ($rewriting) {
- $ch =~ s{^committer .*$}{$committer_authline}m
- or confess "$ch ?";
+ read_tree_upstream $want_upstream, 0, $want_debian;
+
+ my $newtree = cmdoutput @git, qw(write-tree);
+ my $ch = $cl->{Hdr};
+ $ch =~ s{^tree .*}{tree $newtree}m or confess "$ch ?";
+ $ch =~ s{^parent .*\n}{}mg;
+ $ch =~ s{(?=^author)}{
+ join '', map { "parent $_\n" } @parents
+ }me or confess "$ch ?";
+ if ($rewriting) {
+ $ch =~ s{^committer .*$}{$committer_authline}m
+ or confess "$ch ?";
+ }
+ my $cf = "$rd/m$rewriting";
+ open CD, ">", $cf or die $!;
+ print CD $ch, "\n", $cl->{Msg} or die $!;
+ close CD or die $!;
+ my @cmd = (@git, qw(hash-object));
+ push @cmd, qw(-w) if $rewriting;
+ push @cmd, qw(-t commit), $cf;
+ my $newcommit = cmdoutput @cmd;
+ confess "$ch ?" unless $rewriting
+ or $newcommit eq $cl->{CommitId};
+ $build = $newcommit;
+ } else {
+ $build = $cl->{CommitId};
+ trees_diff_walk "$want_upstream:", "$build:", sub {
+ my ($n) = @_;
+ no warnings qw(exiting);
+ next if $n eq 'debian/';
+ confess "mismatch @_ ?";
+ };
+ trees_diff_walk "$want_debian:debian", "$build:debian", sub {
+ confess "mismatch @_ ?";
+ };
+ my @old_parents = map { $_->{CommitId} } @{ $cl->{Parents} };
+ confess "mismatch @parents != @old_parents ?"
+ unless "@parents" eq "@old_parents";
}
- my $cf = "$rd/m$rewriting";
- open CD, ">", $cf or die $!;
- print CD $ch, "\n", $cl->{Msg} or die $!;
- close CD or die $!;
- my @cmd = (@git, qw(hash-object));
- push @cmd, qw(-w) if $rewriting;
- push @cmd, qw(-t commit), $cf;
- my $newcommit = cmdoutput @cmd;
- confess "$ch ?" unless $rewriting or $newcommit eq $cl->{CommitId};
- $build = $newcommit;
if (grep { $method eq $_ } qw(DgitImportUpstreamUpdate)) {
$last_anchor = $cur;
}
@@ -941,7 +1711,7 @@ sub walk ($;$$) {
};
my $final_check = get_differs $build, $input;
- die sprintf "internal error %#x %s %s", $final_check, $build, $input
+ die sprintf "internal error %#x %s %s", $final_check, $input, $build
if $final_check & ~D_PAT_ADD;
my @r = ($build, $breakwater, $last_anchor);
@@ -968,7 +1738,7 @@ sub update_head_checkout ($$$) {
sub update_head_postlaunder ($$$) {
my ($old, $tip, $reflogmsg) = @_;
- return if $tip eq $old;
+ return if $tip eq $old && !@deferred_updates;
print "git-debrebase: laundered (head was $old)\n";
update_head $old, $tip, $reflogmsg;
# no tree changes except debian/patches
@@ -1027,11 +1797,6 @@ sub cmd_analyse () {
STDOUT->error and die $!;
}
-sub ffq_prev_branchinfo () {
- my $current = git_get_symref();
- return gdr_ffq_prev_branchinfo($current);
-}
-
sub ffq_check ($;$$) {
# calls $ff and/or $notff zero or more times
# then returns either (status,message) where status is
@@ -1136,7 +1901,7 @@ sub record_ffq_prev_deferred () {
push @deferred_updates, "update $ffq_prev $currentval $git_null_obj";
push @deferred_updates, "delete $gdrlast";
- push @deferred_update_messages, "Recorded current head for preservation";
+ push @deferred_update_messages, "Recorded previous head for preservation";
return ('deferred', undef);
}
@@ -1173,8 +1938,7 @@ sub stitch ($$$$$) {
# ffq-prev is ahead of us, and the only tree changes it has
# are possibly addition of things in debian/patches/.
# Just wind forwards rather than making a pointless pseudomerge.
- push @deferred_updates,
- "update $gdrlast $ffq_prev_commitish $git_null_obj";
+ record_gdrlast $gdrlast, $ffq_prev_commitish;
update_head_checkout $old_head, $ffq_prev_commitish,
"stitch (fast forward)";
return;
@@ -1187,7 +1951,7 @@ sub stitch ($$$$$) {
'Declare fast forward / record previous work',
"[git-debrebase pseudomerge: $prose]",
];
- push @deferred_updates, "update $gdrlast $new_head $git_null_obj";
+ record_gdrlast $gdrlast, $new_head;
update_head $old_head, $new_head, "stitch: $prose";
}
@@ -1406,36 +2170,45 @@ sub cmd_new_upstream () {
"[git-debrebase anchor: new upstream $new_upstream_version, merge]",
];
- my $clogsignoff = cmdoutput qw(git show),
- '--pretty=format:%an <%ae> %aD',
- $new_bw;
-
# Now we have to add a changelog stanza so the Debian version
- # is right.
- die if unlink "debian";
- die $! unless $!==ENOENT or $!==ENOTEMPTY;
- unlink "debian/changelog" or $!==ENOENT or die $!;
- mkdir "debian" or die $!;
- open CN, ">", "debian/changelog" or die $!;
- my $oldclog = git_cat_file ":debian/changelog";
- $oldclog =~ m/^($package_re) \(\S+\) / or
- fail "cannot parse old changelog to get package name";
- my $p = $1;
- print CN <<END, $oldclog or die $!;
-$p ($new_version) UNRELEASED; urgency=medium
-
- * Update to new upstream version $new_upstream_version.
-
- -- $clogsignoff
-
-END
- close CN or die $!;
+ # is right. We use debchange to do this. Invoking debchange
+ # here is a bit fiddly because it has a lot of optional
+ # exciting behaviours, some of which will break stuff, and
+ # some of which won't work in a playtree.
+
+ # Make debchange use git's idea of the user's identity.
+ # That way, if the user never uses debchange et al, configuring
+ # git is enough.
+ my $usetup = sub {
+ my ($e, $k) = @_;
+ my $v = cfg $k, 1;
+ defined $v or return;
+ $ENV{$e} = $v;
+ };
+ $usetup->('DEBEMAIL', 'user.email');
+ $usetup->('DEBFULLNAME', 'user.name');
+
+sleep 2;
+
+ my @dch = (qw(debchange
+ --allow-lower-version .*
+ --no-auto-nmu
+ --preserve
+ --vendor=Unknown-Vendor
+ --changelog debian/changelog
+ --check-dirname-level 0
+ --release-heuristic=changelog
+ -v), $new_version,
+ "Update to new upstream version $new_upstream_version.");
+
+ runcmd @git, qw(checkout -q debian/changelog);
+ runcmd @dch;
runcmd @git, qw(update-index --add --replace), 'debian/changelog';
# Now we have the final new breakwater branch in the index
$new_bw = make_commit [ $new_bw ],
[ "Update changelog for new upstream $new_upstream_version",
- "[git-debrebase: new upstream $new_upstream_version, changelog]",
+ "[git-debrebase changelog: new upstream $new_upstream_version]",
];
};
@@ -1571,7 +2344,10 @@ sub cmd_stitch () {
badusage "no arguments allowed" if @ARGV;
do_stitch $prose, 0;
}
-sub cmd_prepush () { cmd_stitch(); }
+sub cmd_prepush () {
+ $opt_noop_ok = 1;
+ cmd_stitch();
+}
sub cmd_quick () {
badusage "no arguments allowed" if @ARGV;
@@ -1595,19 +2371,32 @@ sub cmd_conclude () {
sub cmd_scrap () {
if (currently_rebasing()) {
runcmd @git, qw(rebase --abort);
+ push @deferred_updates, 'verify HEAD HEAD';
+ # noop, but stops us complaining that scrap was a noop
}
+ badusage "no arguments allowed" if @ARGV;
my ($ffq_prev, $gdrlast, $ffq_prev_commitish) = ffq_prev_info();
- if (!$ffq_prev_commitish) {
+ my $scrapping_head;
+ if ($ffq_prev_commitish) {
+ $scrapping_head = get_head();
+ push @deferred_updates,
+ "update $gdrlast $ffq_prev_commitish $git_null_obj",
+ "update $ffq_prev $git_null_obj $ffq_prev_commitish";
+ }
+ if (git_get_ref $merge_cache_ref) {
+ push @deferred_updates,
+ "delete $merge_cache_ref";
+ }
+ if (!@deferred_updates) {
fail "No ongoing git-debrebase session." unless $opt_noop_ok;
finish 0;
}
- my $scrapping_head = get_head();
- badusage "no arguments allowed" if @ARGV;
- push @deferred_updates,
- "update $gdrlast $ffq_prev_commitish $git_null_obj",
- "update $ffq_prev $git_null_obj $ffq_prev_commitish";
snags_maybe_bail();
- update_head_checkout $scrapping_head, $ffq_prev_commitish, "scrap";
+ if ($scrapping_head) {
+ update_head_checkout $scrapping_head, $ffq_prev_commitish, "scrap";
+ } else {
+ run_deferred_updates "scrap";
+ }
}
sub make_patches_staged ($) {
@@ -1616,31 +2405,30 @@ sub make_patches_staged ($) {
# laundered.
my ($secret_head, $secret_bw, $last_anchor) = walk $head;
fresh_workarea();
+ my $any;
in_workarea sub {
- runcmd @git, qw(checkout -q -b bw), $secret_bw;
- runcmd @git, qw(checkout -q -b patch-queue/bw), $secret_head;
- my @gbp_cmd = (qw(gbp pq export));
- my $r = system shell_cmd 'exec >../gbp-pq-err 2>&1', @gbp_cmd;
- if ($r) {
- { local ($!,$?); copy('../gbp-pq-err', \*STDERR); }
- failedcmd @gbp_cmd;
- }
- runcmd @git, qw(add -f debian/patches);
+ $any = gbp_pq_export 'bw', $secret_bw, $secret_head;
};
+ return $any;
}
sub make_patches ($) {
my ($head) = @_;
keycommits $head, 0, \&snag;
- make_patches_staged $head;
+ my $any = make_patches_staged $head;
my $out;
in_workarea sub {
- my $ptree = cmdoutput @git, qw(write-tree --prefix=debian/patches/);
+ my $ptree = !$any ? undef :
+ cmdoutput @git, qw(write-tree --prefix=debian/patches/);
runcmd @git, qw(read-tree), $head;
- read_tree_subdir 'debian/patches', $ptree;
+ if ($ptree) {
+ read_tree_subdir 'debian/patches', $ptree;
+ } else {
+ rm_subdir_cached 'debian/patches';
+ }
$out = make_commit [$head], [
'Commit patch queue (exported by git-debrebase)',
- '[git-debrebase: export and commit patches]',
+ '[git-debrebase make-patches: export and commit patches]',
];
};
return $out;
@@ -1671,6 +2459,54 @@ sub cmd_make_patches () {
}
}
+sub check_series_has_all_patches ($) {
+ my ($head) = @_;
+ my $seriesfn = 'debian/patches/series';
+ my ($dummy, $series) = git_cat_file "$head:$seriesfn",
+ [qw(blob missing)];
+ $series //= '';
+ my %series;
+ our $comments_snagged;
+ foreach my $f (grep /\S/, grep {!m/^\s\#/} split /\n/, $series) {
+ if ($f =~ m/^\s*\#/) {
+ snag 'series-comments',
+ "$seriesfn contains comments, which will be discarded"
+ unless $comments_snagged++;
+ next;
+ }
+ fail "patch $f repeated in $seriesfn !" if $series{$f}++;
+ }
+ foreach my $patchfile (get_tree "$head:debian/patches", 1,1) {
+ my ($f,$i) = @$patchfile;
+ next if $series{$f};
+ next if $f eq 'series';
+ snag 'unused-patches', "Unused patch file $f will be discarded";
+ }
+}
+
+sub begin_convert_from () {
+ my $head = get_head();
+ my ($ffqs, $ffqm, $symref, $ffq_prev, $gdrlast) = ffq_prev_branchinfo();
+
+ fail "ffq-prev exists, this is already managed by git-debrebase!"
+ if $ffq_prev && git_get_ref $ffq_prev;
+
+ my $gdrlast_obj = $gdrlast && git_get_ref $gdrlast;
+ snag 'already-converted',
+ "ahead of debrebase-last, this is already managed by git-debrebase!"
+ if $gdrlast_obj && is_fast_fwd $gdrlast_obj, $head;
+ return ($head, { LastRef => $gdrlast, LastObj => $gdrlast_obj });
+}
+
+sub complete_convert_from ($$$$) {
+ my ($old_head, $new_head, $gi, $mrest) = @_;
+ ffq_check $new_head;
+ record_gdrlast $gi->{LastRef}, $new_head, $gi->{LastObj}
+ if $gi->{LastRef};
+ snags_maybe_bail();
+ update_head_checkout $old_head, $new_head, $mrest;
+}
+
sub cmd_convert_from_gbp () {
badusage "want only 1 optional argument, the upstream git commitish"
unless @ARGV<=1;
@@ -1685,7 +2521,7 @@ sub cmd_convert_from_gbp () {
my $upstream =
resolve_upstream_version($upstream_spec, $upstream_version);
- my $old_head = get_head();
+ my ($old_head, $gdrlastinfo) = begin_convert_from();
my $upsdiff = get_differs $upstream, $old_head;
if ($upsdiff & D_UPS) {
@@ -1718,6 +2554,8 @@ END
"upstream ($upstream) contains debian/ directory";
}
+ check_series_has_all_patches $old_head;
+
my $previous_dgit_view = eval {
my @clogcmd = qw(dpkg-parsechangelog --format rfc822 -n2);
my ($lvsn, $suite);
@@ -1738,19 +2576,25 @@ END
my $mtag = cmdoutput @git, qw(describe --always --abbrev=0 --match),
$mtag_pat;
die "could not find suitable maintainer view tag $mtag_pat\n"
- unless $mtag_pat =~ m{/};
+ unless $mtag =~ m{/};
is_fast_fwd $mtag, 'HEAD' or
die "HEAD is not FF from maintainer tag $mtag!";
my $dtag = "archive/$mtag";
+ git_get_ref "refs/tags/$dtag" or
+ die "dgit view tag $dtag not found\n";
is_fast_fwd $mtag, $dtag or
- die "dgit view tag $dtag is not FF from maintainer tag $mtag";
+ die "dgit view tag $dtag is not FF from maintainer tag $mtag\n";
print "will stitch in dgit view, $dtag\n";
git_rev_parse $dtag;
};
if (!$previous_dgit_view) {
$@ =~ s/^\n+//;
chomp $@;
- print STDERR "cannot stitch in dgit view: $@\n";
+ print STDERR <<END;
+Cannot confirm dgit view: $@
+Failed to stitch in dgit view (see messages above).
+dgit --overwrite will be needed on the first dgit push after conversion.
+END
}
snags_maybe_bail_early();
@@ -1791,24 +2635,30 @@ END
}
};
- ffq_check $work;
- snags_maybe_bail();
- update_head_checkout $old_head, $work, 'convert-from-gbp';
+ complete_convert_from $old_head, $work, $gdrlastinfo, 'convert-from-gbp';
+ print <<END or die $!;
+git-debrebase: converted from patched-unapplied (gbp) branch format, OK
+END
}
sub cmd_convert_to_gbp () {
badusage "no arguments allowed" if @ARGV;
my $head = get_head();
my (undef, undef, undef, $ffq, $gdrlast) = ffq_prev_branchinfo();
- keycommits $head, 0;
- my $out;
- make_patches_staged $head;
- in_workarea sub {
- $out = make_commit ['HEAD'], [
- 'Commit patch queue (converted from git-debrebase format)',
- '[git-debrebase convert-to-gbp: commit patches]',
- ];
- };
+ my ($anchor, $breakwater) = keycommits $head, 0;
+ my $out = $breakwater;
+ my $any = make_patches_staged $head;
+ if ($any) {
+ in_workarea sub {
+ $out = make_commit [$out], [
+ 'Commit patch queue (converted from git-debrebase format)',
+ '[git-debrebase convert-to-gbp: commit patches]',
+ ];
+ };
+ } else {
+ # in this case, it can be fast forward
+ $out = $head;
+ }
if (defined $ffq) {
push @deferred_updates, "delete $ffq";
push @deferred_updates, "delete $gdrlast";
@@ -1850,7 +2700,7 @@ sub cmd_convert_from_dgit_view () {
};
}
- my $head = get_head();
+ my ($head, $gdrlastinfo) = begin_convert_from();
if (!$always) {
my $troubles = 0;
@@ -1868,6 +2718,8 @@ END
}
}
+ check_series_has_all_patches $head;
+
snags_maybe_bail_early();
my $version = upstreamversion $clogp->{Version};
@@ -1920,7 +2772,7 @@ Import effective orig tree for upstream version $version
END
This includes the contents of the .orig(s), minus any debian/ directory.
-[git-debrebase import-from-dgit-view upstream-import-convert: $version]
+[git-debrebase convert-from-dgit-view upstream-import-convert: $version]
END
];
push @upstreams, { Commit => $ups_synth,
@@ -1954,7 +2806,7 @@ END
'git-debrebase convert-from-dgit-view: drop upstream changes from breakwater',
"Drop upstream changes, and delete debian/patches, as part of converting\n".
"to git-debrebase format. Upstream changes will appear as commits.",
- '[git-debrebase convert-from-dgit-view: drop patches from tree]'
+ '[git-debrebase convert-from-dgit-view drop-patches]'
];
}
$work = make_commit [ $work, $u->{Commit} ], [
@@ -2004,12 +2856,62 @@ END
printf STDERR "Yes, will base new branch on %s\n", $result->{Source};
- ffq_check $result->{Result};
- snags_maybe_bail();
- update_head_checkout $head, $result->{Result},
+ complete_convert_from $head, $result->{Result}, $gdrlastinfo,
'convert-from-dgit-view';
}
+sub cmd_forget_was_ever_debrebase () {
+ badusage "forget-was-ever-debrebase takes no further arguments" if @ARGV;
+ my ($ffqstatus, $ffq_msg, $current, $ffq_prev, $gdrlast) =
+ ffq_prev_branchinfo();
+ fail "Not suitable for recording git-debrebaseness anyway: $ffq_msg"
+ if defined $ffq_msg;
+ push @deferred_updates, "delete $ffq_prev";
+ push @deferred_updates, "delete $gdrlast";
+ snags_maybe_bail();
+ run_deferred_updates "forget-was-ever-debrebase";
+}
+
+sub cmd_record_resolved_merge () {
+ badusage "record-resolved-merge takes no further arguments" if @ARGV;
+ # MERGE-TODO needs documentation
+ my $new = get_head();
+ my $method;
+
+ print "Checking how you have resolved the merge problem\n";
+ my $nope = sub { print "Not $method: @_"; 0; };
+
+ my $maybe = sub { print "Seems to be $method.\n"; };
+ my $yes = sub {
+ my ($key, $ref) = @_;
+ reflog_cache_insert $merge_cache_ref, $key, $ref;
+ print "OK. You can switch branches and try git-debrebase again.\n";
+ 1;
+ };
+
+ fresh_workarea 'merge';
+ sub {
+ $method = 'vanilla-merge patchqueue';
+ my $vanilla = git_get_ref "$wrecknoteprefix/vanilla-merge";
+ $vanilla or return $nope->("wreckage was not of vanilla-merge");
+ foreach my $lr (qw(left right)) {
+ my $n = "$wrecknoteprefix/$lr-patchqueue";
+ my $lrpq = git_get_ref $n;
+ $lrpq or return $nope->("wreckage did not contain patchqueues");
+ is_fast_fwd $lrpq, $new or return $nope->("HEAD not ff of $n");
+ }
+ $maybe->();
+ my $newbase = git_get_ref "$wrecknoteprefix/new-base"
+ or die "wreckage element $wrecknoteprefix/new-base missing";
+ my $result = merge_series_patchqueue_convert
+ {}, $newbase, $new;
+ $yes->("vanilla-merge $vanilla", $result);
+ 1;
+ }->() or sub {
+ fail "No resolved merge method seems applicable.\n";
+ }->();
+}
+
sub cmd_downstream_rebase_launder_v0 () {
badusage "needs 1 argument, the baseline" unless @ARGV==1;
my ($base) = @ARGV;
@@ -2067,6 +2969,7 @@ getoptions_main
'anchor=s' => \@opt_anchors,
'--dgit=s' => \($dgit[0]),
'force!',
+ 'experimental-merge-resolution!', \$opt_merges,
'-i:s' => sub {
my ($opt,$val) = @_;
badusage "git-debrebase: no cuddling to -i for git-rebase"
diff --git a/git-debrebase.1.pod b/git-debrebase.1.pod
index fe65674..1156825 100644
--- a/git-debrebase.1.pod
+++ b/git-debrebase.1.pod
@@ -95,16 +95,22 @@ If the branch is already laundered and stitched, does nothing.
=item git-debrebase prepush [--prose=<for commit message>]
+If the branch is unstitched,
+stitches it,
+consuming ffq-prev.
+
+This is a good command to run before pushing to a git server.
+You should consider using B<conclude> instead,
+because that launders the branch too.
+
=item git-debrebase stitch [--prose=<for commit message>]
Stitches the branch,
consuming ffq-prev.
-This is a good command to run before pushing to a git server.
If there is no ffq-prev, it is an error, unless --noop-ok.
-You should consider using B<conclude> instead,
-because that launders the branch too.
+You should consider using B<prepush> or B<conclude> instead.
=item git-debrebase scrap
@@ -416,6 +422,20 @@ and it should not be necessary.
=back
+=item git-debrebase forget-was-ever-debrebase
+
+Deletes the ffq-prev and debrebase-last refs
+associated with this branch,
+that git-debrebase and dgit use to determine
+whether this branch is managed by git-debrebase,
+and what previous head may need to be stitched back in.
+
+This can be useful if you were just playing with git-debrebase,
+and have used git-reset --hard to go back to a commit
+before your experiments.
+
+Do not use this if you expect to run git-debrebase on the branch again.
+
=back
=head1 OPTIONS
@@ -490,6 +510,20 @@ This is provided mostly for the benefit of the test suite.
Requests (more) debugging. May be repeated.
+=item --experimntal-merge-resolution
+
+Enable experimental code for handling general merges
+(see L<git-debrebase(5)/General merges>).
+
+This option may generate branch structures that
+require the use of this same option by other people.
+Also you may experience lossage of various kinds,
+including false error messages,
+references to nonexistent documentation,
+being handed an incomprehensible pile of
+multidimensional merge wreckage,
+and/or possible mangling of your package's source code.
+
=back
=head1 UNSTITCHING AND LAUNDERING
diff --git a/git-debrebase.5.pod b/git-debrebase.5.pod
index d39ad94..439fd63 100644
--- a/git-debrebase.5.pod
+++ b/git-debrebase.5.pod
@@ -378,8 +378,11 @@ from the interchange branch and no pseudomerge is needed.
When ffq-prev is not present,
C<refs/debrebase-last/B> records some ancestor of refs/B,
(usually, the result of last stitch).
-This can be used to quickly determine whether refs/B
-is being maintained in git-debrebase form.
+This is used for status printing and some error error checks -
+especially for printing guesses what a problem is.
+To determine whether a branch is
+is being maintained in git-debrebase form
+it is necessary to walk its history.
=head1 OTHER MERGES
@@ -527,6 +530,21 @@ If you add patches your work is likely to be discarded.
Instead of editing patches,
use git-debrebase to edit the corresponding commits.
+=item Renaming (etc.) branch while unstitched
+
+The previous HEAD,
+which will be pseudomerged over
+by operations like git-debrebase stitch,
+is recorded in a ref name dervied from your branch name.
+
+If you rename unstitched branches,
+this information can get out of step.
+
+Conversely,
+creating a new branch from an unstitched branch
+is good for making a branch to play about in,
+but the result cannot be stitched.
+
=back
=head1 COMMIT MESSAGE ANNOTATIONS
@@ -536,27 +554,31 @@ in the messages of commits it generates.
The general form is
- [git-debrebase[ COMMIT-TYPE [ ARGS...]]: PROSE, MORE PROSE]
+ [git-debrebase COMMIT-TYPE [ ARGS...]: PROSE, MORE PROSE]
git-debrebase treats anything after the colon as a comment,
paying no attention to PROSE.
The full set of annotations is:
- [git-debrebase: split mixed commit, debian part]
- [git-debrebase: split mixed commit, upstream-part]
- [git-debrebase: convert dgit import, debian changes]
+ [git-debrebase split: mixed commit, debian part]
+ [git-debrebase split: mixed commit, upstream-part]
+ [git-debrebase onvert dgit import: debian changes]
[git-debrebase anchor: convert dgit import, upstream changes]
[git-debrebase upstream-combine . PIECE[ PIECE...]: new upstream]
[git-debrebase anchor: new upstream NEW-UPSTREAM-VERSION, merge]
- [git-debrebase: new upstream NEW-UPSTREAM-VERSION, changelog]
- [git-debrebase: export and commit patches]
+ [git-debrebase changelog: new upstream NEW-UPSTREAM-VERSION]
+ [git-debrebase make-patches: export and commit patches]
[git-debrebase convert-from-gbp: drop patches]
[git-debrebase anchor: declare upstream]
[git-debrebase pseudomerge: stitch]
+ [git-debrebase merged-breakwater: constructed from vanilla merge]
+
[git-debrebase convert-to-gbp: commit patches]
+ [git-debrebase convert-from-dgit-view upstream-import-convert: VERSION]
+ [git-debrebase convert-from-dgit-view drop-patches]
Only anchor merges have the C<[git-debrebase anchor: ...]> tag.
Single-parent anchors are not generated by git-debrebase,
diff --git a/local-pod-man b/local-pod-man
deleted file mode 100755
index 3c3e0ea..0000000
--- a/local-pod-man
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/bash
-set -e
-
-case "$#.$1" in
-1.[^-]*) ;;
-*) echo >&2 'usage: ./local-pod-man dgit-something[.7[.pod]]'; exit 16;;
-esac
-base="$1"
-base="${base%.pod}"
-base="${base%.7}"
-
-make "$base.7"
-man -l "$base.7"
diff --git a/tests/Makefile b/tests/Makefile
index 88bc0a0..31eb7b6 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -11,11 +11,11 @@ all: $(foreach t,$(TESTNAMES),tests/tmp/$t.ok)
tests/tmp/%.ok: tests/tests/%
ifeq ($(DGIT_TEST_RETRY_COUNT),)
- tests/tests/$* >tests/tmp/$*.log 2>&1
+ $(DGIT_TEST_RUN_PFX) tests/tests/$* >tests/tmp/$*.log 2>&1
else
@for retry in $$( seq 1 $(DGIT_TEST_RETRY_COUNT) ); do \
echo "[$$retry] $*"; \
- tests/tests/$* >tests/tmp/$*.$$retry.log 2>&1; \
+ $(DGIT_TEST_RUN_PFX) tests/tests/$* >tests/tmp/$*.$$retry.log 2>&1; \
rc=$$?; \
if [ $$rc = 0 ]; then exit 0; fi; \
echo >&2 "[$$retry] $* TEST FAILED $$rc"; \
diff --git a/tests/enumerate-tests b/tests/enumerate-tests
index 0f37c96..429d46d 100755
--- a/tests/enumerate-tests
+++ b/tests/enumerate-tests
@@ -42,7 +42,7 @@ finish- () {
test-begin-gencontrol () {
restrictions=''
- dependencies='dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc'
+ dependencies='dgit, dgit-infrastructure, devscripts, debhelper (>=8), fakeroot, build-essential, chiark-utils-bin, bc, faketime'
}
restriction-gencontrol () {
@@ -58,10 +58,10 @@ gencontrol-add-deps () {
dependencies-gencontrol () {
for dep in "$deps"; do
case "$dep" in
- NO-DGIT) dependencies='chiark-utils-bin' ;;
+ NO-DGIT) dependencies='chiark-utils-bin, faketime' ;;
NO-DEFAULT) dependencies='' ;;
GDR) gencontrol-add-deps \
- git-debrebase git-buildpackage faketime
+ git-debrebase git-buildpackage
;;
*) gencontrol-add-deps "$dep" ;;
esac
diff --git a/tests/lib b/tests/lib
index 6028133..57b9e38 100644
--- a/tests/lib
+++ b/tests/lib
@@ -33,8 +33,8 @@ export DGIT_TEST_DEBUG
: ${DGIT_TEST_DISTRO+ ${distro=${DGIT_TEST_DISTRO}}}
-export GIT_COMMITTER_DATE='1515000000 +0100'
-export GIT_AUTHOR_DATE='1515000000 +0100'
+export GIT_COMMITTER_DATE='1530000000 +0100'
+export GIT_AUTHOR_DATE='1530000000 +0100'
root=`pwd`
troot=$root/tests
@@ -62,10 +62,11 @@ t-set-using-tmp
test -f $tmp/.save-env || \
env -0 >$tmp/.save-env
-ln -f $troot/ssh ssh
+ln -sf $troot/ssh ssh
export DEBCHANGE_VENDOR=dpkg
unset VISUAL
+unset GIT_EDITOR
mkdir -p $tmp/incoming
cat <<END >$tmp/dput.cf
@@ -341,7 +342,7 @@ t-archive () {
v=$2
local dscf=${p}_$2.dsc
rm -f $tmp/mirror/pool/main/${p}_*
- ln $troot/pkg-srcs/${p}_${2%-*}* $tmp/mirror/pool/main/
+ ln -s $troot/pkg-srcs/${p}_${2%-*}* $tmp/mirror/pool/main/
t-archive-query $suite
rm -rf $tmp/extract
mkdir $tmp/extract
@@ -403,6 +404,13 @@ t-git-fsck () {
esac
}
+t-check-only-bpd () {
+ if [ "$bpd" = .. ]; then return; fi
+ t-files-notexist \
+ ../*.{deb,changes,dsc,buildinfo} \
+ ../*.{tar,diff}.*
+}
+
t-fscks () {
(
shopt -s nullglob
@@ -415,6 +423,7 @@ t-fscks () {
t-ok () {
: '========================================'
+ t-check-only-bpd
t-fscks
t-save-artifacts
echo ok.
@@ -1025,10 +1034,32 @@ t-make-badcommit () {
t-expect-fsck-fail $badcommit
}
+t-make-orig () {
+ # leaves ust set to filename of orig tarball
+ local p=$1
+ local v=$2
+ local tag=${3-v$2}
+ ust=${p}_${v}.orig.tar.gz
+ GZIP=-1 git archive -o $bpd/$ust --prefix=${p}-${v}/ $tag
+}
+
+t-merge-conflicted-stripping-conflict-markers () {
+ local otherbranch=$1
+ local file=$2
+
+ t-expect-fail F:"Merge conflict in $file" \
+ git merge $otherbranch
+
+ perl -i~ -ne 'print unless m{^(?:\<\<\<|\>\>\>|===)}' "$file"
+ git add "$file"
+ git commit --no-edit
+}
+
t-commit () {
local msg=$1
v=${2:-${majorv:-1}.$revision}
- dch --force-distribution -v$v --distribution ${3:-unstable} "$1"
+ $troot/tstunt/debchange \
+ --force-distribution -v$v --distribution ${3:-unstable} "$1"
git add debian/changelog
debcommit
revision=$(( ${revision-0} + 1 ))
@@ -1045,7 +1076,7 @@ t-dch-commit-r () {
}
t-dch-commit () {
- faketime @"${GIT_AUTHOR_DATE% *}" dch "$@"
+ $troot/tstunt/debchange "$@"
git commit -m "dch $*" debian/changelog
}
diff --git a/tests/lib-build-modes b/tests/lib-build-modes
index 3bb1bf3..dbceb42 100644
--- a/tests/lib-build-modes
+++ b/tests/lib-build-modes
@@ -14,6 +14,7 @@ END
bm-prep () {
t-tstunt-parsechangelog
+ t-tstunt dpkg-deb
t-prep-newpackage example 1.0
@@ -250,3 +251,7 @@ bm-act-iterate () {
done
: bm-act-iterate done.
}
+
+bm-buildproductsdir-nonworking () {
+ t-git-config dgit.default.build-products-dir ../bpd-dummy
+}
diff --git a/tests/lib-core b/tests/lib-core
index 724c8ac..4550b9e 100644
--- a/tests/lib-core
+++ b/tests/lib-core
@@ -25,8 +25,9 @@ t-set-using-tmp () {
export DGIT_TEST_DUMMY_DIR=$tmp
export DGIT_TEST_TMP=$tmp
export GNUPGHOME=$tmp/nonexistent
+ export DEBFULLNAME='dgit test git user'
git config --global user.email 'dgit-test@debian.example.net'
- git config --global user.name 'dgit test git user'
+ git config --global user.name "$DEBFULLNAME"
git config --global protocol.ext.allow always
}
diff --git a/tests/lib-gdr b/tests/lib-gdr
index 479ef41..cda11df 100644
--- a/tests/lib-gdr
+++ b/tests/lib-gdr
@@ -3,6 +3,8 @@
: ${GDR_TEST_DEBUG=-D}
export GDR_TEST_DEBUG
+t-tstunt debchange
+
t-git-debrebase () {
local gdr=${DGIT_GITDEBREBASE_TEST-git-debrebase}
: '
@@ -21,22 +23,35 @@ t-gdr-good () {
# stitched
# pushed
- git diff --quiet ${beforetag-t.before} -- ':.' ':!debian/patches'
+ case $state in
+ pushed*)
+ t-gdr-made-patches
+ ;;
+ esac
- local etypes bwtip
+ git diff --quiet ${beforetag-t.before} -- ':.' ':!debian/patches'
LC_MESSAGES=C t-git-debrebase status >../status.check
case $state in
- laundered)
+ laundered*)
egrep '^ *branch is laundered' ../status.check
;;
- stitched|pushed)
+ stitched*|pushed*)
egrep \
'^ *branch contains furniture|^ *branch is unlaundered|^ *branch needs laundering' ../status.check
egrep '^ stitched$' ../status.check
;;
esac
+ t-gdr-good-analyse HEAD $state
+}
+
+t-gdr-good-analyse () {
+ local head=$1
+ local state=$2
+ local wsfx=$3
+ local etypes bwtip
+
# etypes is either a type,
# or PseudoMerge-<more etypes>
# or AddPatches-<more etypes>
@@ -49,11 +64,14 @@ t-gdr-good () {
stitched) etypes=Pseudomerge-Upstream ;;
pushed) etypes=AddPatches-Pseudomerge-Upstream ;;
pushed-interop) etypes=Pseudomerge-AddPatchesInterop-Upstream ;;
+ breakwater) etypes=Packaging ;;
+ *) fail-unknown-state-$state ;;
esac
- t-git-debrebase analyse >../anal.check
- expect=`git rev-parse HEAD`
- exec <../anal.check
+ anal=../anal$wsfx
+ t-git-debrebase analyse $head >$anal.check
+ expect=`git rev-parse $head`
+ exec <$anal.check
local cid ctype info nparents
while read cid ctype info; do
: ===== $cid $ctype $info =====
@@ -69,6 +87,8 @@ t-gdr-good () {
Pseudomerge/SAME) ;;
Packaging/SAME) ;;
Packaging/Upstream) ;;
+ MergedBreakwaters/Packaging) ;;
+ MergedBreakwaters/Upstream) ;;
AddPatches/SAME) ;;
AddPatches/AddPatchesInterop) ;;
Changelog/Packaging) ;;
@@ -108,9 +128,9 @@ t-gdr-good () {
: 'reject pointless pseudomerges'
local overwritten=${parents/$expect/}
overwritten=${overwritten// /}
- t-git-debrebase analyse $overwritten >../anal.overwr
+ t-git-debrebase analyse $overwritten >$anal.overwr
local ocid otype oinfo
- read <../anal.overwr ocid otype oinfo
+ read <$anal.overwr ocid otype oinfo
case "$otype" in
Pseudomerge) test "x$info" != "x$oinfo" ;;
esac
@@ -133,6 +153,9 @@ t-gdr-good () {
Upstream/SAME)
git diff --quiet $expect..$cid -- ':debian'
;;
+ MergedBreakwaters)
+ enparents=2
+ ;;
Anchor)
break
;;
@@ -151,37 +174,52 @@ t-gdr-good () {
*) fail 'unexpected parent' ;;
esac
+ case "$ctype" in
+ MergedBreakwaters)
+ local f
+ local parent_ix=0
+ for f in $parents; do
+ t-gdr-good-analyse $f breakwater \
+ $wsfx-mp$parent_ix
+ parent_ix=$(( $parent_ix + 1 ))
+ done
+ return
+ ;;
+ esac
+
done
}
t-some-changes () {
local token=$1
- local which=${2-dum}
+ local which=${2:-dum}
+ local fsuffix=$3
t-git-next-date
case "$which" in
*d*)
- echo >>debian/zorkmid "// debian $token"
- git add debian/zorkmid
- git commit -m "DEBIAN add zorkmid ($token)"
+ echo >>debian/zorkmid$fsuffix "// debian $token"
+ git add debian/zorkmid$fsuffix
+ git commit -m "DEBIAN add zorkmid$fsuffix ($token)"
;;
esac
case "$which" in
*u*)
- echo >>src.c "// upstream $token"
- git commit -a -m "UPSTREAM edit src.c ($token)"
+ echo >>src$fsuffix.c "// upstream $token"
+ git add src$fsuffix.c
+ git commit -m "UPSTREAM edit src$fsuffix.c ($token)"
;;
esac
case "$which" in
*m*)
- for f in debian/zorkmid src.c; do
+ for f in debian/zorkmid$fsuffix src$fsuffix.c; do
echo "// both! $token" >>$f
git add $f
done
- git commit -m "MIXED add both ($token)"
+ git commit -m "MIXED add both($fsuffix) ($token)"
;;
esac
@@ -191,11 +229,9 @@ t-some-changes () {
t-make-new-upstream-tarball () {
local uv=$1
git checkout make-upstream
- # leaves ust set to filename of orig tarball
echo "upstream $uv" >>docs/README
git commit -a -m "upstream $uv tarball"
- ust=example_$uv.orig.tar.gz
- git archive -o ../$ust --prefix=example-2.0/ make-upstream
+ t-make-orig example $uv make-upstream
}
t-nmu-upload-1 () {
@@ -289,3 +325,53 @@ t-nmu-reconciled-good () {
git checkout master
t-dgit -wgf quilt-fixup
}
+
+t-gdr-prep-new-upstream () {
+ uv=$1
+ t-git-next-date
+ git checkout make-upstream
+ git reset --hard upstream
+ t-make-new-upstream-tarball $uv
+ git push . make-upstream:upstream
+ git checkout master
+ t-git-next-date
+}
+
+t-gdr-gbp-import-core () {
+ p=example
+ t-worktree 1.1
+
+ cd example
+
+ : 'fake up some kind of upstream'
+ git checkout -b upstream quilt-tip
+ rm -rf debian
+ mkdir debian
+ echo junk >debian/rules
+ git add debian
+ git commit -m "an upstream retcon ($0)"
+ git tag v1.0
+}
+
+t-gdr-gbp-import-core-with-queue () {
+ t-gdr-gbp-import-core
+
+ : 'fake up that our quilt-tip was descended from upstream'
+ git checkout quilt-tip
+ git merge --no-edit -s ours upstream
+
+ : 'fake up that our quilt-tip had the patch queue in it'
+ git checkout patch-queue/quilt-tip
+ gbp pq export
+ git add debian/patches
+ git commit -m "patch queue update ($0)"
+
+ : 'make branch names more conventional'
+ git branch -D master
+ git branch -m quilt-tip master
+}
+
+t-gdr-made-patches () {
+ git log -n1 --'pretty=format:%B' \
+ | egrep '^\[git-debrebase'
+}
diff --git a/tests/run-all b/tests/run-all
index c930171..736e0fe 100755
--- a/tests/run-all
+++ b/tests/run-all
@@ -2,6 +2,9 @@
set -e
# convenience script for running the tests outside adt-run
# usage: tests/using-intree tests/run-all [-p|--progressive] [tests/tests/*]
+#
+# passing `:' as if it were tests/tests/something is
+# a no-op and therefore just means to (delete and) set up the tmpdir
set -o pipefail
@@ -17,13 +20,36 @@ done
ncpus=$(nproc || echo 1)
jcpus=-j$(( ncpus * 134 / 100 ))
-if [ $# != 0 ]; then
- set TESTSCRIPTS="$*"
+if [ "x$DGIT_TESTS_TMPDIR" != x ]; then
+ tmpdir="$PWD"
+ tmpdir="${tmpdir#/}"
+ tmpdir="${tmpdir//!/!#!}"
+ tmpdir="${tmpdir//\//!}"
+ tmpdir="$DGIT_TESTS_TMPDIR/$tmpdir"
+ rm -f tests/tmp
+ ln -ns -- "$tmpdir" tests/tmp
+else
+ tmpdir=tests/tmp
fi
-mkdir -p tests/tmp
+case "$DGIT_TESTS_PROGRESSIVE" in
+''|n)
+ rm -rf -- "$tmpdir"
+ ;;
+esac
+
+mkdir -p -- "$tmpdir"
+
+case "$1" in
+:)
+ shift
+ if [ $# = 0 ]; then exit 0; fi
+ ;;
+esac
-${DGIT_TESTS_PROGRESSIVE+:} rm -f tests/tmp/*.ok
+if [ $# != 0 ]; then
+ set TESTSCRIPTS="$*"
+fi
export DGIT_GNUPG_STUNT_ERRLOG=$( tty -s ||: )
diff --git a/tests/setup/gdr-convert-gbp b/tests/setup/gdr-convert-gbp
index a3baf2d..93ed4b6 100755
--- a/tests/setup/gdr-convert-gbp
+++ b/tests/setup/gdr-convert-gbp
@@ -15,28 +15,7 @@ not-gdr-processable () {
t-git-debrebase analyse | grep 'Unknown Unprocessable'
}
-p=example
-t-worktree 1.1
-
-cd example
-
-: 'fake up some kind of upstream'
-git checkout -b upstream quilt-tip
-rm -rf debian
-mkdir debian
-echo junk >debian/rules
-git add debian
-git commit -m "an upstream retcon ($0)"
-
-: 'fake up that our quilt-tip was descended from upstream'
-git checkout quilt-tip
-git merge --no-edit -s ours upstream
-
-: 'fake up that our quilt-tip had the patch queue in it'
-git checkout patch-queue/quilt-tip
-gbp pq export
-git add debian/patches
-git commit -m "patch queue update ($0)"
+t-gdr-gbp-import-core-with-queue
not-gdr-processable origin
@@ -44,10 +23,6 @@ not-gdr-processable origin
git branch make-upstream upstream
t-make-new-upstream-tarball 2.0
-: 'make branch names more conventional'
-git branch -D master
-git branch -m quilt-tip master
-
for b in \
quilt-tip-2 \
gitish-only \
@@ -67,6 +42,10 @@ not-gdr-processable merge
t-dch-commit -v 2.0-1 -m 'new upstream (did gbp import-orig)'
t-dch-commit-r
+echo garbage >debian/patches/garbage
+git add debian/patches/garbage
+git commit -m 'add garbage' debian/patches/garbage
+
$ifarchive t-archive-none $p
$ifarchive t-git-none
$ifarchive t-dgit -wgf --gbp push-source --new
@@ -84,8 +63,13 @@ git push --set-upstream origin master
t-expect-fail E:'identical in upstream files' \
t-git-debrebase -fupstream-has-debian convert-from-gbp upstream/2.0~
+t-expect-fail E:'Unused patch file garbage will be discarded' \
t-git-debrebase -fupstream-has-debian convert-from-gbp
+t-git-debrebase -fupstream-has-debian -funused-patches convert-from-gbp
+
+git branch converted-from-gbp
+
v=2.0-2
t-dch-commit -v $v -m 'switch to git-debrebase, no other changes'
t-dch-commit-r
diff --git a/tests/tests/build-modes b/tests/tests/build-modes
index c476ec8..58ec58d 100755
--- a/tests/tests/build-modes
+++ b/tests/tests/build-modes
@@ -3,6 +3,7 @@ set -e
. tests/lib
. $troot/lib-build-modes
+bm-buildproductsdir-nonworking
bm-prep
for act in \
@@ -14,21 +15,10 @@ for act in \
'build -F' \
'build -g' \
'build -G' \
- build-source \
; do
bm-guess-e-source-e-targets "$act"
-
- case $act in
- build-source)
- cleanmodes="$cleanmodes_all"
- ;;
- *)
- cleanmodes="$cleanmodes_default"
- ;;
- esac
-
+ cleanmodes="$cleanmodes_default"
real_act="$act"
-
bm-act-iterate
done
diff --git a/tests/tests/build-modes-long b/tests/tests/build-modes-long
new file mode 100755
index 0000000..20a2ce3
--- /dev/null
+++ b/tests/tests/build-modes-long
@@ -0,0 +1,36 @@
+#!/bin/bash
+set -e
+. tests/lib
+. $troot/lib-build-modes
+
+bm-prep
+
+for build in \
+ 'source' \
+ 'any' \
+ 'all' \
+ 'binary' \
+ 'full' \
+ 'source,any' \
+ 'all,any' \
+; do
+ act="build --build=$build"
+
+ case "$build" in
+ *source*|*full*) e_source=true ;;
+ *) e_source=false ;;
+ esac
+
+ case "$build" in
+ *binary*|*any*all*|*all*any*)
+ e_targets='build binary' ;;
+ *any*) e_targets='build-arch binary-arch' ;;
+ *all*) e_targets='build-indep binary-indep' ;;
+ esac
+
+ cleanmodes="$cleanmodes_default"
+ real_act="$act"
+ bm-act-iterate
+done
+
+t-ok
diff --git a/tests/tests/build-modes-source b/tests/tests/build-modes-source
new file mode 100755
index 0000000..480e573
--- /dev/null
+++ b/tests/tests/build-modes-source
@@ -0,0 +1,17 @@
+#!/bin/bash
+set -e
+. tests/lib
+. $troot/lib-build-modes
+
+bm-prep
+
+for act in \
+ build-source \
+; do
+ bm-guess-e-source-e-targets "$act"
+ cleanmodes="$cleanmodes_all"
+ real_act="$act"
+ bm-act-iterate
+done
+
+t-ok
diff --git a/tests/tests/gdr-diverge-nmu-dgit b/tests/tests/gdr-diverge-nmu-dgit
index 1d136a9..007c35b 100755
--- a/tests/tests/gdr-diverge-nmu-dgit
+++ b/tests/tests/gdr-diverge-nmu-dgit
@@ -42,7 +42,7 @@ t-nmu-causes-ff-fail
git checkout dgit/dgit/sid # detach
-t-expect-fail 'E:CONFLICT.*Commit Debian 3\.0 \(quilt\) metadata' \
+t-expect-fail 'E:CONFLICT.*Commit patch queue' \
git rebase master
git rebase --skip
diff --git a/tests/tests/gdr-edits b/tests/tests/gdr-edits
index 124de29..0bdf15f 100755
--- a/tests/tests/gdr-edits
+++ b/tests/tests/gdr-edits
@@ -38,4 +38,11 @@ t-refs-same-start
t-ref-same refs/heads/before-noop
t-ref-head
+git checkout -b some-new-branch
+t-some-changes 2nd-maintainer
+t-dgit quilt-fixup
+t-gdr-made-patches
+# NB quilt-fixup doesn't always manage to call gdr;
+# push does, since it will make the pseudomerge first
+
t-ok
diff --git a/tests/tests/gdr-fresh b/tests/tests/gdr-fresh
new file mode 100755
index 0000000..169181c
--- /dev/null
+++ b/tests/tests/gdr-fresh
@@ -0,0 +1,45 @@
+#!/bin/bash
+set -e
+autoimport=
+. tests/lib
+. $troot/lib-gdr
+
+t-dependencies GDR
+
+t-gdr-gbp-import-core
+
+t-git-next-date
+
+# leaves us on upstream
+git rm debian/rules
+git commit -m 'strip upstream rules'
+git tag -f v1.0
+
+git branch -m master master.old
+git checkout -b master
+
+t-git-next-date
+
+git checkout quilt-tip debian
+git commit -m 'initial debianisation'
+
+t-make-orig example 1.0
+
+dgit-quilt-fixup-uses-gdr () {
+ t-git-next-date
+
+ DGIT_TEST_DEBUG=-DD t-dgit quilt-fixup 2>&1 |tee ../fixup.out
+ grep '^branch_is_gdr .* YES$' ../fixup.out
+}
+
+dgit-quilt-fixup-uses-gdr
+
+git checkout --detach patch-queue/quilt-tip
+git rebase master
+git push . HEAD:master
+git checkout master
+
+dgit-quilt-fixup-uses-gdr
+t-gdr-made-patches
+
+t-ok
diff --git a/tests/tests/gdr-import-dgitview b/tests/tests/gdr-import-dgitview
index 3670deb..18d06f5 100755
--- a/tests/tests/gdr-import-dgitview
+++ b/tests/tests/gdr-import-dgitview
@@ -38,7 +38,8 @@ t-ref-head
t-git-debrebase --noop-ok convert-from-dgit-view
t-ref-head
-t-git-debrebase convert-from-dgit-view --always-convert-anyway
+t-git-debrebase -falready-converted convert-from-dgit-view \
+ --always-convert-anyway
t-expect-fail E:'ref varies' t-ref-head
t-gdr-good laundered
diff --git a/tests/tests/gdr-import-nostitch b/tests/tests/gdr-import-nostitch
new file mode 100755
index 0000000..c32b71e
--- /dev/null
+++ b/tests/tests/gdr-import-nostitch
@@ -0,0 +1,30 @@
+#!/bin/bash
+set -e
+autoimport=
+. tests/lib
+. $troot/lib-gdr
+
+t-dependencies GDR
+
+t-tstunt-parsechangelog
+
+t-gdr-gbp-import-core-with-queue
+
+git checkout master
+
+git tag -m synthetic debian/1.0-1
+
+echo '# comment' >>debian/patches/series
+git add debian/patches/series
+git commit -m 'add a comment'
+
+t-expect-fail F:-fseries-comments \
+t-git-debrebase -fupstream-has-debian -funused-patches convert-from-gbp
+
+t-git-debrebase -fupstream-has-debian -funused-patches -fseries-comments \
+ convert-from-gbp \
+ 2>&1 | tee ../convert-msg
+
+grep -e 'dgit --overwrite will be needed' ../convert-msg >/dev/null
+
+t-ok
diff --git a/tests/tests/gdr-makepatches7 b/tests/tests/gdr-makepatches7
index 52462d4..d6a5143 100755
--- a/tests/tests/gdr-makepatches7
+++ b/tests/tests/gdr-makepatches7
@@ -13,7 +13,7 @@ git checkout upstream/2.0
echo '*patch*' >>.gitignore
git commit -m nasty .gitignore
git tag v2.1
-GZIP=-1 git archive -o ../${p}_2.1.orig.tar.gz --prefix ${p}/ v2.1
+t-make-orig $p 2.1
git checkout master
t-git-debrebase new-upstream 2.1-1
diff --git a/tests/tests/gdr-merge b/tests/tests/gdr-merge
new file mode 100755
index 0000000..78b79e9
--- /dev/null
+++ b/tests/tests/gdr-merge
@@ -0,0 +1,65 @@
+#!/bin/bash
+set -e
+autoimport=
+. tests/lib
+
+t-dependencies GDR
+
+t-tstunt-parsechangelog
+t-setup-import gdr-convert-gbp-noarchive
+
+cd $p
+
+t-gdr-prep-new-upstream 2.1
+git tag v2.1 upstream
+
+t-some-changes before
+t-git-debrebase quick
+
+: ----- prepare LH branch -----
+
+git checkout -b other
+t-some-changes other '' -other
+
+echo 'other-conflict' >>debian/zorkmid
+git commit -m 'other-conflict' debian/zorkmid
+
+: ----- prepare RH branch -----
+
+git checkout master
+t-some-changes us-1 '' -us
+t-git-debrebase new-upstream 2.1
+t-some-changes us-2 '' -us
+echo 'us-conflict' >>debian/zorkmid
+git commit -m 'us-conflict' debian/zorkmid
+t-git-debrebase quick
+
+: ----- introduce a vanilla merge -----
+
+t-merge-conflicted-stripping-conflict-markers other debian/zorkmid
+
+t-expect-fail E:'general two-parent merge' \
+t-git-debrebase
+
+t_gdr_xopts+=' --experimental-merge-resolution'
+
+: ----- expect to be able to merge -----
+
+t-git-debrebase
+t-gdr-good laundered
+
+t-refs-same-start
+t-ref-head
+t-git-debrebase
+t-ref-head
+
+t-git-debrebase scrap
+
+t-some-changes after
+t-git-debrebase
+t-gdr-good laundered
+
+t-dgit quilt-fixup
+t-gdr-made-patches
+
+t-ok
diff --git a/tests/tests/gdr-merge-conflicts b/tests/tests/gdr-merge-conflicts
new file mode 100755
index 0000000..5a3f243
--- /dev/null
+++ b/tests/tests/gdr-merge-conflicts
@@ -0,0 +1,150 @@
+#!/bin/bash
+set -e
+autoimport=
+. tests/lib
+
+t-dependencies NO-DGIT GDR quilt
+
+t-tstunt-parsechangelog
+t-tstunt debchange
+t-setup-import gdr-convert-gbp-noarchive
+
+t_gdr_xopts+=' --experimental-merge-resolution'
+
+wreckage-before () {
+ junkref=refs/debrebase/wreckage/junk
+ git update-ref $junkref v2.1
+}
+
+wreckage-after () {
+ test '' = "$(t-git-get-ref-exact $junkref)"
+ git for-each-ref refs/debrebase/wreckage | egrep .
+}
+
+no-wreckage () {
+ if git for-each-ref refs/debrebase/wreckage | egrep .; then
+ fail wreckage
+ fi
+}
+
+: ----- prepare the baseline -----
+
+cd $p
+
+t-gdr-prep-new-upstream 2.1
+git tag v2.1 upstream
+
+t-some-changes before
+t-git-debrebase quick
+
+: ===== early failure in walk =====
+
+: ----- prepare other -----
+
+git checkout -b other
+t-some-changes other '' -other
+
+git branch other-before-new-upstream
+
+t-git-debrebase new-upstream 2.1
+t-git-next-date
+
+: ----- prepare master -----
+
+git checkout master
+t-git-debrebase new-upstream 2.1
+t-git-next-date
+
+git branch master-before-merge
+
+: ----- make the merge -----
+
+git merge --no-edit -s ours other
+
+# we have to do a little dance to make this not a pseudomerge
+t-git-next-date
+dch -a 'Merge, only conflict was in debian/changelog'
+t-dch-r-rune dch
+git commit -a --amend --no-edit
+
+wreckage-before
+
+t-expect-fail F:'divergent anchors' \
+t-git-debrebase
+
+wreckage-after
+
+: ===== late failure in apply =====
+
+git checkout other
+git reset --hard other-before-new-upstream
+
+echo other-upstream-confict >>docs/README
+git commit -m 'other-upstream-conflict' docs/README
+
+t-git-debrebase quick
+
+no-wreckage
+
+: ----- make the merge -----
+
+git checkout master
+git reset --hard master-before-merge
+
+t-merge-conflicted-stripping-conflict-markers other docs/README
+t-git-debrebase stitch
+
+: ----- expect failure -----
+
+wreckage-before
+
+t-expect-fail F:'docs/README' \
+t-git-debrebase
+
+wreckage-after
+
+: ===== resolve the conflict =====
+
+# omg
+
+quilt_faff_before () {
+ git checkout -b fix$1 debrebase/wreckage/merged-patchqueue
+ QUILT_PATCHES=debian/patches quilt push -a
+}
+quilt_faff_after () {
+ QUILT_PATCHES=debian/patches quilt refresh
+ git add debian/patches
+ git commit -m FIX
+ git reset --hard
+ git clean -xdff
+ t-git-debrebase record-resolved-merge
+ git checkout master
+}
+
+: ----- badly -----
+
+quilt_faff_before 1
+quilt_faff_after
+
+t-expect-fail E:'upstream files are not the same' \
+t-git-debrebase
+
+t-git-debrebase scrap
+no-wreckage
+
+: ----- well -----
+
+t-expect-fail F:'docs/README' \
+t-git-debrebase
+wreckage-after
+
+quilt_faff_before 2
+git checkout master docs/README
+git reset docs/README
+quilt_faff_after
+
+t-git-debrebase
+
+t-gdr-good laundered
+
+t-ok
diff --git a/tests/tests/gdr-newupstream b/tests/tests/gdr-newupstream
index ada5516..3039669 100755
--- a/tests/tests/gdr-newupstream
+++ b/tests/tests/gdr-newupstream
@@ -12,18 +12,7 @@ cd $p
: 'upstream hat'
-new-upstream () {
- uv=$1
- t-git-next-date
- git checkout make-upstream
- git reset --hard upstream
- t-make-new-upstream-tarball $uv
- git push . make-upstream:upstream
- git checkout master
- t-git-next-date
-}
-
-new-upstream 2.1
+t-gdr-prep-new-upstream 2.1
: 'maintainer hat'
@@ -37,8 +26,35 @@ t-git-debrebase new-upstream $v
git tag v2.1 upstream
+git branch before-new-upstream
+
+clog-check-1 () {
+ before=$1
+ date=$(git log --format=%aD -n1 debian/changelog)
+ date=$(date -R -d "$date")
+ git show $before:debian/changelog >../clog.before
+ m=" * Update to new upstream version ${v%-*}."
+ e="dgit test git user <dgit-test@debian.example.net> $date"
+}
+clog-check-2 () {
+ diff -u ../clog.expected debian/changelog
+}
+
+: ----- ordinary new upstream test -----
+
t-git-debrebase new-upstream ${v%-*}
+clog-check-1 before-new-upstream
+cat - <<END ../clog.before >../clog.expected
+example ($v) UNRELEASED; urgency=medium
+
+$m
+
+ -- $e
+
+END
+clog-check-2
+
t-gdr-good laundered
git reflog | egrep 'debrebase new-upstream.*checkout'
@@ -47,9 +63,9 @@ t-gdr-good stitched
git branch ordinary
-: 'with --anchor'
+: ----- 'with --anchor' -----
-git reset --hard startpoint
+git checkout -b with-anchor startpoint
t-git-debrebase analyse >../anal.anch
anchor=$(perl <../anal.anch -ne '
@@ -69,4 +85,25 @@ t-gdr-good stitched
git diff --quiet ordinary
+: ----- with pre-existing changes -----
+
+git checkout -b with-preexisting before-new-upstream
+
+t-dch-commit drivel-in-changelog
+git branch before-new-upstream-with-changes
+
+t-git-next-date
+
+t-git-debrebase new-upstream ${v%-*}
+
+clog-check-1 before-new-upstream-with-changes
+perl -pe <../clog.before >../clog.expected '
+ BEGIN { ($m,$e,@ARGV) = @ARGV; }
+ next unless 1..m/^ --/;
+ s/\([^()]+\)/('$v')/ if m/^example /;
+ $_ .= "$m\n" if m/^ \* drivel-in-changelog/;
+ $_ = " -- $e\n" if m/^ -- /;
+' "$m" "$e"
+clog-check-2
+
t-ok
diff --git a/tests/tests/gdr-subcommands b/tests/tests/gdr-subcommands
index b4d8f44..672ddc9 100755
--- a/tests/tests/gdr-subcommands
+++ b/tests/tests/gdr-subcommands
@@ -25,6 +25,11 @@ mix-it () {
t-git-next-date
}
+git checkout -b raw-converted-from-gbp converted-from-gbp
+test "$(git diff --stat HEAD^2)"
+git diff --exit-code --stat HEAD^1
+git reset --hard HEAD^1
+
git checkout -b stitched-laundered master
mix-it
t-git-debrebase quick
@@ -51,7 +56,8 @@ git show-ref
subcmd () {
local subcmd=$1
shift
- for startbranch in {stitched,unstitched}-{laundered,mixed}; do
+ for startbranch in {stitched,unstitched}-{laundered,mixed} \
+ raw-converted-from-gbp; do
work="work-$subcmd-$startbranch"
: "---------- $subcmd $startbranch ----------"
@@ -127,7 +133,7 @@ make_check () {
case "$1" in
[Nn]*)
t-refs-same-start
- t-refs-same refs/heads/$before refs/heads/$work
+ t-refs-same refs/heads/$before refs/heads/$peel
;;
U*)
t-refs-same-start
@@ -162,6 +168,7 @@ make_check () {
P*)
t-dgit -wgf --quilt=nofix quilt-fixup
git diff HEAD~ debian/patches | egrep .
+ t-gdr-made-patches
git diff --quiet HEAD~ -- ':.' ':!debian/patches'
git reset --hard HEAD~
;;
@@ -169,6 +176,11 @@ make_check () {
git diff --quiet HEAD refs/heads/$before -- ':.' ':!debian/patches'
t-gdr-good laundered
;;
+ A*)
+ t-refs-notexist \
+ refs/ffq-prev/heads/$work \
+ refs/debrebase-last/heads/$work
+ ;;
t*)
git diff --quiet HEAD refs/heads/$before
;;
@@ -177,7 +189,7 @@ make_check () {
;;
Z*)
t-refs-same-start
- t-refs-same refs/heads/$work
+ t-refs-same refs/heads/$peel
t-refs-same refs/heads/before-unstitch/$startbranch
t-ref-head
;;
@@ -191,20 +203,21 @@ Ec="F:No ongoing git-debrebase session"
Ep="F:Patch export produced patch amendments"
# input state:
-# stitched? st'd st'd unst'd unst'd
-# laundered? laund'd mixed laund'd mixed
+# stitched? st'd st'd unst'd unst'd convert
+# laundered? laund'd mixed laund'd mixed -from-gbp
#
# "mixed" means an out of order branch
# containing mixed commits and patch additions,
# but which needs even more patches
#
-subcmd '' Ult Ull Vlt Vl
-subcmd stitch Ns Nu Sltf Stf
-subcmd prepush Ns Nu Sltf Stf
-subcmd quick ns Sl Sltf Sl
-subcmd conclude "$Ec" "$Ec" Sltf Sl
-subcmd scrap Ns Ns Z Z
-subcmd make-patches sPft "$Ep" uPft "$Ep"
+subcmd '' Ult Ull Vlt Vl nU
+subcmd stitch Ns Nu Sltf Stf Ns
+subcmd prepush ns nu Sltf Stf ns
+subcmd quick ns Sl Sltf Sl n
+subcmd conclude "$Ec" "$Ec" Sltf Sl N
+subcmd scrap Ns Ns Z Z N
+subcmd make-patches sPft "$Ep" uPft "$Ep" Pn
+subcmd forget-was-ever-debrebase nA nA nA nA nA
#
# result codes, each one is a check:
# E:$pat } this is an error (must come first)
@@ -224,6 +237,7 @@ subcmd make-patches sPft "$Ep" uPft "$Ep"
# s result is stitched, debrebase-last exists and is unchanged
# S result is stitch just made, remaining letters apply to result~
# Z result is rewind to before changes made
+# A no ffq-prev or debrebase-last refs ("Amnesia")
#
# P result is add-patches, remaining letters apply to result~
#
diff --git a/tests/tests/gdr-unprocessable b/tests/tests/gdr-unprocessable
index 14d1e8e..5e86522 100755
--- a/tests/tests/gdr-unprocessable
+++ b/tests/tests/gdr-unprocessable
@@ -14,7 +14,7 @@ t-dgit setup-mergechangelogs
subcmd () {
cmd=("$@")
- branch merge 'complex merge'
+ branch merge 'general two-parent merge'
branch origin 'origin commit'
}
diff --git a/tests/tests/gdr-unprocessable-hints b/tests/tests/gdr-unprocessable-hints
new file mode 100755
index 0000000..710d7c9
--- /dev/null
+++ b/tests/tests/gdr-unprocessable-hints
@@ -0,0 +1,33 @@
+#!/bin/bash
+set -e
+. tests/lib
+
+t-dependencies GDR
+
+t-tstunt-parsechangelog
+t-setup-import gdr-convert-gbp
+
+cd $p
+
+b=gdr-unprocessable/origin
+
+git checkout $b
+t-expect-fail E:'maybe you needed git-debrebase convert-from' \
+t-git-debrebase quick
+
+git update-ref refs/ffq-prev/heads/$b master
+t-expect-fail E:'Consider git-debrebase scrap' \
+t-git-debrebase quick
+
+git update-ref -d refs/ffq-prev/heads/$b master
+git update-ref refs/debrebase-last/heads/$b HEAD
+t-expect-fail E:'Branch/history seems mangled' \
+t-git-debrebase quick
+test "$(grep 'git-debrebase scrap' ../t.output)" = ""
+
+git update-ref refs/debrebase-last/heads/$b master
+t-expect-fail E:'Branch/history mangled, and diverged' \
+t-git-debrebase quick
+test "$(grep 'git-debrebase scrap' ../t.output)" = ""
+
+t-ok
diff --git a/tests/tests/gitattributes b/tests/tests/gitattributes
index bdc61f8..9ebc216 100755
--- a/tests/tests/gitattributes
+++ b/tests/tests/gitattributes
@@ -44,7 +44,7 @@ badattr () {
done
}
-# xxx want to make each of these files into a quilt patch
+# todo: want to make each of these files into a quilt patch
t-git-config core.eol crlf
diff --git a/tests/tests/sbuild-gitish b/tests/tests/sbuild-gitish
index 7c0a30c..da90b6f 100755
--- a/tests/tests/sbuild-gitish
+++ b/tests/tests/sbuild-gitish
@@ -6,7 +6,6 @@ t-dependencies sbuild man-db
t-restrict x-dgit-schroot-build
t-tstunt-parsechangelog
-t-buildproductsdir-config
t-prep-newpackage example 1.1
diff --git a/tests/tests/test-list-uptodate b/tests/tests/test-list-uptodate
index 1e5f199..f28f404 100755
--- a/tests/tests/test-list-uptodate
+++ b/tests/tests/test-list-uptodate
@@ -1,7 +1,10 @@
#!/bin/bash
set -e
+autoimport=
. tests/lib
+t-dependencies NO-DEFAULT git
+
: "just verifies internal consistency of test suite"
cd $root
diff --git a/tests/tstunt/debchange b/tests/tstunt/debchange
new file mode 100755
index 0000000..452b8c9
--- /dev/null
+++ b/tests/tstunt/debchange
@@ -0,0 +1,17 @@
+#!/bin/sh
+set -e
+
+spec=$(date --rfc-3339=seconds -d @"${GIT_AUTHOR_DATE% *}")
+spec=${spec%+*}
+
+# The nonsense with TZ is to work around #907264
+tz_set=${TZ+set}
+if ! test "$tz_set"; then tz_undef=-u; fi
+
+set -x
+
+exec \
+env TZ=UTC \
+faketime -f "$spec" \
+env $tz_undef TZ${tz_set:+=}"$TZ" \
+"${DGIT_TEST_REAL_DEBCHANGE-debchange}" "$@"
diff --git a/tests/tstunt/dpkg-deb b/tests/tstunt/dpkg-deb
new file mode 100755
index 0000000..acaadf4
--- /dev/null
+++ b/tests/tstunt/dpkg-deb
@@ -0,0 +1,4 @@
+#!/bin/sh
+set -x
+exec \
+"${DGIT_TEST_REAL_DPKG_DEB}" -Znone "$@"
diff --git a/tests/worktrees/example_1.1.tar b/tests/worktrees/example_1.1.tar
index 6acd590..f58c41a 120000..100644
--- a/tests/worktrees/example_1.1.tar
+++ b/tests/worktrees/example_1.1.tar
Binary files differ