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;
}
# 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.
+ 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));
}
-# 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)
# 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 @earlier;
while (my $patch = <S>) {
chomp $patch or die $!;
+ $prereq{$patch} //= {};
foreach my $earlier (@earlier) {
$prereq{$patch}{$earlier}{$s}++ and die;
}
"[git-debrebase merge-innards patch-queue import:".
" $q->{SeriesTip}]"
];
+ printdebug "merge_series pec $pec ";
runcmd @git, qw(rm -q --cached), $seriesfile;
$pec = make_commit [ $pec ], [
"Drop series file from $s to avoid merge trouble",
"[git-debrebase merge-innards patch-queue prep:".
" $q->{SeriesTip}]"
];
+ printdebug "pec' $pec\n";
runcmd @git, qw(reset -q --hard), $pec;
$q->{MR}{PEC} = $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");
debugcmd '+', @mergecmd;
$!=0; $?=-1;
failedcmd @mergecmd;
}
+ printdebug "merge_series merge ok, series...\n";
# We need to construct a new series file
# Firstly, resolve prereq
- foreach my $f (keys %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;
}
- foreach my $g (keys %{ $prereq{$f} }) {
+ print DEBUG " keep\n" if $debuglevel;
+ foreach my $g (sort keys %{ $prereq{$f} }) {
my $gfp = $prereq{$f}{$g};
- next unless
- # want to keep it
- !!$gfp->{0} == !!$gfp->{1}
+ printdebug "merge_series prereq\t$f\t-> $g\t";
+ if (!!$gfp->{0} == !!$gfp->{1}
? $gfp->{0}
- : !$gfp->{base}
- ;
- delete $prereq{$f}{$g};
+ : !$gfp->{base}) {
+ print DEBUG "\tkeep\n" if $debuglevel;
+ } else {
+ print DEBUG "\tdrop\n" if $debuglevel;
+ delete $prereq{$f}{$g};
+ }
}
}
}
$best = $try;
}
+ printdebug "merge_series series next $best\n";
print NS "$best\n" or die $!;
delete $prereq{$best};
foreach my $gp (values %prereq) {
runcmd @git, qw(add), $seriesfile;
runcmd @git, qw(commit --quiet -m), 'Merged series';
+ printdebug "merge_series series gbp pq import\n";
runcmd qw(gbp pq import);
# 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 merge..patch-queue/merge));
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;
my $tree = cmdoutput @git, qw(write-tree);
$commit =~ s{^parent (\S+)$}{parent $build}m or confess;
OrigParents => \@orig_ps);
}
- if (@p > 2) {
- return $unknown->("octopus merge");
- }
if (@p == 2 and
$r->{Msg} =~ m{^\[git-debrebase merged-breakwater.*\]$}m) {
# xxx ^ metadata tag needs adding to (5)
if ($r->{Msg} =~ m{^\[(git-debrebase|dgit)[: ].*\]$}m) {
return $unknown->("unknown kind of merge from $1");
}
+ if (@p > 2) {
+ return $unknown->("octopus merge");
+ }
if (!$ENV{GIT_DEBREBASE_EXPERIMENTAL_MERGE}) {
return $unknown->("general two-parent merge");
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)")
}
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);