X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=dgit.git;a=blobdiff_plain;f=git-debrebase;h=55fbbd5defe135eafd83115bdc08284649184957;hp=2b38d4f85d2cdb89f95b3af03627d09b3516a5e8;hb=362391a2806abd5787a3094c34b7f5d8d838c49e;hpb=a87e08ae2e6861e87a386dd5c621e3da9c5d41d1 diff --git a/git-debrebase b/git-debrebase index 2b38d4f8..55fbbd5d 100755 --- a/git-debrebase +++ b/git-debrebase @@ -18,66 +18,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . - -# usages: -# -# git-debrebase [] new-upstream-v0 \ -# [ \ -# [ ...] \ -# [...]] -# -# git-debrebase [ --] [] -# git-debrebase [] analyse -# git-debrebase [] breakwater # prints breakwater tip only -# git-debrebase [] stitch [--prose=] -# git-debrebase [] launder-v0 # prints breakwater tip etc. -# git-debrebase [] downstream-rebase-launder-v0 # experimental -# -# git-debrebase [] convert-from-gbp [] -# git-debrebase [] convert-to-gbp - -# problems / outstanding questions: -# -# * dgit push with a `3.0 (quilt)' package means doing quilt -# fixup. Usually this involves recommitting the whole patch -# series, one at a time, with dpkg-source --commit. This is -# terribly terribly slow. (Maybe this should be fixed in dgit.) -# -# * dgit push usually needs to (re)make a pseudomerge. The "first" -# git-debrebase stripped out the previous pseudomerge and could -# have remembeed the HEAD. But it's not quite clear what history -# ought to be preserved and what should be discarded. For now -# the user will have to tell dgit --overwrite. -# -# To fix this, do we need a new push hook for dgit ? -# -# * Workflow is currently clumsy. Lots of spurious runes to type. -# There's not even a guide. -# -# * There are no tests. -# -# * new-upstream-v0 has a terrible UI. You end up with giant -# runic command lines. -# -# One consequence of the lack of richness it can need --force in -# fairly sensible situations and there is no way to tell it what -# you are really trying to do, other than just --force. There -# should be an interface with some default branch names. -# -# * There should be a standard convention for the version number, -# and unfinalised or not changelog, after new-upstream. -# -# * Handing of multi-orig dgit new-upstream .dsc imports is known to -# be broken. They may be not recognised, improperly converted, or -# their conversion may be unrecognised. -# -# * Docs need writing and updating. Even README.git-debrebase -# describes a design but may not reflect the implementation. -# -# * We need to develop a plausible model that works for derivatives, -# who probably want to maintain their stack on top of Debian's. -# downstream-rebase-launder-v0 may be a starting point? - use strict; use Debian::Dgit qw(:DEFAULT :playground); @@ -91,7 +31,7 @@ use Getopt::Long qw(:config posix_default gnu_compat bundling); use Dpkg::Version; use File::FnMatch qw(:fnmatch); -our ($opt_force, $opt_noop_ok); +our ($opt_force, $opt_noop_ok, @opt_anchors); our $us = qw(git-debrebase); @@ -356,6 +296,8 @@ sub any_fproblems () { # has additional entry in classification result # OrigParents = [ subset of Parents ] # singleton list # +# TreatAsAnchor +# # BreakwaterStart # # Unknown @@ -424,6 +366,10 @@ sub classify ($) { return $r; }; + if (grep { $_ eq $objid } @opt_anchors) { + return $classify->('TreatAsAnchor'); + } + my @identical = grep { !$_->{Differs} } @p; my ($stype, $series) = git_cat_file "$t:debian/patches/series"; my $haspatches = $stype ne 'missing' && $series =~ m/^\s*[^#\n\t ]/m; @@ -432,6 +378,23 @@ sub classify ($) { # multi-orig upstreams are represented with an anchor merge # from a single upstream commit which combines the orig tarballs + # Every anchor tagged this way must be a merge. + # We are relying on the + # [git-debrebase anchor: ...] + # commit message annotation in "declare" anchor merges (which + # do not have any upstream changes), to distinguish those + # anchor merges from ordinary pseudomerges (which we might + # just try to strip). + # + # However, the user is going to be doing git-rebase a lot. We + # really don't want them to rewrite an anchor commit. + # git-rebase trips up on merges, so that is a useful safety + # catch. + # + # BreakwaterStart commits are also anchors in the terminology + # of git-debrebase(5), but they are untagged (and always + # manually generated). + my $badanchor = sub { $unknown->("git-debrebase \`anchor' but @_"); }; @p == 2 or return $badanchor->("has other than two parents"); $haspatches and return $badanchor->("contains debian/patches"); @@ -531,12 +494,19 @@ sub classify ($) { return $unknown->("complex merge"); } -sub breakwater_of ($) { - my ($head) = @_; # must be laundered +sub breakwater_of ($;$) { + my ($head, $unclean_fproblem_tag) = @_; + # $head should be laundered; if not, $unclean_fproblem_tag controls: + # if falseish, calls fail; otherwise, calls fproblem and returns undef my $breakwater; my $unclean = sub { my ($why) = @_; - fail "branch needs laundering (run git-debrebase): $why"; + my $m = "branch needs laundering (run git-debrebase): $why"; + fail $m unless $unclean_fproblem_tag; + fproblem $unclean_fproblem_tag, $m; + $breakwater = undef; + no warnings qw(exiting); + last; }; for (;;) { my $cl = classify $head; @@ -545,6 +515,7 @@ sub breakwater_of ($) { $ty eq 'Changelog') { $breakwater //= $head; } elsif ($ty eq 'Anchor' or + $ty eq 'TreatAsAnchor' or $ty eq 'BreakwaterStart') { $breakwater //= $head; last; @@ -677,7 +648,7 @@ sub walk ($;$$) { $rewrite_from_here->(\@upp_cl); $cur = $contrib; next; - } elsif ($ty eq 'Anchor') { + } elsif ($ty eq 'Anchor' or $ty eq 'TreatAsAnchor') { $last_anchor = $cur; $build_start->("Anchor", $cur); } elsif ($ty eq 'DgitImportUnpatched') { @@ -913,7 +884,7 @@ sub cmd_analyse () { } sub ffq_prev_branchinfo () { - # => ('status', "message", [$current, $ffq_prev]) + # => ('status', "message", [$current, $ffq_prev, $drlast]) # 'status' may be # branch message is undef # weird-symref } no $current, @@ -923,8 +894,9 @@ sub ffq_prev_branchinfo () { return ('weird-symref', 'HEAD symref is not to refs/') unless $current =~ m{^refs/}; my $ffq_prev = "refs/$ffq_refprefix/$'"; + my $drlast = "refs/$gdrlast_refprefix/$'"; printdebug "ffq_prev_branchinfo branch current $current\n"; - return ('branch', undef, $current, $ffq_prev); + return ('branch', undef, $current, $ffq_prev, $drlast); } sub record_ffq_prev_deferred () { @@ -939,7 +911,8 @@ sub record_ffq_prev_deferred () { # if "deferred", will have added something about that to # @deferred_update_messages, and also maybe printed (already) # some messages about ff checks - my ($status, $message, $current, $ffq_prev) = ffq_prev_branchinfo(); + my ($status, $message, $current, $ffq_prev, $drlast) + = ffq_prev_branchinfo(); return ($status, $message) unless $status eq 'branch'; my $currentval = get_head(); @@ -1006,6 +979,7 @@ sub record_ffq_prev_deferred () { fproblems_maybe_bail(); push @deferred_updates, "update $ffq_prev $currentval $git_null_obj"; + push @deferred_updates, "delete $drlast"; push @deferred_update_messages, "Recorded current head for preservation"; return ('deferred', undef); } @@ -1074,13 +1048,17 @@ sub cmd_new_upstream_v0 () { my $old_bw_cl = classify $old_bw; my $old_anchor_cl = classify $old_anchor; - confess unless $old_anchor_cl->{OrigParents}; - my $old_upstream = parsecommit - $old_anchor_cl->{OrigParents}[0]{CommitId}; - - $piece->('', Old => $old_upstream->{CommitId}); + my $old_upstream; + if (!$old_anchor_cl->{OrigParents}) { + fproblem 'anchor-treated', + 'old anchor is recognised due to --anchor, cannot check upstream'; + } else { + $old_upstream = parsecommit + $old_anchor_cl->{OrigParents}[0]{CommitId}; + $piece->('', Old => $old_upstream->{CommitId}); + } - if ($old_upstream->{Msg} =~ m{^\[git-debrebase }m) { + if ($old_upstream && $old_upstream->{Msg} =~ m{^\[git-debrebase }m) { if ($old_upstream->{Msg} =~ m{^\[git-debrebase upstream-combine \.((?: $extra_orig_namepart_re)+)\:.*\]$}m ) { @@ -1098,7 +1076,9 @@ sub cmd_new_upstream_v0 () { } foreach my $pc (values %pieces) { - if (!$pc->{Old}) { + if (!$old_upstream) { + # we have complained already + } elsif (!$pc->{Old}) { fproblem 'upstream-new-piece', "introducing upstream piece \`$pc->{Name}'"; } elsif (!$pc->{New}) { @@ -1221,7 +1201,8 @@ sub cmd_stitch () { my $prose = ''; GetOptions('prose=s', \$prose) or die badusage("bad options to stitch"); badusage "no arguments allowed" if @ARGV; - my ($status, $message, $current, $ffq_prev) = ffq_prev_branchinfo(); + my ($status, $message, $current, $ffq_prev, $drlast) + = ffq_prev_branchinfo(); if ($status ne 'branch') { fproblem $status, "could not check ffq-prev: $message"; fproblems_maybe_bail(); @@ -1229,16 +1210,21 @@ sub cmd_stitch () { my $prev = $ffq_prev && git_get_ref $ffq_prev; if (!$prev) { fail "No ffq-prev to stitch." unless $opt_noop_ok; + return; } + my $old_head = get_head(); + + breakwater_of $old_head, 'unclean-stitch'; + push @deferred_updates, "delete $ffq_prev $prev"; - my $old_head = get_head(); if (is_fast_fwd $old_head, $prev) { my $differs = get_differs $old_head, $prev; unless ($differs & ~D_PAT_ADD) { # 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 $drlast $prev $git_null_obj"; update_head_checkout $old_head, $prev, "stitch (fast forward)"; return; } @@ -1248,6 +1234,7 @@ sub cmd_stitch () { 'Declare fast forward / record previous work', "[git-debrebase pseudomerge: stitch$prose]", ]; + push @deferred_updates, "update $drlast $new_head $git_null_obj"; update_head $old_head, $new_head, "stitch"; } @@ -1323,7 +1310,7 @@ sub cmd_convert_from_gbp () { sub cmd_convert_to_gbp () { badusage "no arguments allowed" if @ARGV; my $head = get_head(); - my $ffq = (ffq_prev_branchinfo())[3]; + my (undef, undef, undef, $ffq, $drlast) = ffq_prev_branchinfo(); my $bw = breakwater_of $head; fresh_workarea(); my $out; @@ -1339,6 +1326,7 @@ sub cmd_convert_to_gbp () { }; if (defined $ffq) { push @deferred_updates, "delete $ffq"; + push @deferred_updates, "delete $drlast"; } update_head_checkout $head, $out, "convert to gbp (v0)"; print < \$debuglevel, 'noop-ok', => \$opt_noop_ok, 'f=s' => \@fproblem_force_opts, + 'anchor=s' => \@opt_anchors, 'force!') or die badusage "bad options\n"; initdebug('git-debrebase '); enabledebug if $debuglevel; @@ -1409,6 +1398,8 @@ chdir $toplevel or die "chdir $toplevel: $!"; $rd = fresh_playground "$playprefix/misc"; +@opt_anchors = map { git_rev_parse $_ } @opt_anchors; + if (!@ARGV || $ARGV[0] =~ m{^-}) { defaultcmd_rebase(); } else {