6 # called as GIT_EXTERNAL_DIFF script
7 $old = parse_cooking
($ARGV[1]);
8 $new = parse_cooking
($ARGV[4]);
10 # called with old and new
11 $old = parse_cooking
($ARGV[0]);
12 $new = parse_cooking
($ARGV[1]);
14 compare_cooking
($old, $new);
16 ################################################################
18 use File
::Temp
qw(tempfile);
22 my ($a, $b, $force, $soft) = @_;
31 if (!defined $a->[0]) {
32 return map { "$plus$_\n" } map { split(/\n/) } @
{$b};
33 } elsif (!defined $b->[0]) {
34 return map { "$minus$_\n" } map { split(/\n/) } @
{$a};
35 } elsif (join('', @
$a) eq join('', @
$b)) {
37 return map { " $_\n" } map { split(/\n/) } @
{$a};
42 my ($ah, $aname) = tempfile
();
43 my ($bh, $bname) = tempfile
();
56 open(my $fh, "-|", 'diff', "-U$cnt", $aname, $bname);
63 unlink ($aname, $bname);
68 my ($cooking, $name, $desc) = @_;
69 my $section = $cooking->{SECTIONS
}[-1];
71 return if (!defined $name);
73 $desc =~ s/\s+\Z/\n/s;
76 IN_SECTION
=> $section,
80 $cooking->{TOPICS
}{$name} = $topic;
81 push @
{$cooking->{TOPIC_ORDER
}}, $name;
85 my ($cooking, @line) = @_;
87 while (@line && $line[-1] =~ /^\s*$/) {
92 if (!exists $cooking->{SECTIONS
}) {
93 $cooking->{SECTIONS
} = [];
94 $cooking->{TOPICS
} = {};
95 $cooking->{TOPIC_ORDER
} = [];
97 if (!exists $cooking->{HEADER
}) {
98 my $line = join('', @line);
99 $line =~ s/\A.*?\n\n//s;
100 $cooking->{HEADER
} = $line;
103 if (!exists $cooking->{GREETING
}) {
104 $cooking->{GREETING
} = join('', @line);
108 my ($section_name, $topic_name, $topic_desc);
110 if (!defined $section_name && /^\[(.*)\]$/) {
112 push @
{$cooking->{SECTIONS
}}, $section_name;
117 flush_topic
($cooking, $topic_name, $topic_desc);
118 $topic_name = $next_name;
123 flush_topic
($cooking, $topic_name, $topic_desc);
128 print $cooking->{HEADER
};
129 print "-" x
50, "\n";
130 print $cooking->{GREETING
};
131 for my $section_name (@
{$cooking->{SECTIONS
}}) {
132 print "\n", "-" x
50, "\n";
133 print "[$section_name]\n";
134 for my $topic_name (@
{$cooking->{TOPIC_ORDER
}}) {
135 $topic = $cooking->{TOPICS
}{$topic_name};
136 next if ($topic->{IN_SECTION
} ne $section_name);
137 print "\n", $topic->{DESC
};
144 my (%cooking, @current, $fh);
145 open $fh, "<", $filename
146 or die "cannot open $filename: $!";
149 parse_section
(\
%cooking, @current);
156 parse_section
(\
%cooking, @current);
164 print compare_them
($a, $b, 1, 1);
168 # otherwise they both have title.
169 $a = [map { "$_\n" } split(/\n/, join('', @
$a))];
170 $b = [map { "$_\n" } split(/\n/, join('', @
$b))];
171 my $atitle = shift @
$a;
172 my $btitle = shift @
$b;
173 print compare_them
([$atitle], [$btitle], 1);
176 while (@
$a && $a->[-1] !~ /^\s/) {
177 unshift @atail, pop @
$a;
179 while (@
$b && $b->[-1] !~ /^\s/) {
180 unshift @btail, pop @
$b;
182 print compare_them
($a, $b);
183 print compare_them
(\
@atail, \
@btail);
187 my ($fromto, $names, $topics) = @_;
190 for my $name (@
$names) {
191 my $t = $topics->{$name};
192 my ($a, $b, $in, $force);
193 if ($t->{OLD
} && $t->{NEW
}) {
194 $a = [$t->{OLD
}{DESC
}];
195 $b = [$t->{NEW
}{DESC
}];
196 if ($t->{OLD
}{IN_SECTION
} ne $t->{NEW
}{IN_SECTION
}) {
200 $in = "[$t->{NEW}{IN_SECTION}]";
202 } elsif ($t->{OLD
}) {
203 $a = [$t->{OLD
}{DESC
}];
205 $in = "Was in [$t->{OLD}{IN_SECTION}]";
208 $b = [$t->{NEW
}{DESC
}];
209 $in = "[$t->{NEW}{IN_SECTION}]";
211 next if (defined $a->[0] &&
213 $a->[0] eq $b->[0] && !$force);
215 if (!exists $where{$in}) {
219 push @
{$where{$in}}, [$a, $b];
223 for my $in (@where) {
224 my @bag = @
{$where{$in}};
225 if (defined $fromto && $fromto ne '') {
226 print "\n", '-' x
50, "\n$fromto\n";
229 print "\n$in\n" if ($in ne '');
233 compare_topics
($a, $b);
238 sub compare_cooking
{
239 my ($old, $new) = @_;
241 print compare_them
([$old->{HEADER
}], [$new->{HEADER
}]);
242 print compare_them
([$old->{GREETING
}], [$new->{GREETING
}]);
244 my (@sections, %sections, @topics, %topics, @fromto, %fromto);
246 for my $section_name (@
{$old->{SECTIONS
}}, @
{$new->{SECTIONS
}}) {
247 next if (exists $sections{$section_name});
248 $sections{$section_name} = scalar @sections;
249 push @sections, $section_name;
252 my $gone_class = "Gone topics";
253 my $born_class = "Born topics";
254 my $stay_class = "Other topics";
256 push @fromto, $born_class;
257 for my $topic_name (@
{$old->{TOPIC_ORDER
}}, @
{$new->{TOPIC_ORDER
}}) {
258 next if (exists $topics{$topic_name});
259 push @topics, $topic_name;
261 my $oldtopic = $old->{TOPICS
}{$topic_name};
262 my $newtopic = $new->{TOPICS
}{$topic_name};
263 $topics{$topic_name} = +{
267 my $oldsec = $oldtopic->{IN_SECTION
};
268 my $newsec = $newtopic->{IN_SECTION
};
269 if (defined $oldsec && defined $newsec) {
270 if ($oldsec ne $newsec) {
272 "Moved from [$oldsec] to [$newsec]";
273 if (!exists $fromto{$fromto}) {
274 $fromto{$fromto} = [];
275 push @fromto, $fromto;
277 push @
{$fromto{$fromto}}, $topic_name;
279 push @
{$fromto{$stay_class}}, $topic_name;
281 } elsif (defined $oldsec) {
282 push @
{$fromto{$gone_class}}, $topic_name;
284 push @
{$fromto{$born_class}}, $topic_name;
287 push @fromto, $stay_class;
288 push @fromto, $gone_class;
290 for my $fromto (@fromto) {
291 compare_class
($fromto, $fromto{$fromto}, \
%topics);