dpkg-mergechangelogs.pl 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. #!/usr/bin/perl
  2. # Copyright © 2009-2010 Raphaël Hertzog <hertzog@debian.org>
  3. #
  4. # This program is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation; either version 2 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. use warnings;
  17. use strict;
  18. use Dpkg;
  19. use Dpkg::Changelog::Debian;
  20. use Dpkg::ErrorHandling;
  21. use Dpkg::Gettext;
  22. use Dpkg::Version;
  23. use Getopt::Long;
  24. use Scalar::Util qw(blessed);
  25. textdomain("dpkg-dev");
  26. sub merge_entries($$$);
  27. sub merge_block($$$;&);
  28. sub merge_entry_item($$$$);
  29. sub merge_conflict($$);
  30. sub get_conflict_block($$);
  31. sub join_lines($);
  32. BEGIN {
  33. eval 'use Algorithm::Merge qw(merge);';
  34. if ($@) {
  35. eval q{
  36. sub merge {
  37. my ($o, $a, $b) = @_;
  38. return @$a if join("\n", @$a) eq join("\n", @$b);
  39. return get_conflict_block($a, $b);
  40. }
  41. };
  42. }
  43. }
  44. sub version {
  45. printf _g("Debian %s version %s.\n"), $progname, $version;
  46. printf "\n" . _g(
  47. "This is free software; see the GNU General Public License version 2 or
  48. later for copying conditions. There is NO warranty.
  49. ");
  50. }
  51. sub usage {
  52. printf(_g(
  53. "Usage: %s [<option> ...] <old> <new-a> <new-b> [<out>]
  54. Options:
  55. --merge-prereleases, -m merge pre-releases together, ignores everything
  56. after the last '~' in the version.
  57. --help, -h, -? show this help message.
  58. --version show the version.
  59. "), $progname);
  60. }
  61. my $merge_prereleases;
  62. unless (GetOptions('help|?' => sub { usage(); exit(0) },
  63. 'merge-prereleases|m' => \$merge_prereleases,
  64. 'version' => sub { version(); exit(0) })) {
  65. usage();
  66. exit(2);
  67. }
  68. my ($old, $new_a, $new_b, $out_file) = @ARGV;
  69. unless (defined $old and defined $new_a and defined $new_b and
  70. -e $old and -e $new_a and -e $new_b)
  71. {
  72. usage();
  73. exit(2);
  74. }
  75. my ($cho, $cha, $chb, $res);
  76. $cho = Dpkg::Changelog::Debian->new();
  77. $cho->load($old);
  78. $cha = Dpkg::Changelog::Debian->new();
  79. $cha->load($new_a);
  80. $chb = Dpkg::Changelog::Debian->new();
  81. $chb->load($new_b);
  82. my @o = reverse @$cho;
  83. my @a = reverse @$cha;
  84. my @b = reverse @$chb;
  85. my @result; # Lines to output
  86. my $exitcode = 0; # 1 if conflict encountered
  87. unless (merge_block($cho, $cha, $chb, sub {
  88. my $tail = $_[0]->get_unparsed_tail();
  89. chomp $tail if defined $tail;
  90. return $tail;
  91. }))
  92. {
  93. merge_conflict($cha->get_unparsed_tail(), $chb->get_unparsed_tail());
  94. }
  95. while (1) {
  96. my ($o, $a, $b) = get_items_to_merge();
  97. last unless defined $o or defined $a or defined $b;
  98. next if merge_block($o, $a, $b);
  99. # We only have the usually conflicting cases left
  100. if (defined $a and defined $b) {
  101. # Same entry, merge sub-items separately for a nicer result
  102. merge_entries($o, $a, $b);
  103. } else {
  104. # Non-existing on one side, changed on the other side
  105. merge_conflict($a, $b);
  106. }
  107. }
  108. if (defined($out_file) and $out_file ne "-") {
  109. open(OUT, ">", $out_file) || syserr(_g("cannot write %s"), $out_file);
  110. print OUT ((blessed $_) ? "$_" : "$_\n") foreach @result;
  111. close(OUT) || syserr(_g("cannot write %s"), $out_file);
  112. } else {
  113. print ((blessed $_) ? "$_" : "$_\n") foreach @result;
  114. }
  115. exit $exitcode;
  116. # Returns the next items to merge, all items returned correspond to the
  117. # same minimal version among the 3 possible next items (undef is returned
  118. # if the next item on the given changelog is skipped)
  119. sub get_items_to_merge {
  120. my @items = (shift @o, shift @a, shift @b);
  121. my @arrays = (\@o, \@a, \@b);
  122. my $minver;
  123. foreach my $i (0 .. 2) {
  124. if (defined $minver and defined $items[$i]) {
  125. my $cmp = compare_versions($minver, $items[$i]->get_version());
  126. if ($cmp > 0) {
  127. $minver = $items[$i]->get_version();
  128. foreach my $j (0 .. $i - 1) {
  129. unshift @{$arrays[$j]}, $items[$j];
  130. $items[$j] = undef;
  131. }
  132. } elsif ($cmp < 0) {
  133. unshift @{$arrays[$i]}, $items[$i];
  134. $items[$i] = undef;
  135. }
  136. } else {
  137. $minver = $items[$i]->get_version() if defined $items[$i];
  138. }
  139. }
  140. return @items;
  141. }
  142. # Compares the versions taking into account some oddities like the fact
  143. # that we want backport/volatile versions to sort higher than the version
  144. # on which they are based.
  145. sub compare_versions {
  146. my ($a, $b) = @_;
  147. return 0 if not defined $a and not defined $b;
  148. return 1 if not defined $b;
  149. return -1 if not defined $a;
  150. $a = $a->get_version() if ref($a) and $a->isa("Dpkg::Changelog::Entry");
  151. $b = $b->get_version() if ref($b) and $b->isa("Dpkg::Changelog::Entry");
  152. # Backport and volatile are not real prereleases
  153. $a =~ s/~(bpo|vola)/+$1/;
  154. $b =~ s/~(bpo|vola)/+$1/;
  155. if ($merge_prereleases) {
  156. $a =~ s/~[^~]*$//;
  157. $b =~ s/~[^~]*$//;
  158. }
  159. $a = Dpkg::Version->new($a);
  160. $b = Dpkg::Version->new($b);
  161. return $a <=> $b;
  162. }
  163. # Merge changelog entries smartly by merging individually the different
  164. # parts constituting an entry
  165. sub merge_entries($$$) {
  166. my ($o, $a, $b) = @_;
  167. # NOTE: Only $o can be undef
  168. # Merge the trailer line
  169. unless (merge_entry_item("blank_after_trailer", $o, $a, $b)) {
  170. unshift @result, "";
  171. }
  172. unless (merge_entry_item("trailer", $o, $a, $b)) {
  173. merge_conflict($a->get_part('trailer'), $b->get_part('trailer'));
  174. }
  175. # Merge the changes
  176. unless (merge_entry_item("blank_after_changes", $o, $a, $b)) {
  177. unshift @result, "";
  178. }
  179. my @merged = merge(defined $o ? $o->get_part('changes') : [],
  180. $a->get_part('changes'), $b->get_part('changes'),
  181. {
  182. CONFLICT => sub {
  183. $exitcode = 1;
  184. return get_conflict_block($_[0], $_[1]);
  185. }
  186. });
  187. unshift @result, @merged;
  188. # Merge the header line
  189. unless (merge_entry_item("blank_after_header", $o, $a, $b)) {
  190. unshift @result, "";
  191. }
  192. unless (merge_entry_item("header", $o, $a, $b)) {
  193. merge_conflict($a->get_part('header'), $b->get_part('header'));
  194. }
  195. }
  196. sub join_lines($) {
  197. my $array = shift;
  198. return join("\n", @$array) if ref($array) eq "ARRAY";
  199. return $array;
  200. }
  201. # Try to merge the obvious cases, return 1 on success and 0 on failure
  202. # O A B
  203. # - x x => x
  204. # o o b => b
  205. # - - b => b
  206. # o a o => a
  207. # - a - => a
  208. sub merge_block($$$;&) {
  209. my ($o, $a, $b, $preprocess) = @_;
  210. $preprocess = \&join_lines unless defined $preprocess;
  211. $o = &$preprocess($o) if defined($o);
  212. $a = &$preprocess($a) if defined($a);
  213. $b = &$preprocess($b) if defined($b);
  214. return 1 if not defined($a) and not defined($b);
  215. if (defined($a) and defined($b) and ($a eq $b)) {
  216. unshift @result, $a;
  217. } elsif ((defined($a) and defined($o) and ($a eq $o)) or
  218. (not defined($a) and not defined($o))) {
  219. unshift @result, $b if defined $b;
  220. } elsif ((defined($b) and defined($o) and ($b eq $o)) or
  221. (not defined($b) and not defined($o))) {
  222. unshift @result, $a if defined $a;
  223. } else {
  224. return 0;
  225. }
  226. return 1;
  227. }
  228. sub merge_entry_item($$$$) {
  229. my ($item, $o, $a, $b) = @_;
  230. if (blessed($o) and $o->isa('Dpkg::Changelog::Entry')) {
  231. $o = $o->get_part($item);
  232. } elsif (ref $o) {
  233. $o = $o->{$item};
  234. }
  235. if (blessed($a) and $a->isa('Dpkg::Changelog::Entry')) {
  236. $a = $a->get_part($item);
  237. } elsif (ref $a) {
  238. $a = $a->{$item};
  239. }
  240. if (blessed($b) and $b->isa('Dpkg::Changelog::Entry')) {
  241. $b = $b->get_part($item);
  242. } elsif (ref $b) {
  243. $b = $b->{$item};
  244. }
  245. return merge_block($o, $a, $b);
  246. }
  247. sub merge_conflict($$) {
  248. my ($a, $b) = @_;
  249. unshift @result, get_conflict_block($a, $b);
  250. $exitcode = 1;
  251. }
  252. sub get_conflict_block($$) {
  253. my ($a, $b) = @_;
  254. my (@a, @b);
  255. push @a, $a if defined $a;
  256. push @b, $b if defined $b;
  257. @a = @{$a} if ref($a) eq "ARRAY";
  258. @b = @{$b} if ref($b) eq "ARRAY";
  259. return ("<<<<<<<", @a, "=======", @b, ">>>>>>>");
  260. }