use File::FnMatch qw(:fnmatch);
use File::Copy;
+$debugcmd_when_debuglevel = 2;
+
our ($usage_message) = <<'END';
usages:
git-debrebase [<options>] [--|-i <git rebase options...>]
unless $differs & (D_PAT_ADD|D_PAT_OTH);
}
- printdebug sprintf "get_differs %s, %s = %#x\n", $x, $y, $differs;
+ printdebug sprintf "get_differs %s %s = %#x\n", $x, $y, $differs;
return $differs;
}
rm_subdir_cached 'debian/patches';
}
-sub read_tree_upstream ($;$) {
- my ($treeish, $keep_patches) = @_;
- my $save = cmdoutput @git, qw(write-tree --prefix=debian/);
+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', $save;
+ read_tree_subdir 'debian', $debian;
rm_subdir_cached 'debian/patches' unless $keep_patches;
};
}
-# xxx allow merge separately from laundering
+# xxx allow merge resolution separately from laundering, before git merge
#
-# xxx docs highlight forbidden things
-# xxx docs list allowable things ?
-# xxx docs explicitly forbid some rebase
+# xxx general gdr docs highlight forbidden things
+# xxx general gdr docs list allowable things ?
+# xxx general gdr docs explicitly forbid some rebase
#
# xxx provide a way for the user to help
# xxx (eg, provide wreckage provide way to continue)
my @earlier;
while (my $patch = <S>) {
chomp $patch or die $!;
+ $prereq{$patch} //= {};
foreach my $earlier (@earlier) {
$prereq{$patch}{$earlier}{$s}++ and die;
}
}
$result = $build;
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);
+ printdebug sprintf "merge_series done debug=%s\n",
+ git_rev_parse 'HEAD';
};
printdebug "merge_series returns $result\n";
return $result;
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;
+ }
+ return $best_anchor;
+}
+
+sub keycommits ($;$$$$$) {
+ my ($head, $furniture, $unclean, $trouble, $fatal, $claimed_bw) = @_;
# => ($anchor, $breakwater)
# $unclean->("unclean-$tagsfx", $msg, $cl)
# $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:
# if the answer is no longer wanted.
my ($anchor, $breakwater);
+ $breakwater = $head if $claimed_bw;
my $clogonly;
my $cl;
my $found_pm;
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};
} 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)")
$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}",
}
die "$ty ?";
} elsif ($ty eq 'VanillaMerge' or $ty eq 'MergedBreakwaters') {
- # xxx need to handle ffq if one side was unstitched
- # wait both of them may be!
+ # 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.
+ # xxx 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
push @brw_cl, {
%$cl,
SpecialMethod => 'MergeCreateMergedBreakwaters',
- $xmsg->('construct merged breakwater from vanilla merge'),
+ $xmsg->('constructed from vanilla merge',
+ ' merged-breakwater'),
};
push @upp_cl, {
%$cl,
$cl->{MergeInterchangeBaseInfo},
@{ $cl->{Parents} };
$last_anchor = $cl->{MergeBestAnchor};
- # xxx need to check the tree somehow
+ my $check_differs = get_differs $build, $cl->{CommitId};
+ # Breakwater changes which were in each side of the
+ # merge should be in MergeCreateMergedBreakwaters
+ # output. Upstream changes are exactly the result
+ # of merge_series. So the only difference should
+ # be potential laundry results.
+ $nomerge->(sprintf
+ "merge misresolved: tree is not the same (%s %s d.%#x)",
+ $cl->{CommitId}, $build, $check_differs)
+ if $check_differs & ~D_PAT_ADD;
print "Merge resolution successful.\n";
next;
} else {
};
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);