1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
|
#!CVSGUI1.0 --selection --name "Build ChangeLog"
# A WinCVS macro for generating a GNU style ChangeLog file.
#
# Please send diffs to David Gravereaux <davygrvy@pobox.com>
# if you can improve this :)
#
# 2002.03.01 -- Denis Ballant added revision numbers and branch names
# near each filename. <Denis.Ballant@cediti.be>
#
# 2000.11.07 -- Stephane Matamontero <dev1.gemodek@t-online.de> added
# better wordwrapping code in [Cvs2CL::RewriteIt].
#
# RCS: @(#) $Id: cvs2cl.tcl,v 1.10 2002/03/19 13:02:05 jerzyk Exp $
namespace eval ::Cvs2CL {
variable db ;# our database array
array set db [list]
variable usermap ;# user translations from $CVSROOT/CVSROOT/users
array set usermap [list]
variable outputList [list] ;# the "structure" we write output from
variable sorter [list] ;# the pre-sort helper list
variable CLog_fname "ChangeLog" ;# the output filename we want to use
variable rawLog ;# All the log in one string before parsing
variable major [lindex [split $::tcl_version .] 0]
variable minor [lindex [split $::tcl_version .] 1]
}
proc ::Cvs2CL::Init {} {
variable CLog_fname
variable rawLog
variable usermap
switch -regexp [string tolower [info nameofexecutable]] {
^(.*)wincvs.exe$ -
^(.*)maccvs$ -
^(.*)gcvs$ {
set cvsCmd [list cvs -Q log]
# uncomment this stuff when the replace works in [Cvs2CL::ReWriteIt]
#
# if {![catch {set fOldLog [open $CLog_fname r]}]} {
# cvsout "Checking date range of current $CLog_fname...\n"
# # the first "word" of the first line is a date string.
# # we ask cvs for logs greater than this old date
# lappend cvsCmd -d ">[join [split [lindex [split [gets $fOldLog] { }] 0] /] -]"
# close $fOldLog
# }
cvsout "Downloading the log...\n"
set rawLog [eval $cvsCmd]
# TODO: fill the usermap array here
}
^(.*)tclsh -
^(.*)wish {
# debugging under a tcl shell
set cvsCmd [list cvs -Q log]
# puts "Checking date range..."
# if {![catch {set fOldLog [open $CLog_fname r]}]} {
# # the first "word" of the first line is a date string.
# lappend cvsCmd -d "[lindex [split [gets $fOldLog] { }] 0]>"
# close $fOldLog
# }
# puts $cvsCmd
if {$argc != 2} { puts stderr "gimme a file to open!" ; exit }
set flog [open [lindex $argv 1] r]
fconfigure $flog -encoding iso8859-1 -translation auto
# bring the file into Tcl in one big SLAM!
# this of-course assumes one character==one byte
seek $flog 0 end
set theEnd [tell $flog]
seek $flog 0 start
fconfigure $flog -buffersize $theEnd
puts "Downloading the log..."
set rawLog [read $flog $theEnd]
close $flog
proc ::cvsout {text} {puts -nonewline $text}
}
}
}
proc ::Cvs2CL::cmpDateScan {a b} {
variable major
variable minor
if {$major < 8 || ($major == 8 && $minor < 3)} {
# ISO8601 date formats can't be scanned by tcl8.2, bummer...
# works in 8.3, though...
#
# clock scan wants mm/dd/yyyy, so convert it from yyyy-mm-dd.
#
set al [split $a -]
set a [lreplace $a 0 0 [list "[lindex $al 1]/[lindex $al 2]/[lindex $al 0]"]]
set bl [split $b -]
set b [lreplace $b 0 0 [list "[lindex $bl 1]/[lindex $bl 2]/[lindex $bl 0]"]]
}
return [expr {[clock scan $a -gmt 1] - [clock scan $b -gmt 1]}]
}
proc ::Cvs2CL::cmpTime {a b} {
if {[set sort [cmpDateScan [lindex $a 0] [lindex $b 0]]] == 0} {
# dates are identical so sort using the id
return [expr {[lindex $a 1] - [lindex $b 1]}]
}
return $sort
}
proc ::Cvs2CL::cmpDate {a b} {
set dateA [lindex [split $a ,] 0]
set dateB [lindex [split $b ,] 0]
return [cmpDateScan $dateA $dateB]
}
proc ::Cvs2CL::cmpInteger {a b} {
return [expr {$a - $b}]
}
proc ::Cvs2CL::ParseIt {} {
variable db
variable sorter
variable rawLog
variable CLog_fname
array set db {}
set sorter {}
set id 0
# make each line a list element
if {[regexp -nocase {^(.*)maccvs$} [info nameofexecutable]]} {
#### BUG: MacCvs's cvs Tcl command returns lines with a \r <eol>.
#### BUG: This is a big bug and needs to be repaired.
set rawLogList [split $rawLog \r]
} else {
set rawLogList [split $rawLog \n]
}
# count the number of lines
set lines [llength $rawLogList]
for {set a 0} {$a < $lines} {incr a} {
set line [lindex $rawLogList $a]
# The log for a certain file and version starts with
# Working file: <filename>
# The is our tag for a new entry into our database. First we just
# store <filename> in fname
if {[regexp {^Working file: ([^,]+)} $line null fname]} {
# we don't care about ourselves
if {![string compare $CLog_fname $fname]} {
# skip all the way forward to the next
while {$a < $lines} {
if {[regexp {^======} [lindex $rawLogList $a]]} {break} {incr a}
}
}
set branchnames(x) x
unset branchnames
continue
}
if {[regexp {^revision } $line]} {
set rev [lindex $line 1]
# If part of a branch, have the branch name appear along with the revision number
regexp {(.*)\.[0-9]+$} $rev --> rootrev
if [info exists branchnames($rootrev)] {
set rev "\[$branchnames($rootrev)\] $rev"
}
}
if {[regexp {^symbolic names:} $line]} {
# interpret the list of symbolic names
# to get the branches names
while {[incr a] < $lines} {
set line [lindex $rawLogList $a]
if {![regexp {^\t([^:]+): ([0-9\.]+)} $line --> branchname version]} break
if [regexp {(.*)\.0\.([0-9]+)$} $version --> branchhead branchid] {
# cvsout "$branchname - $branchhead.$branchid\n"
set branchnames($branchhead.$branchid) $branchname
}
}
}
# A line like
# date: <date> <time> ...
# follows soon. Everthing after that up to a line with all `='s in
# it is stored.
if {[regexp {^date:} $line]} {
set date [join [split [lindex $line 1] /] -]
set author [string trimright [lindex $line 4] {;}]
set text {}
# skip over branches line if any
if {[regexp {^branches:} [lindex $rawLogList [expr {$a+1}]]]} {
incr a
}
# extract the message
while {[incr a] < $lines} {
set line [lindex $rawLogList $a]
if {[regexp {^------} $line] || [regexp {^======} $line]} break
append text "$line\n"
}
set text [string trimright $text \n]
if {![string compare $text "Initial revision"]} {
# we don't log the initial import branch announcement
continue
}
if {![string compare $text "no message"]} {
# no message means exactly that. So ignore it.
continue
}
set db($id,fname) $fname
set db($id,date) $date
set db($id,revision) $rev
set db($id,text) $text
set db($id,author) $author
lappend sorter [list $date $id]
incr id
}
}
}
proc ::Cvs2CL::SortIt {} {
variable outputList
variable sorter
variable db
array set dates [list]
array set datesAuthors [list]
# first, do a rough sort along date
set preSort [lsort -command cmpTime -decreasing $sorter]
# next, we seperate it by date.
foreach element $preSort {
lappend dates([lindex $element 0]) [lindex $element 1]
}
# next, sub-divide each date by the commit author
foreach date [array names dates] {
# sort the id's in decending numerical order
set dates($date) [lsort -command cmpInteger -decreasing $dates($date)]
foreach id $dates($date) {
lappend datesAuthors($date,$db($id,author)) $id
}
}
# foreach author on a specific date, organize by log message and build the
# applies-to list then store it, repeat 'till done.
foreach DApair [lsort -command cmpDate -decreasing [array names datesAuthors]] {
if {[array exist commitArray]} {unset commitArray}
set entry [list]
lappend entry $db([lindex $datesAuthors($DApair) 0],date)
lappend entry $db([lindex $datesAuthors($DApair) 0],author)
foreach id $datesAuthors($DApair) {
lappend commitArray($db($id,text)) "$db($id,fname) $db($id,revision)"
}
# alphabetize 'em
foreach commitMsg [array names commitArray] {
set commitArray($commitMsg) [lsort $commitArray($commitMsg)]
}
lappend entry [array get commitArray]
lappend outputList $entry
}
}
proc ::Cvs2CL::RewriteIt {} {
variable usermap
variable outputList
variable CLog_fname
# if {[file exist $CLog_fname]} {
# file rename $CLog_fname $CLog_fname.old
# }
set fCLog [open $CLog_fname w]
fconfigure $fCLog -encoding iso8859-1
foreach outputElement $outputList {
# write the date
puts -nonewline $fCLog "[lindex $outputElement 0] "
# write the author of this commit. translate to an email, if listed
# in the usermap array we got from CVSROOT/users
if {[info exist usermap([lindex $outputElement 1])]} {
puts $fCLog $usermap([lindex $outputElement 1])
} else {
puts $fCLog [lindex $outputElement 1]
}
if {[array exist commits]} {unset commits}
array set commits [lindex $outputElement 2]
foreach msg [array names commits] {
# write the applies-to files
foreach applyto $commits($msg) {
puts $fCLog "\t* $applyto:"
}
# check if the message has line seperators
if {[llength [set msg_split [split $msg "\n"]]] > 1} {
# must already be formatted.. just append some tabs to each line
foreach i $msg_split {puts $fCLog "\t\t$i"}
puts $fCLog ""
} else {
# check to make sure this single line entry isn't too long
if {[string length $msg] > 64} {
# wordwrap it to look nicer than one big long line
set start 0
set next 0
set previous 0
set not_done true
while {$not_done} {
set start [string wordstart $msg [expr {$previous + 1}]]
while {($next - $start) < 64 && $not_done} {
set previous $next
set next [string wordend $msg [expr {$previous + 1}]]
if {$next == [string length $msg]} {
set previous end
set not_done false
}
}
puts $fCLog "\t\t[string range $msg $start [expr {$previous == "end" ? "end" : $previous - 1}]]"
}
puts $fCLog ""
} else {
puts $fCLog "\t\t$msg\n"
}
}
}
}
close $fCLog
# if {[file exist $CLog_fname.old]} {
# file delete -force $CLog_fname.old
# }
}
Cvs2CL::Init
cvsout "Parsing output...\n"
Cvs2CL::ParseIt
cvsout "Sorting entries...\n"
Cvs2CL::SortIt
cvsout "Rewriting ChangeLog...\n"
Cvs2CL::RewriteIt
cvsout "Done!\n"
|