use Data::Dumper;
use Getopt::Long qw(:config posix_default gnu_compat bundling);
-use Debian::Dgit qw(:DEFAULT $wa);
+use Debian::Dgit qw(:DEFAULT :playground);
sub badusage ($) {
my ($m) = @_;
return ($`,$');
}
-sub D_DEB () { return 0x1; } # debian/ (not including debian/patches/)
-sub D_UPS () { return 0x2; } # upstream files
-sub D_PAT_ADD () { return 0x4; } # debian/patches/ extra patches at end
-sub D_PAT_OTH () { return 0x8; } # debian/patches other changes
+sub D_UPS () { 0x02; } # upstream files
+sub D_PAT_ADD () { 0x04; } # debian/patches/ extra patches at end
+sub D_PAT_OTH () { 0x08; } # debian/patches other changes
+sub D_DEB_CLOG () { 0x10; } # debian/ (not patches/ or changelog)
+sub D_DEB_OTH () { 0x20; } # debian/changelog
+sub DS_DEB () { D_DEB_CLOG | D_DEB_OTH; } # debian/ (not patches/)
+
+our $playprefix = 'debrebase';
+our $rd;
+our $workarea;
-our $rd = ".git/git-debrebase";
-our $ud = "$rd/work";
our @git = qw(git);
+sub in_workarea ($) {
+ my ($sub) = @_;
+ changedir $workarea;
+ my $r = eval { $sub->(); };
+ changedir $maindir;
+}
+
+sub fresh_workarea () {
+ $workarea = fresh_playground "$playprefix/work";
+ in_workarea sub { playtree_setup };
+}
+
sub get_differs ($$) {
my ($x,$y) = @_;
# This resembles quiltify_trees_differ, in dgit, a bit.
};
$rundiff->([qw(--name-only)], [], sub {
- $differs |= $_ eq 'debian' ? D_DEB : D_UPS;
+ $differs |= $_ eq 'debian' ? DS_DEB : D_UPS;
});
- if ($differs & D_DEB) {
- $differs &= ~D_DEB;
+ if ($differs & DS_DEB) {
+ $differs &= ~DS_DEB;
$rundiff->([qw(--name-only -r)], [qw(debian)], sub {
- $differs |= $_ eq m{^debian/patches/} ? D_PAT_OTH : D_DEB;
+ $differs |=
+ m{^debian/patches/} ? D_PAT_OTH :
+ $_ eq 'debian/changelog' ? D_DEB_CLOG :
+ D_DEB_OTH;
});
die "mysterious debian changes $x..$y"
- unless $differs & (D_PAT_OTH|D_DEB);
+ unless $differs & (D_PAT_OTH|DS_DEB);
}
if ($differs & D_PAT_OTH) {
#
# Types:
# Packaging
+# Changelog
# Upstream
# AddPatches
# Mixed
return $classify->(qw(AddPatches));
} elsif ($d & (D_PAT_ADD|D_PAT_OTH)) {
return $unknown->("edits debian/patches");
- } elsif ($d == D_DEB) {
+ } elsif ($d & DS_DEB and !($d & ~DS_DEB)) {
my ($ty,$dummy) = git_cat_file "$ph[0]:debian";
if ($ty eq 'tree') {
- return $classify->(qw(Packaging));
+ if ($d == D_DEB_CLOG) {
+ return $classify->(qw(Changelog));
+ } else {
+ return $classify->(qw(Packaging));
+ }
} elsif ($ty eq 'missing') {
return $classify->(qw(BreakwaterStart));
} else {
}
} elsif ($d == D_UPS) {
return $classify->(qw(Upstream));
- } elsif ($d == (D_DEB|D_UPS)) {
+ } elsif ($d & DS_DEB and $d & D_UPS and !($d & ~(DS_DEB|D_UPS))) {
return $classify->(qw(Mixed));
} elsif ($d == 0) {
return $unknown->("no changes");
# debian/) so debian breakwater branch should be 1st parent; that
# way also there's also an easy rune to look for the upstream
# patches (--topo-order).
- if (@p == 2 &&
- !$haspatches &&
- !$p[0]{IsOrigin} && # breakwater merge never starts with an origin
- !($p[0]{Differs} & ~D_DEB) &&
- !($p[1]{Differs} & ~D_UPS)) {
- return $classify->(qw(BreakwaterUpstreamMerge),
- OrigParents => [ $p[1] ]);
+
+ # The above tells us which way *we* will generate them. But we
+ # might encounter ad-hoc breakwater merges generated manually,
+ # which might be the other way around. In principle, in some odd
+ # situations, a breakwater merge might have two identical parents.
+ # In that case we guess which way round it is (ie, which parent
+ # has the upstream history). The order of the 2-iteration loop
+ # controls which guess we make.
+
+ foreach my $prevbrw (qw(0 1)) {
+ if (@p == 2 &&
+ !$haspatches &&
+ !$p[$prevbrw]{IsOrigin} && # breakwater never starts with an origin
+ !($p[$prevbrw]{Differs} & ~DS_DEB) &&
+ !($p[!$prevbrw]{Differs} & ~D_UPS)) {
+ return $classify->(qw(BreakwaterUpstreamMerge),
+ OrigParents => [ $p[!$prevbrw] ]);
+ }
+ # xxx multi-.orig upstreams
}
- # xxx multi-.orig upstreams
return $unknown->("complex merge");
}
$cur = $p0;
$rewrite_from_here->();
next;
- } elsif ($ty eq 'Packaging') {
+ } elsif ($ty eq 'Packaging' or $ty eq 'Changelog') {
push @brw_cl, $cl;
$cur = $p0;
next;
# Now we build it back up again
- workarea_fresh();
+ fresh_workarea();
my $rewriting = 0;
my $rm_tree_cached = sub {
my ($subdir) = @_;
- runcmd @git, qw(rm --quiet -rf --cached), $subdir;
+ runcmd @git, qw(rm --quiet -rf --cached --ignore-unmatch), $subdir;
};
my $read_tree_debian = sub {
my ($treeish) = @_;
STDOUT->error and die $!;
}
+sub cmd_downstream_rebase_launder_v0 () {
+ badusage "needs 1 argument, the baseline" unless @ARGV=0;
+ my ($base) = @ARGV;
+ $base = git_rev_parse $base;
+ my $old_head = get_head();
+ my $current = $old_head;
+ my $topmost_keep;
+ for (;;) {
+ if ($current eq $base) {
+ $topmust_keep //= $current;
+ print "$current BASE: stopping\n";
+ last;
+ }
+ my $cl = classify $current;
+ print " $current $cl->{Type}";
+ my $keep = 0;
+ my $p0 = $cl->{Parents}[0]{CommitId};
+ if ($cl->{Type} eq 'Pseudomerge') {
+ $current = $cl->{Contributor}{CommitId};
+ print " ^".($cl->{Contributor}{Ix}+1);
+ } elsif ($cl->{Type} eq 'AddPatches' or
+ $cl->{Type} eq 'Changelog') {
+ print " strip";
+ $current = $p0;
+ } else {
+ print " keep";
+ $current = $p0;
+ $keep = 1;
+ }
+ print "\n";
+ if ($keep) {
+ $topmost_keep //= $current;
+ } else {
+ die "to-be stripped changes not on top of the branch\n"
+ if $topmost_unstripped;
+ }
+ }
+ if ($topmost_keep eq $old_head) {
+ print "unchanged\n";
+ } else {
+ print "updating to $topmost_keep\n";
+ update_head $old_head, $topmost_keep, 'downstream-rebase-launder-v0';
+ }
+}
+
GetOptions("D+" => \$debuglevel) or die badusage "bad options\n";
initdebug('git-debrebase ');
enabledebug if $debuglevel;
my $toplevel = cmdoutput @git, qw(rev-parse --show-toplevel);
chdir $toplevel or die "chdir $toplevel: $!";
+$rd = fresh_playground "$playprefix/misc";
+
my $cmd = shift @ARGV;
my $cmdfn = $cmd;
$cmdfn =~ y/-/_/;