X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=dgit.git;a=blobdiff_plain;f=git-debrebase;h=96966adb3bb07b1c81a90da3427057d7ecc6c6f9;hp=0558e614828004d238ce1ad1e64963856514974f;hb=6a7d91dc81cbc8922bebc4930991902a9a00da79;hpb=cd91db80343bdae936479474deed7b70c1028a8e diff --git a/git-debrebase b/git-debrebase index 0558e614..96966adb 100755 --- a/git-debrebase +++ b/git-debrebase @@ -51,11 +51,12 @@ See git-debrebase(1), git-debrebase(5), dgit-maint-debrebase(7) (in dgit). END our ($opt_force, $opt_noop_ok, @opt_anchors); -our ($opt_defaultcmd_interactive, $opt_save_wreckage); +our ($opt_defaultcmd_interactive); our $us = qw(git-debrebase); -our $wrecknoteprefix = 'refs/debrebase-wreckage'; +our $wrecknoteprefix = 'refs/debrebase/wreckage'; +our $merge_cache_ref = 'refs/debrebase/merge-resolutions'; $|=1; @@ -128,8 +129,10 @@ 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 }; } @@ -170,6 +173,7 @@ sub run_deferred_updates ($) { confess 'dangerous internal error' unless all_snags_summarised(); + merge_wreckage_cleaning \@deferred_updates; run_ref_updates_now $mrest, \@deferred_updates; print $_, "\n" foreach @deferred_update_messages; @@ -378,30 +382,33 @@ sub gbp_pq_export ($$$) { # our own patch identification algorithm? # this is an alternative strategy -sub merge_failed ($$) { - my ($wrecknotes, $emsg) = @_; +sub merge_failed ($$;@) { + my ($wrecknotes, $emsg, @xmsgs) = @_; my @m; push @m, "Merge resolution failed: $emsg"; + push @m, @xmsgs; - $opt_save_wreckage //= do { - my $c = cfg 'git-debrebase.merge.save-wreckage', 1; - # ^ xxx documentation - $c && $c =~ m/^[1ty]/; - }; + changedir $maindir; - if ($opt_save_wreckage) { - my @updates; - merge_wreckage_cleaning \@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/* as requested."; + 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."; - # ^ xxx this section does not yet exist - fail join '', map { "$_\n" } @m; + + # 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 ($$$) { @@ -411,6 +418,19 @@ sub mwrecknote ($$$) { $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} @@ -435,11 +455,14 @@ sub merge_series ($$$;@) { # $prereq{}{} exists or not (even later) - my $result; + my $merged_pq; my $mwrecknote = sub { &mwrecknote($wrecknotes, @_); }; - local $workarea = fresh_playground "$playprefix/merge"; + my $attempt_cmd = sub { &merge_attempt_cmd($wrecknotes, @_); }; + + local $workarea; + fresh_workarea "merge"; my $seriesfile = "debian/patches/series"; in_workarea sub { playtree_setup(); @@ -498,11 +521,8 @@ sub merge_series ($$$;@) { 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; - if (system @mergecmd) { - failedcmd @mergecmd; - } + + $attempt_cmd->(@mergecmd); printdebug "merge_series merge ok, series...\n"; # We need to construct a new series file @@ -575,17 +595,30 @@ sub merge_series ($$$;@) { runcmd @git, qw(add), $seriesfile; runcmd @git, qw(commit --quiet -m), 'Merged patch queue form'; - $mwrecknote->('merged-patchqueue', git_rev_parse 'HEAD'); + $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 qw(gbp pq import); + runcmd @git, qw(checkout -q -b mergec), $merged_pq; + + merge_attempt_cmd($wrecknotes, 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)); + 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"; @@ -599,7 +632,7 @@ sub merge_series ($$$;@) { $build = cmdoutput @git, qw(hash-object -w -t commit ../mcommit); } $result = $build; - $mwrecknote->('merged-result', $result); + mwrecknote($wrecknotes, 'merged-result', $result); runcmd @git, qw(update-ref refs/heads/result), $result; @@ -609,7 +642,7 @@ sub merge_series ($$$;@) { 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->('merged-debug', $mdebug); + mwrecknote($wrecknotes, 'merged-debug', $mdebug); }; printdebug "merge_series returns $result\n"; return $result; @@ -1337,8 +1370,9 @@ sub walk ($;$$$) { my ($btip, $bbw, $banchor) = eval { walk $ib, 0, $report, $report_lprefix.' '; }; - $nomerge->("walking interchange branch merge base ($ibleaf): ". - $@) if length $@; + $nomerge->("walking interchange branch merge base ($ibleaf):\n". + $@) + if length $@; $mwrecknote->("mergebase-laundered", $btip); $mwrecknote->("mergebase-breakwater", $bbw); @@ -1448,6 +1482,7 @@ sub walk ($;$$$) { @parents = map { $_->{Breakwater} } @{ $cl->{Parents} }; } elsif ($method eq 'MergeMergeSeries') { print "Running merge resolution for $cl->{CommitId}...\n"; + $mwrecknote->('new-base', $build); $build = merge_series $build, $cl->{MergeWreckNotes}, $cl->{MergeInterchangeBaseInfo}, @@ -2170,19 +2205,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 ($) { @@ -2577,6 +2625,46 @@ END 'convert-from-dgit-view'; } +sub cmd_record_resolved_merge () { + badusage "record-resolved-merge takes no further arguments" if @ARGV; + # xxx 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; @@ -2634,7 +2722,6 @@ getoptions_main 'anchor=s' => \@opt_anchors, '--dgit=s' => \($dgit[0]), 'force!', - '--save-wreckage', \$opt_save_wreckage, '-i:s' => sub { my ($opt,$val) = @_; badusage "git-debrebase: no cuddling to -i for git-rebase"