3 # Copyright © 2009-2010 Raphaël Hertzog <hertzog@debian.org>
4 # Copyright © 2012 Guillem Jover <guillem@debian.org>
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <https://www.gnu.org/licenses/>.
22 use Scalar
::Util
qw(blessed);
23 use Getopt
::Long
qw(:config posix_default bundling_values no_ignorecase);
26 use Dpkg
::Changelog
::Debian
;
27 use Dpkg
::ErrorHandling
;
30 use Dpkg
::Vendor
qw(run_vendor_hook);
32 textdomain
('dpkg-dev');
34 sub merge_entries
($$$);
35 sub merge_block
($$$;&);
36 sub merge_entry_item
($$$$);
37 sub merge_conflict
($$);
38 sub get_conflict_block
($$);
43 use Algorithm::Merge qw(merge);
48 return @
$a if join("\n", @
$a) eq join("\n", @
$b);
49 return get_conflict_block
($a, $b);
55 printf g_
("Debian %s version %s.\n"), $Dpkg::PROGNAME
, $Dpkg::PROGVERSION
;
58 'This is free software; see the GNU General Public License version 2 or
59 later for copying conditions. There is NO warranty.
65 "Usage: %s [<option>...] <old> <new-a> <new-b> [<out>]
68 -m, --merge-prereleases merge pre-releases together, ignores everything
69 after the last '~' in the version.
70 --merge-unreleased merge UNRELEASED entries together, ignoring their
72 -?, --help show this help message.
73 --version show the version.
77 my $merge_prereleases;
81 'help|?' => sub { usage
(); exit(0) },
82 'version' => sub { version
(); exit(0) },
83 'merge-prereleases|m' => \
$merge_prereleases,
84 'merge-unreleased' => \
$merge_unreleased,
88 local $SIG{__WARN__
} = sub { usageerr
($_[0]) };
89 GetOptions
(@options_spec);
92 my $backport_version_regex = run_vendor_hook
('backport-version-regex');
94 my ($old, $new_a, $new_b, $out_file) = @ARGV;
95 unless (defined $old and defined $new_a and defined $new_b)
97 usageerr
(g_
('needs at least three arguments'));
99 unless (-e
$old and -e
$new_a and -e
$new_b)
101 usageerr
(g_
('file arguments need to exist'));
104 my ($cho, $cha, $chb);
105 $cho = Dpkg
::Changelog
::Debian
->new();
107 $cha = Dpkg
::Changelog
::Debian
->new();
109 $chb = Dpkg
::Changelog
::Debian
->new();
112 my @o = reverse @
$cho;
113 my @a = reverse @
$cha;
114 my @b = reverse @
$chb;
116 my @result; # Lines to output
117 my $exitcode = 0; # 1 if conflict encountered
119 unless (merge_block
($cho, $cha, $chb, sub {
121 my $tail = $changes->get_unparsed_tail();
122 chomp $tail if defined $tail;
126 merge_conflict
($cha->get_unparsed_tail(), $chb->get_unparsed_tail());
130 my ($o, $a, $b) = get_items_to_merge
();
131 last unless defined $o or defined $a or defined $b;
132 next if merge_block
($o, $a, $b);
133 # We only have the usually conflicting cases left
134 if (defined $a and defined $b) {
135 # Same entry, merge sub-items separately for a nicer result
136 merge_entries
($o, $a, $b);
138 # Non-existing on one side, changed on the other side
139 merge_conflict
($a, $b);
143 if (defined($out_file) and $out_file ne '-') {
144 open(my $out_fh, '>', $out_file)
145 or syserr
(g_
('cannot write %s'), $out_file);
146 print { $out_fh } ((blessed
$_) ?
"$_" : "$_\n") foreach @result;
147 close($out_fh) or syserr
(g_
('cannot write %s'), $out_file);
149 print ((blessed
$_) ?
"$_" : "$_\n") foreach @result;
154 # Returns the next items to merge, all items returned correspond to the
155 # same minimal version among the 3 possible next items (undef is returned
156 # if the next item on the given changelog is skipped)
157 sub get_items_to_merge
{
158 my @items = (shift @o, shift @a, shift @b);
159 my @arrays = (\
@o, \
@a, \
@b);
161 foreach my $i (0 .. 2) {
162 if (defined $minitem and defined $items[$i]) {
163 my $cmp = compare_versions
($minitem, $items[$i]);
165 $minitem = $items[$i];
166 foreach my $j (0 .. $i - 1) {
167 unshift @
{$arrays[$j]}, $items[$j];
171 unshift @
{$arrays[$i]}, $items[$i];
175 $minitem = $items[$i] if defined $items[$i];
181 # Compares the versions taking into account some oddities like the fact
182 # that we want backport versions to sort higher than the version
183 # on which they are based.
184 sub compare_versions
{
187 return 0 if not defined $a and not defined $b;
188 return 1 if not defined $b;
189 return -1 if not defined $a;
191 my ($av, $bv) = ($a, $b);
193 $av = $a->get_version() if ref $a and $a->isa('Dpkg::Changelog::Entry');
194 $bv = $b->get_version() if ref $b and $b->isa('Dpkg::Changelog::Entry');
196 if ($merge_unreleased) {
197 return 0 if $a->get_distributions() eq 'UNRELEASED' and
198 $b->get_distributions() eq 'UNRELEASED';
200 # Backports are not real prereleases.
201 if (defined $backport_version_regex) {
202 $a =~ s/$backport_version_regex/+$1/;
203 $b =~ s/$backport_version_regex/+$1/;
205 if ($merge_prereleases) {
209 $av = Dpkg
::Version
->new($av);
210 $bv = Dpkg
::Version
->new($bv);
214 # Merge changelog entries smartly by merging individually the different
215 # parts constituting an entry
216 sub merge_entries
($$$) {
217 my ($o, $a, $b) = @_;
218 # NOTE: Only $o can be undef
220 # Merge the trailer line
221 unless (merge_entry_item
('blank_after_trailer', $o, $a, $b)) {
224 unless (merge_entry_item
('trailer', $o, $a, $b)) {
225 merge_conflict
($a->get_part('trailer'), $b->get_part('trailer'));
229 unless (merge_entry_item
('blank_after_changes', $o, $a, $b)) {
232 my @merged = merge
(defined $o ?
$o->get_part('changes') : [],
233 $a->get_part('changes'), $b->get_part('changes'),
238 return get_conflict_block
($ca, $cb);
241 unshift @result, @merged;
243 # Merge the header line
244 unless (merge_entry_item
('blank_after_header', $o, $a, $b)) {
247 unless (merge_entry_item
('header', $o, $a, $b)) {
248 merge_conflict
($a->get_part('header'), $b->get_part('header'));
254 return join("\n", @
$array) if ref($array) eq 'ARRAY';
258 # Try to merge the obvious cases, return 1 on success and 0 on failure
265 sub merge_block
($$$;&) {
266 my ($o, $a, $b, $preprocess) = @_;
267 $preprocess //= \
&join_lines
;
268 $o = $preprocess->($o) if defined $o;
269 $a = $preprocess->($a) if defined $a;
270 $b = $preprocess->($b) if defined $b;
271 return 1 if not defined($a) and not defined($b);
272 if (defined($a) and defined($b) and ($a eq $b)) {
274 } elsif ((defined($a) and defined($o) and ($a eq $o)) or
275 (not defined($a) and not defined($o))) {
276 unshift @result, $b if defined $b;
277 } elsif ((defined($b) and defined($o) and ($b eq $o)) or
278 (not defined($b) and not defined($o))) {
279 unshift @result, $a if defined $a;
286 sub merge_entry_item
($$$$) {
287 my ($item, $o, $a, $b) = @_;
288 if (blessed
($o) and $o->isa('Dpkg::Changelog::Entry')) {
289 $o = $o->get_part($item);
293 if (blessed
($a) and $a->isa('Dpkg::Changelog::Entry')) {
294 $a = $a->get_part($item);
298 if (blessed
($b) and $b->isa('Dpkg::Changelog::Entry')) {
299 $b = $b->get_part($item);
303 return merge_block
($o, $a, $b);
306 sub merge_conflict
($$) {
308 unshift @result, get_conflict_block
($a, $b);
312 sub get_conflict_block
($$) {
315 push @a, $a if defined $a;
316 push @b, $b if defined $b;
317 @a = @
{$a} if ref($a) eq 'ARRAY';
318 @b = @
{$b} if ref($b) eq 'ARRAY';
319 return ('<<<<<<<', @a, '=======', @b, '>>>>>>>');