chiark / gitweb /
git-debrebase: merge: Clarify an internal commit message
[dgit.git] / git-debrebase
index 672551d5e921ea30ad7728952b2602e5e98c7299..792e145961edfc1c2920cfe5f69bec2a8a9d523c 100755 (executable)
@@ -263,11 +263,15 @@ sub read_tree_debian ($) {
     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;
 };
 
@@ -358,8 +362,8 @@ sub gbp_pq_export ($$$) {
 #  our own patch identification algorithm?
 #  this is an alternative strategy
 
-sub merge_series ($$;@) {
-    my ($newbase, $base_q, @input_qs) = @_;
+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)
@@ -391,8 +395,8 @@ sub merge_series ($$;@) {
        foreach my $q ($base_q, reverse @input_qs) {
            my $s = $q->{MR}{S};
            gbp_pq_export "p-$s", $q->{SeriesBase}, $q->{SeriesTip};
+           my @earlier;
            if (open S, $seriesfile) {
-               my @earlier;
                while (my $patch = <S>) {
                    chomp $patch or die $!;
                    $prereq{$patch} //= {};
@@ -420,6 +424,20 @@ sub merge_series ($$;@) {
                 "[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;
@@ -504,7 +522,7 @@ sub merge_series ($$;@) {
        }
 
        runcmd @git, qw(add), $seriesfile;
-       runcmd @git, qw(commit --quiet -m), 'Merged series';
+       runcmd @git, qw(commit --quiet -m), 'Merged patch queue form';
 
        printdebug "merge_series series gbp pq import\n";
        runcmd qw(gbp pq import);
@@ -518,7 +536,7 @@ sub merge_series ($$;@) {
        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;
+           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;
@@ -529,6 +547,13 @@ sub merge_series ($$;@) {
        }
        $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);
+       my $mdebug = git_rev_parse 'HEAD';
+       printdebug sprintf "merge_series done debug=%s\n", $mdebug;
     };
     printdebug "merge_series returns $result\n";
     return $result;
@@ -821,6 +846,8 @@ sub mergedbreakwaters_anchor ($) {
        $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;
 }
@@ -1002,6 +1029,11 @@ sub walk ($;$$$) {
        fail "something useful about failed merge attempt @_ xxx".Dumper($cl);
     };
 
+    my $mwrecknote = sub {
+       my ($reftail, $commitish) = @_;
+       $cl->{MergeWreckNotes}{$reftail} = $commitish;
+    };
+
     my $last_anchor;
 
     for (;;) {
@@ -1105,7 +1137,11 @@ sub walk ($;$$$) {
                return $bomb->();
            }
            die "$ty ?";
-       } elsif ($ty eq 'VanillaMerge' or $ty eq 'MergedBreakwaters') {
+       } 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
@@ -1141,15 +1177,14 @@ sub walk ($;$$$) {
 
            printdebug "*** MERGE\n";
 
-           # xxx avoid calling walk without nogenerate when
-           # we have a MergedBreakwater; instead call keycommits ?
-
            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->();
@@ -1160,6 +1195,17 @@ sub walk ($;$$$) {
                $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;
@@ -1168,10 +1214,12 @@ sub walk ($;$$$) {
                    " 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->('DivergentAnchor');
+                   $nomerge->('divergent anchors');
                } elsif ($p->{Anchor} eq $best_anchor) {
                    print $report " SameAnchor" if $report;
                } else {
@@ -1189,14 +1237,14 @@ sub walk ($;$$$) {
            }
 
            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);
            }
 
-           $nomerge->("alleged merged-breakwater is not a breakwater")
-               unless $ty eq 'VanillaMerge';
-
            my $bwb = cmdoutput @bwbcmd;
 
            # OK, now we have a breakwater base, but we need the merge
@@ -1207,17 +1255,39 @@ sub walk ($;$$$) {
            # breakwater merge base as an ancestor.
 
            my @ibs =
-               grep { is_fast_fwd $bwb, $_ }
                grep /./,
                split /\n/,
                cmdoutput @ibcmd;
-           my ($ib) = @ibs
-               or $nomerge->("no suitable interchange merge base");
+
+            @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) =
+           my ($btip, $bbw, $banchor) = eval {
                walk $ib, 0, $report, $report_lprefix.'  ';
+           };
+           $nomerge->("walking interchange branch merge base ($ibleaf): ".
+                      $@) if length $@;
+
+           $mwrecknote->("mergebase-laundered", $btip);
+           $mwrecknote->("mergebase-breakwater", $bbw);
+           $mwrecknote->("mergebase-anchor", $banchor);
 
            my $ibinfo = { SeriesTip => $btip,
                           SeriesBase => $bbw,
@@ -1226,6 +1296,7 @@ sub walk ($;$$$) {
                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})".
@@ -1233,7 +1304,6 @@ sub walk ($;$$$) {
                               (join ' ', map { $_->{Anchor} } @$ps).
                               ")");
 
-
            $cl->{MergeInterchangeBaseInfo} = $ibinfo;
            $cl->{MergeBestAnchor} = $best_anchor;
            push @brw_cl, {
@@ -1265,6 +1335,11 @@ sub walk ($;$$$) {
 
     my $rewriting = 0;
 
+    my $read_tree_upstream = sub {
+       my ($treeish) = @_;
+       read_tree_upstream $treeish, 0, $build;
+    };
+
     $#upp_cl = $upp_limit if defined $upp_limit;
  
     my $committer_authline = calculate_committer_authline();
@@ -1292,7 +1367,7 @@ sub walk ($;$$$) {
            if ($method eq 'Debian') {
                read_tree_debian($cltree);
            } elsif ($method eq 'Upstream') {
-               read_tree_upstream($cltree);
+               $read_tree_upstream->($cltree);
            } elsif ($method eq 'StartRewrite') {
                $rewriting = 1;
                next;
@@ -1305,32 +1380,45 @@ sub walk ($;$$$) {
                confess unless $rewriting;
                my $differs = (get_differs $build, $cltree);
                next unless $differs & D_UPS;
-               read_tree_upstream($cltree);
+               $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_upstream($cl->{MergeBestAnchor});
+               $read_tree_upstream->($cl->{MergeBestAnchor});
+               $read_tree_upstream->($cl->{MergeBestAnchor});
                read_tree_debian($cltree);
                @parents = map { $_->{Breakwater} } @{ $cl->{Parents} };
            } elsif ($method eq 'MergeMergeSeries') {
                print "Running merge resolution for $cl->{CommitId}...\n";
                $build = merge_series
-                   $build,
+                   $build, $cl->{MergeWreckNotes},
                    $cl->{MergeInterchangeBaseInfo},
                    @{ $cl->{Parents} };
                $last_anchor = $cl->{MergeBestAnchor};
-               my $check_differs = get_differs $build, $cl->{CommitId};
+
+               # 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 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;
+               # 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 {