chiark / gitweb /
bb2d91a243e49cb5729e778954d5e19dbd2ceda4
[dgit.git] / git-debrebase
1 #!/usr/bin/perl -w
2 # git-debrebase
3 # Script helping make fast-forwarding histories while still rebasing
4 # upstream deltas when working on Debian packaging
5 #
6 # Copyright (C)2017 Ian Jackson
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the GNU General Public License
19 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21 use strict;
22 use Memoize;
23
24 use Debian::Dgit;
25
26 sub cfg ($) {
27     my ($k) = @_;
28     $/ = "\0";
29     my @cmd = qw(git config -z);
30     push @cmd, qw(--get-all) if wantarray;
31     push @cmd, $k;
32     my $out = cmdoutput @cmd;
33     return split /\0/, $out;
34 }
35
36 memoize('cfg');
37
38 # usage
39 #  git debrebase launder
40
41 sub get_commit ($) {
42     my ($objid) = @_;
43     my ($type,$data) = git_cat_file $objid;
44     die unless $type eq 'commit';
45     $data =~ m/(?<=\n)\n/;
46     return ($`,$');
47 }
48
49 sub D_DEB ()     { return 0x1; }
50 sub D_UPS ()     { return 0x2; }
51 sub D_PAT_ADD () { return 0x4; }
52 sub D_PAT_OTH () { return 0x8; }
53
54 sub classify ($) {
55     my ($objid) = @_;
56
57     my ($h,$m) = get_commit $objid;
58
59     my ($t) = $h =~ m/^tree (\w+)$/m or die $cur;
60     my (@ph) = $h =~ m/^parent (\w+)$/m;
61     my @p;
62
63     my $r = {
64         CommitId => $objid,
65         Hdr => $hdr,
66         Msg => $m,
67         Parents => \@p,
68     };
69
70     foreach my $ph (@ph) {
71         push @p, {
72             Ix => $#p,
73             CommitId => $ph,
74             Differs => (get_differs $t, $ph),
75         };
76     }
77
78     if (@p == 1) {
79         my $d = $r->{Parents}[0]{Differs};
80         if ($d == D_DPAT_ADD) {
81             return $classify->(qw(AddPatches));
82         } elsif ($d & (D_DPAT_ADD|D_DPAT_OTH)) {
83             return $unknown->("edits debian/patches");
84         } elsif ($d == D_DEB) {
85             return $classify->(qw(Packaging));
86         } elsif ($d == D_UPS) {
87             return $classify->(qw(Upstream));
88         } elsif ($d == D_DEB|D_UPS) {
89             return $classify->(qw(Mixed));
90         } elsif ($d == 0) {
91             return $unknown->("no changes");
92         } else {
93             confess "internal error $objid ?";
94         }
95     }
96     if (!@p) {
97         return $unknown->("origin commit");
98     }
99
100     my @identical = grep { !$_->{Differs} } @p;
101     if (@p == 2 && @identical == 1) {
102         my @overwritten = grep { $_->{Differs} } @p;
103         confess "internal error $objid ?" unless @overwritten==1;
104         return $classify->(qw(Pseudomerge),
105                            Overwritten => $overwritten[0],
106                            Contributor => $identical[0]);
107     }
108     if (@p == 2 && @identical == 2) {
109         my @bytime = nsort_by {
110             my ($ph,$pm) = get_commit $_->{CommitId};
111             $ph =~ m/^committer .* (\d+) [-+]\d+$/m or die "$_->{CommitId} ?";
112             $1;
113         } @p;
114         return $classify->(qw(Pseudomerge),
115                            Xtype => qw(Ambiguous),
116                            Overwritten => $bytime[0],
117                            Contributyor => $bytime[1]);
118     }
119     foreach my $p (@p) {
120         my ($type) = git_cat_file "$p^1";
121         $p->{IsOrigin} = $type eq 'missing';
122     }
123     if (!grep { !$_->{IsOrigin} } @p
124         && $m =~ m{^\[dgit import unpatched .*\]$}m) {
125         return $classify->(qw(DgitImport));
126     }
127
128     my ($stype, $series) = git_cat_file "$t:debian/patches/series";
129     my $haspatches = $stype ne 'missing' && $series =~ m/^\s*[^#\n\t ]/m;
130
131     # how to decide about l/r ordering of breakwater merges
132     # git --topo-order prefers to expand 2nd parent first
133     # easy rune to look for debian/ history so that should
134     # be 1st parent.
135     if (@p == 2 &&
136         !$haspatches &&
137         !$p[0]{IsOrigin} && # breakwater merge never starts with an origin
138         !($p[0]{Differs} & ~D_DEB) &&
139         !($p[1]{Differs} & ~D_UPS)) {
140         return $classify->(qw(BreakwaterUpstreamMerge),
141                            Upstream => $p[1]);
142     }
143
144     return $unknown->("complex merge");
145 }
146
147         ((git_cat_file "$t:debian/patches/series"
148         
149     
150     my @
151                            
152         
153
154             return $r;
155             $r->{Type} = '
156             $r->{Type} = '
157             return 
158             # changes on debian/patches, discard it
159             
160             $cur = $p[0];
161             next;
162         }
163         if ($d & DPAT) {
164             
165
166     ($r->{Tree},) = 
167     
168                     
169
170 sub launder () {
171     # go through commits backwards
172     # we generate two lists of commits to apply
173     my (@deb_cl, @ups_cl);
174     my $cur = git_rev_parse('HEAD');
175     for (;;) {
176 }   
177
178 if ($ARGV[0] eq 'launder') {
179     launder();
180 }
181
182 use Data::Dumper;
183 print Dumper(cfg('wombat.foo.bar'));
184
185
186
187
188
189  when starting must record original start (for ff)
190  and new rebase basis
191