Nico Golde:
[apps/madmutt.git] / svnlog2changelog.pl
1 #!/usr/bin/perl -w
2
3 # Purpose:
4 # summarize and re-format subversion's log output to plain text
5 #
6 # Written by Rocco Rutte <pdmef@cs.tu-berlin.de>
7 # for internal use with mutt-ng <http://mutt-ng.berlios.de/>.
8 #
9 # License: GPL
10 #
11 # Usage/Options:
12 #       -t              print only today's messages
13 #       -s YYYY-MM-DD   print only messages since (and including) date
14 #       -i string       use string for indentation
15 #       -m number       break lines at the latest at number columns
16 #       -h              help
17 #
18 # Note: lines matching ``^([^:]+):?$'' will be ignored if the first
19 # submatch in brackets is a known author; i.e. the logs
20 #
21 # | Foo Bar
22 # | added l33t c0d3
23 #
24 # and
25 #
26 # | Foo Bar:
27 # | added l33t c0d3
28 #
29 # will be interpreted as only 'added l33t c0d3' if 'Foo Bar' is known as
30 # an author. This does not count when grouping messages for authors,
31 # those lines are just skipped. This is hard-coded, see below.
32
33 use strict;
34 use POSIX;
35 use Getopt::Std;
36
37 # hard-coded configuration: this maps user to full names
38 my %commiters = (
39   "ak1"         => "Andreas Krennmair",
40   "nion"        => "Nico Golde",
41   "pdmef"       => "Rocco Rutte",
42   "dkg1"        => "Daniel K. Gebhart"
43 );
44
45 my %fmap = (
46   "filesA"      => "Added",
47   "filesM"      => "Modified",
48   "filesD"      => "Deleted"
49 );
50
51 # default config
52 my %options = ();
53 my $linemax = 70;
54 my $today = "";
55 my $since = "";
56 my $indent = "    ";
57
58 # some stuff we need
59 my $currev = 0;
60 my $lastrev = 0;
61 my @curlog = ();
62 my $curentry = "";
63 my $curauthor = "";
64 my $count = 0;
65 my %changes = ();
66
67 # nicely print log lines in itemized style with eye-candy indentation and
68 # somewhat smart line-breaking
69 sub niceline {
70   my ($text) = (@_);
71   my @lines = split (/\n/, $text);
72   for my $l (@lines) {
73     print "$indent$indent-";
74     my @words = split (/\ /, $l);
75     my $c = length ($indent);
76     for my $w (@words) {
77       if (length ($w) + $c > $linemax) {
78         print "\n$indent$indent  $w";
79         $c = length ($indent);
80       } else {
81         print " $w";
82       }
83       $c += length ($w) + 1;
84     }
85     print "\n";
86   }
87 }
88
89 sub usage {
90   print <<EOF
91 This is: svnlog2changelog.pl
92 written by Rocco Rutte <pdmef\@cs.tu-berlin.de>
93 for use with mutt-ng <http://mutt-ng.berlios.de/>
94
95 Usage:
96   Print help:
97   svn log | svnlog2changelog.pl -h
98
99   Print Subversion's log for today:
100   svn log | svnlog2changelog.pl -t [-i string]
101
102   Print Subversion's log since (and including) YYYY-MM-DD
103   svn log | svnlog2changelog.pl -s YYYY-MM-DD [-i string]
104 EOF
105   ;
106 }
107
108 sub isknown {
109   my ($name) = (@_);
110   for my $k (keys %commiters) {
111     if ($commiters{$k} eq $name) {
112       return (1);
113     }
114   }
115   return (0);
116 }
117
118 # get and process options
119 getopts ("tm:s:hi:", \%options);
120 if (defined $options{'t'}) {
121   $today = strftime ("%Y-%m-%d", localtime (time ()));
122 }
123 if (defined $options{'m'} and $options{'m'} =~ /^[0-9]{2,}$/) {
124   $linemax = $options{'m'};
125 }
126 if (defined $options{'s'} and $options{'s'} =~ /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/) {
127   $since = $options{'s'};
128 }
129 if (defined $options{'h'}) {
130   &usage ();
131   exit (0);
132 }
133 if (defined $options{'i'}) {
134   $indent = $options{'i'};
135 }
136
137 # parse log
138 while (<STDIN>) {
139   chomp;
140   if ($_ =~ /^r([0-9]+)/) {
141     $currev = $1;
142     @curlog = ();
143     $curauthor = "";
144     $count = 0;
145     my @items = split (/\ \|\ /, $_);
146     my @dateinfo = split (/\ /, $items[2]);
147     $curentry = $dateinfo[0];
148     ${$changes{$curentry}}{'author'} = $items[1];
149     $curauthor = $items[1];
150     # _keep_ latest rev. number for day
151     if (not defined ${$changes{$curentry}}{'rev'}) {
152       ${$changes{$curentry}}{'rev'} = substr ($items[0], 1);
153     }
154     # _keep_ latest commit time for day
155     if (not defined ${$changes{$curentry}}{'time'}) {
156       ${$changes{$curentry}}{'time'} = "$dateinfo[1] $dateinfo[2]";
157     }
158     $count++;
159     next;
160   }
161   if ($count > 0) {
162     # ignore noise
163     if (length ($_) == 0 or $_ =~ /^[-]+$/ or 
164         $_ =~ /^([^:]+):?$/ and &isknown ($1) or
165         $_ eq "Changed paths:") {
166       next;
167     }
168     if ($_ =~ /([A-Z]) \/((trunk|branches|tags).*)$/) {
169       my $what = $1;
170       my $target = $2;
171       ${${$changes{$curentry}}{"files$what"}}{$target} = 1;
172     } else {
173       # try to be smart and remove itemizations people make
174       my $clean = $_;
175       $clean =~ s/^[- \t*]*//;
176       if (length ($clean) > 0) {
177         # _pre_pend space for continued log and newline for
178         # new log lines for new revisions
179 #        if (defined ${${$changes{$curentry}}{'log'}}{$curauthor} and
180 #            length (${${$changes{$curentry}}{'log'}}{$curauthor}) > 0) {
181 #          if ($lastrev eq $currev) {
182 #            ${${$changes{$curentry}}{'log'}}{$curauthor} .= " ";
183 #          } else {
184 #            ${${$changes{$curentry}}{'log'}}{$curauthor} .= "\n";
185 #          }
186 #        }
187 #        ${${$changes{$curentry}}{'log'}}{$curauthor} .= "$clean";
188         ${${$changes{$curentry}}{'log'}}{$curauthor} .= "$clean\n";
189         $lastrev = $currev;
190       }
191     }
192   }
193 }
194
195 my $first = "";
196 my $first2 = "";
197
198 for my $k (sort { $b cmp $a } (keys (%changes))) {
199   # ignore noise
200   if (not defined %{${$changes{$k}}{'log'}} or
201       (length ($since) > 0 && length ($today) == 0 && ($k lt $since)) or
202       (length ($today) > 0 && ($k ne $today))) {
203     next;
204   }
205   # print first line with date, time and latest revision for current day
206   print "$first$k  ${$changes{$k}}{'time'}  ";
207   $first = "\n";
208   $first2 = "";
209   print "Latest Revision: ${$changes{$k}}{'rev'}\n\n";
210   # per author: print his name and an itemized list of his log msgs.
211   # with smart line-breaking and indentation
212   for my $a (keys %{${$changes{$k}}{'log'}}) {
213     print "$first2$indent";
214     if (defined $commiters{$a}) {
215       print $commiters{$a};
216     } else {
217       print $a;
218     }
219     print ":\n";
220     &niceline (${${$changes{$k}}{'log'}}{$a});
221     $first2 = "\n";
222   }
223   for my $a (keys %fmap) {
224     if (defined %{${$changes{$k}}{$a}}) {
225       print "$first$indent$fmap{$a} Files:\n";
226       my $fixme = join (", ", keys %{${$changes{$k}}{$a}});
227       &niceline ($fixme);
228     }
229   }
230 }