X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=git-debrebase;h=60a27ad175ff476708464b4851e54c6bf41701b5;hb=632477167abfdf1d0e9f5bb28ffe6cdb74e2a4fa;hp=b27393802ba67e029d71473288b30fdb0180c1af;hpb=825270a6f7b5506c9bb5f444243da7c831408f54;p=dgit.git diff --git a/git-debrebase b/git-debrebase index b2739380..60a27ad1 100755 --- a/git-debrebase +++ b/git-debrebase @@ -117,6 +117,8 @@ use POSIX; use Data::Dumper; use Getopt::Long qw(:config posix_default gnu_compat bundling); +our ($opt_force); + sub badusage ($) { my ($m) = @_; die "bad usage: $m\n"; @@ -252,6 +254,17 @@ sub calculate_committer_authline () { return $&; } +sub rm_subdir_cached ($) { + my ($subdir) = @_; + runcmd @git, qw(rm --quiet -rf --cached --ignore-unmatch), $subdir; +} + +sub read_tree_subdir ($$) { + my ($subdir, $new_tree_object) = @_; + rm_subdir_cached $subdir; + runcmd @git, qw(read-tree), "--prefix=$subdir/"; +} + # classify returns an info hash like this # CommitId => $objid # Hdr => # commit headers, including 1 final newline @@ -635,14 +648,9 @@ sub walk ($;$$) { my $rewriting = 0; - my $rm_tree_cached = sub { - my ($subdir) = @_; - runcmd @git, qw(rm --quiet -rf --cached --ignore-unmatch), $subdir; - }; my $read_tree_debian = sub { my ($treeish) = @_; - $rm_tree_cached->(qw(debian)); - runcmd @git, qw(read-tree --prefix=debian/), "$treeish:debian"; + read_tree_subdir 'debian', "$treeish:debian"; }; my $read_tree_upstream = sub { my ($treeish) = @_; @@ -684,7 +692,7 @@ sub walk ($;$$) { next; } elsif ($method eq 'DgitImportDebianUpdate') { $read_tree_debian->($cltree); - $rm_tree_cached->(qw(debian/patches)); + rm_subdir_cached qw(debian/patches); } elsif ($method eq 'DgitImportUpstreamUpdate') { $read_tree_upstream->($cltree); push @parents, map { $_->{CommitId} } @{ $cl->{OrigParents} }; @@ -769,24 +777,45 @@ sub cmd_analyse () { sub cmd_new_upstream_v0 () { badusage - "need NEW-VERSION ORIG-COMMITISH [EXTRA-ORIG-NAME EXTRA-ORIG-COMMITISH...]" + "need NEW-VERSION UPS-COMMITISH [EXTRA-UPS-NAME EXTRA-UPS-COMMITISH...]" unless @ARGV % 2 == 0 and @ARGV >= 2; # tree should be clean and this is not checked # automatically and unconditionally launders before rebasing # if rebase --abort is used, laundering has still been done + my %pieces; + # parse args - low commitment - my $new_version = new Dpkg::Version scalar(shift @ARGV), 1; + my $new_version = (new Dpkg::Version scalar(shift @ARGV), 1); my $new_upstream_version = $new_version->version(); - my $new_orig_commitish = git_rev_parse shift @ARGV; - my @extra_origs; + + my $new_upstream = git_rev_parse shift @ARGV; + + my $piece = sub { + my ($n, @x) = @_; # may be '' + my $pc = $pieces{$n} //= { + Name => $n, + Desc => ($n ? "upstream piece $n" : "upstream (main piece"), + }; + while (my $k = shift @x) { $pc->{$k} = shift @x; } + }; + + my @newpieces; + my $newpiece = sub { + my ($n, @x) = @_; # may be '' + $pc = $piece->($n, @x, NewIx => (scalar @newpieces)); + push @newpieces, $pc; + }; + + $newpiece->('', + OldIx => 0, + New => $new_upstream, + }; while (@ARGV) { - my $xo = { - Name => shift @ARGV, - New => git_rev_parse shift @ARGV, - }; - die unless $xo->{Name} =~ m/^$extra_orig_namepart_re$/; - push @extra_origs, $xo; + my $n = shift @ARGV; + my $c = git_rev_parse shift @ARGV; + die unless $n =~ m/^$extra_orig_namepart_re$/; + $newpiece->($n, New => $c); } # now we need to investigate the branch this generates the @@ -795,87 +824,136 @@ sub cmd_new_upstream_v0 () { my ($laundered_tip,$breakwater) = walk $old; my $breakwater_cl = classify $breakwater; - my $old_orig_pi = $breakwater_cl->{OrigParents}[0]; + my $old_orig = parsecommit $breakwater_cl->{OrigParents}[0]{CommitId}; - fresh_workarea(); - in_workarea sub { - my $ff_still_ok = 1; + my $problems = 0; + my $problem = sub { + my ($msg) = @_; + $problems++; + print STDERR "preflight check failed: $msg\n"; + }; - my $ffnot = sub { - my ($msg) = @_; - $ff_still_ok = 0; - print STDERR "upstream not fast forward: $msg\n"; - }; + $piece->('', Old => $old_orig); + + if ($old_orig_ci =~ m{^\[git-debrebase }m) { + if ($old_orig_ci =~ + m{^\[git-debrebase new-upstream combine \.((?: $extra_orig_namepart_re)+)\]} + ) { + my @oldpieces = ('', split / /, $1); + my $parentix = -1 + scalar @{ $old_orig->{Parents} }; + foreach my $i (0..$#$oldpieces) { + my $n = $oldpieces[$i]; + $piece->($n, Old => $old_orig.'^'.$parentix); + } + } else { + $problem->("previous upstream $old_orig->{CommitId} is from". + " git-debrebase but not a \`new-upstream combine' commit"); + } + } - if (@extra_origs) { - # check fast forward, and make new combined-orig commit - my $old_orig_ci = parsecommit $old_orig_pi->{CommitId}; - my $n_old_origs = scalar @{ $old_orig_cp->{Parents} }; - @{ $n_old_origs } == @extra_origs+1 or - $ffnot->(sprintf - "previous breakwater upstream has %d parents". - " but new upstream has %d pieces, cannot check ff", - $n_old_origs, - (1 + scalar @extra_origs)); + foreach my $pc (values %pieces) { + if (!$pc->{Old}) { + $problem->("introducing upstream piece $pc->{Name}"); + } elsif (!$pc->{New}) { + $problem->("dropping upstream piece $pc->{Name}"); + } elsif (!is_fast_fwd $pc->{Old}, $pc->{New}) { + $problem->("not fast forward: $pc->{Name} $pc->{Old}..$pc->{New}"); } + } - my @upstream_merge_parents; + if ($problems) { + if ($opt_force) { + printf STDERR + "preflight check failures (%d) overriden by --force\n", + $problems; + } else { + fail sprintf + "preflight check failures (%d) (you could --force)", + $problems; + } + } - foreach my $piece_ix (0..$n_old_origs-1) { - my $prevpc = $breakwater.'^'.($old_orig_pi->{Ix} + 1); - if (@extra_origs) { - $prevpc .= '^'.($piece_ix + 1); - } - die unless $ git_rev_parse $prevpc; - my ($newpc,$newdesc,$pcname); - if (!$piece_ix) { - $newpc = $new_orig_commitish; - $newdesc = 'new main upstream piece'; - } else { - $newpc = $extra_origs[$piece_ix+1]{New}; - $pcname = $extra_origs[$piece_ix-1]{Name} - $newdesc = "new upstream extra piece \`$pcname"; - } - $ffwant->($prevpc, "previous upstream piece ($prevpc)", - $newpc, "newdesc ($newpc)"); + fresh_workarea(); + in_workarea sub { + my @upstream_merge_parents; - push @upstream_merge_parents, $newpc; + if (!$problems) { + push @upstream_merge_parents, $old_orig->{CommitId}; + } - my @cmd = @git, qw(read-tree); - if (defined $pcname) { - push @cmd, "-prefix=$pcname/"; - runcmd @git, qw(rm --cached -f --ignore-unmatch), $pcname; + foreach my $pc (@newpieces) { # always has '' first + if ($pc->{Name}) { + read_tree_subdir $pcname, $pc->{New]; + } else { + runcmd @git, qw(read-tree), $pc->{New}; } - push @cmd, $newpc; - runcmd @cmd; + push @upstream_merge_parents, $pc->{New}; } # index now contains the new upstream - if (!$ff_still_ok) { - die "upstreams not fast forward, stopping". - " (xxx should be an override option)"; - } - if (@extra_origs) { + if (@newpieces > 1) { # need to make the upstream subtree merge commit my $us_tree = cmdoutput @git, qw(write-tree); - my @cmd = @git, qw(commit-tree), $us_tree; - if ($ff_still_ok) { - push @cmd, qw(-p), - } else { - die 'do we want to make ff from previous upstream comb?"'; - } + my @cmd = (@git, qw(commit-tree), $us_tree); push @cmd, qw(-p), $_ foreach @upstream_merge_parents; push @cmd, qw(-m), "Combine upstreams for $new_upstream_version"; push @cmd, qw(-m), - "[git-debrebase combine-upstreams . ". - (join " ", map { $_->{Name} } @extra_upstreams)."]"; - my $combined = cmdoutput @cmd; + "[git-debrebase new-upstream combine . ". + (join " ", map { $_->{Name} } @newpieces[1..$#newpieces]). + "]"; + $new_upstream = cmdoutput @cmd; } - - my $us_txt = " - make_commit_te + # $new_upstream is either the single upstream commit, or the + # combined commit we just made. Either way it will be the + # "upstream" parent of the breakwater special merge. + + read_tree_subdir 'debian', "$breakwater:debian"; + + # index now contains the breakwater merge contents + + my $bw_tree = cmdoutput @git, qw(write_tree); + my @cmd = (@git, qw(commit-tree), $bw_tree); + push @cmd, qw(-p), $breakwater, qw(-p), $new_upstream; + push @cmd, qw(-m), "Update to upstream $new_upstream_version"; + push @cmd, qw(-m), + "[git-debrebase new-upstream breakwater $new_upstream_version]"; + my $new_bw = cmdoutput @git; + + # Now we have to add a changelog stanza so the Debian version + # is right. + + die if unlink "debian"; + die unless $!==ENOTEMPTY; + unlink "debian/changelog" or die $!; + open CN, ">", "debian/changelog" or die $!; + my $oldclog = git_cat_file ":debian/changelog"; + $oldclog =~ m/^($package_re) \(\S+\) / or + fail "cannot parse old changelog to get package name"; + my $p = $1; + print CN, < \$debuglevel) or die badusage "bad options\n"; +GetOptions("D+" => \$debuglevel, + 'force!') or die badusage "bad options\n"; initdebug('git-debrebase '); enabledebug if $debuglevel;