--- cvsspam-0.2.12/svn_cvsspam.rb 2005-07-11 18:53:29.000000000 +0300 +++ cvsspam-svn/svn_cvsspam.rb 2008-08-07 17:27:52.632725455 +0300 @@ -18,7 +18,7 @@ # to your cvssppam.conf -$version = "0.2.11" +$version = "0.2.12" $maxSubjectLength = 200 @@ -339,8 +339,11 @@ # gets the Repository object for the first component of the given path def Repository.get(name) - name =~ /^[^\/]+/ - name = $& + # Leading './' is ignored (for peeps who have done 'cvs checkout .') + # Trailing '/' ensures no match for files in root (we just want dirs) + name =~ /^(?:\.\/)?([^\/]+)\// + name = $1 + name = "/" if name.nil? # file at top-level? fake up a name for repo rep = @@repositories[name] if rep.nil? rep = Repository.new(name) @@ -385,6 +388,7 @@ class FileEntry def initialize(path) @path = path + @fromVer = @toVer = nil @lineAdditions = @lineRemovals = 0 @repository = Repository.get(path) @repository.merge_common_prefix(basedir()) @@ -394,7 +398,7 @@ # the full path and filename within the repository attr_accessor :path - # the type of change committed 'M'=modified, 'A'=added, 'R'=removed + # the type of change committed 'M'=modified, 'A'=added, 'R'=removed, 'P'=properties, 'C'=copied attr_accessor :type # records number of 'addition' lines in diff output, once counted attr_accessor :lineAdditions @@ -412,7 +416,7 @@ # works out the filename part of #path def file @path =~ /.*\/(.*)/ - $1 || @path + $1 end # set the branch on which this change was committed, and add it to the list @@ -430,7 +434,7 @@ # works out the directory part of #path def basedir @path =~ /(.*)\/.*/ - $1 || "/" + $1 end # gives the Repository object this file was automatically associated with @@ -449,16 +453,27 @@ def removal? @type == "R" end - + # was this file added during the commit? def addition? @type == "A" end + # was this file copied during the commit? + def copied? + @type == "C" + end + # was this file simply modified during the commit? def modification? @type == "M" end + + # was this file simply modified during the commit? + def modifiedprops? + @type == "P" + end + # passing true, this object remembers that a diff will appear in the email, # passing false, this object remembers that no diff will appear in the email. @@ -530,6 +545,14 @@ # TODO: consolidate these into a nicer framework, mailSub = proc { |match| "#{match}" } urlSub = proc { |match| "#{match}" } +gforgeTaskSub = proc { |match| + match =~ /([0-9]+)/ + "#{match}" +} +gforgeBugSub = proc { |match| + match =~ /([0-9]+)/ + "#{match}" +} bugzillaSub = proc { |match| match =~ /([0-9]+)/ "#{match}" @@ -541,9 +564,15 @@ match =~ /([0-9]+)/ "#{match}" } +wikiSub = proc { |match| + match =~ /\[\[(.*)\]\]/ + raw = $1 + "[[#{raw}]]" +} commentSubstitutions = { '(?:mailto:)?[\w\.\-\+\=]+\@[\w\-]+(?:\.[\w\-]+)+\b' => mailSub, - '\b(?:http|https|ftp):[^ \t\n<>"]+[\w/]' => urlSub} + '\b(?:http|https|ftp):[^ \t\n<>"]+[\w/]' => urlSub + } # outputs commit log comment text supplied by LogReader as preformatted HTML class CommentHandler < LineConsumer @@ -661,6 +690,12 @@ def diff(file) '->' end + + # may be overridden by subclasses that are able to make a hyperlink to a + # history log for a file + def log(file) + '' + end end # Superclass for objects that can link to CVS frontends on the web (ViewCVS, @@ -701,6 +736,14 @@ "#{super(file)}" end + def log(file) + link = log_url(file) + if link + return "(log)" + end + return nil + end + protected def add_repo(url) if @repository_name @@ -713,6 +756,10 @@ url end end + + def log_url(file) + nil + end end # Link to ViewCVS @@ -771,6 +818,17 @@ def diff_url(file) add_repo("#{@base_url}#{urlEncode(file.path)}.diff?r1=text&tr1=#{file.fromVer}&r2=text&tr2=#{file.toVer}&f=h") end + + protected + + def log_url(file) + if file.toVer + log_anchor = "#rev#{file.toVer}" + else + log_anchor = "" + end + add_repo("#{@base_url}#{urlEncode(file.path)}#{log_anchor}") + end end @@ -792,6 +850,15 @@ end end +# Note when LogReader finds record of a file that was copied in this commit +class CopiedFileHandler < FileHandler + def handleFile(file) + file.type="C" + file.fromVer=$fromVer + file.toVer=$toVer + end +end + # Note when LogReader finds record of a file that was modified in this commit class ModifiedFileHandler < FileHandler def handleFile(file) @@ -801,6 +868,15 @@ end end +# Note when LogReader finds record of a file whose properties were modified in this commit +class ModifiedPropsFileHandler < FileHandler + def handleFile(file) + file.type="P" + file.fromVer=$fromVer + file.toVer=$toVer + end +end + # Used by UnifiedDiffHandler to record the number of added and removed lines # appearing in a unidiff. @@ -967,11 +1043,21 @@ print($frontend.path($file.basedir, $file.tag)) println("
") println("
#{htmlEncode($file.file)} removed after #{$frontend.version($file.path,$file.fromVer)}
") + when "C" + print("") + print($frontend.path($file.basedir, $file.tag)) + println("
") + println("
#{htmlEncode($file.file)} copied from #{$frontend.version($file.path,$file.fromVer)}
") when "M" print("") print($frontend.path($file.basedir, $file.tag)) println("
") println("
#{htmlEncode($file.file)} #{$frontend.version($file.path,$file.fromVer)} #{$frontend.diff($file)} #{$frontend.version($file.path,$file.toVer)}
") + when "P" + print("") + print($frontend.path($file.basedir, $file.tag)) + println("
") + println("
#{htmlEncode($file.file)} #{$frontend.version($file.path,$file.fromVer)} #{$frontend.diff($file)} #{$frontend.version($file.path,$file.toVer)}
") end print("
")
     lines.each do |line|
@@ -1045,7 +1131,7 @@
      else
       @stats.consume(line)
       if $file.wants_diff_in_mail?
-        if @stats.diffLines < $maxLinesPerDiff
+        if $maxLinesPerDiff.nil? || @stats.diffLines < $maxLinesPerDiff
           @colour.consume(line)
         elsif @stats.diffLines == $maxLinesPerDiff
           @colour.consume(line)
@@ -1062,7 +1148,7 @@
       $file.isBinary = true
     else
       if $file.wants_diff_in_mail?
-        if @stats.diffLines > $maxLinesPerDiff
+        if $maxLinesPerDiff && @stats.diffLines > $maxLinesPerDiff
           println("
") println("[truncated at #{$maxLinesPerDiff} lines; #{@stats.diffLines-$maxLinesPerDiff} more skipped]") else @@ -1230,13 +1316,18 @@ $users_file = "#{cvsroot_dir}/users" $debug = false +$svn = false $recipients = Array.new $sendmail_prog = "/usr/sbin/sendmail" +$hostname = ENV['HOSTNAME'] || 'localhost' $no_removed_file_diff = false $no_added_file_diff = false $no_diff = false -$task_keywords = ['TODO', 'FIXME'] +$task_keywords = ['TODO', 'FIXME', 'FIXIT', 'todo'] $bugzillaURL = nil +$gforgeBugURL = nil +$gforgeTaskURL = nil +$wikiURL = nil $jiraURL = nil $ticketURL = nil $viewcvsURL = nil @@ -1257,6 +1348,7 @@ [ "--to", "-t", GetoptLong::REQUIRED_ARGUMENT ], [ "--config", "-c", GetoptLong::REQUIRED_ARGUMENT ], [ "--debug", "-d", GetoptLong::NO_ARGUMENT ], + [ "--svn", "-s", GetoptLong::NO_ARGUMENT ], [ "--from", "-u", GetoptLong::REQUIRED_ARGUMENT ], [ "--charset", GetoptLong::REQUIRED_ARGUMENT ] ) @@ -1265,6 +1357,7 @@ $recipients << EmailAddress.new(arg) if opt=="--to" $config = arg if opt=="--config" $debug = true if opt=="--debug" + $svn = true if opt=="--svn" $from_address = EmailAddress.new(arg) if opt=="--from" # must use different variable as the config is readed later. $arg_charset = arg if opt == "--charset" @@ -1277,12 +1370,13 @@ else $stderr.puts "missing required file argument" end - puts "Usage: cvsspam.rb [ --to ] [ --config ] " + puts "Usage: cvsspam.rb [ --svn ] [ --to ] [ --config ] " exit(-1) end $logfile = ARGV[0] + $additionalHeaders = Array.new $problemHeaders = Array.new @@ -1343,12 +1437,21 @@ if $bugzillaURL != nil commentSubstitutions['\b[Bb][Uu][Gg]\s*#?[0-9]+'] = bugzillaSub end +if $gforgeBugURL != nil + commentSubstitutions['\B\[#[0-9]+\]'] = gforgeBugSub +end +if $gforgeTaskURL != nil + commentSubstitutions['\B\[[Tt][0-9]+\]'] = gforgeTaskSub +end if $jiraURL != nil commentSubstitutions['\b[a-zA-Z]+-[0-9]+\b'] = jiraSub end if $ticketURL != nil commentSubstitutions['\b[Tt][Ii][Cc][Kk][Ee][Tt]\s*#?[0-9]+\b'] = ticketSub end +if $wikiURL != nil + commentSubstitutions['\[\[.+\]\]'] = wikiSub +end $commentEncoder = MultiSub.new(commentSubstitutions) @@ -1359,12 +1462,16 @@ "T" => tagHandler, "A" => AddedFileHandler.new, "R" => RemovedFileHandler.new, + "C" => CopiedFileHandler.new, "M" => ModifiedFileHandler.new, + "P" => ModifiedPropsFileHandler.new, "V" => VersionHandler.new] $handlers["A"].setTagHandler(tagHandler) $handlers["R"].setTagHandler(tagHandler) +$handlers["C"].setTagHandler(tagHandler) $handlers["M"].setTagHandler(tagHandler) +$handlers["P"].setTagHandler(tagHandler) $fileEntries = Array.new $task_list = Array.new @@ -1374,7 +1481,8 @@ $diff_output_limiter = OutputSizeLimiter.new(mail, $mail_size_limit) - reader = LogReader.new($stdin) + File.open($logfile) do |log| + reader = LogReader.new(log) until reader.eof handler = $handlers[reader.currentLineCode] @@ -1383,11 +1491,16 @@ end handler.handleLines(reader.getLines, $diff_output_limiter) end + end end if $subjectPrefix == nil - $subjectPrefix = "[SVN #{Repository.array.join(',')}]" + if $svn + $subjectPrefix = "[SVN #{Repository.array.join(',')}]" + else + $subjectPrefix = "[CVS #{Repository.array.join(',')}]" + end end if $files_in_subject @@ -1434,13 +1547,15 @@ #removed {background-color:#ffdddd;} #removedchars {background-color:#ff9999;font-weight:bolder;} tr.alt #removed {background-color:#f7cccc;} + #copied {background-color:#ccccff;} + tr.alt #copied {background-color:#bbbbf7;} #info {color:#888888;} #context {background-color:#eeeeee;} td {padding-left:.3em;padding-right:.3em;} tr.head {border-bottom-width:1px;border-bottom-style:solid;} tr.head td {padding:0;padding-top:.2em;} .task {background-color:#ffff00;} - .comment {padding:4px;border:1px dashed #000000;background-color:#ffffdd} + .comment {white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap;white-space:pre-wrap;word-wrap:break-word;padding:4px;border:1px dashed #000000;background-color:#ffffdd} .error {color:red;} hr {border-width:0px;height:2px;background:black;} --> @@ -1466,7 +1581,9 @@ filesAdded = 0 filesRemoved = 0 + filesCopied = 0 filesModified = 0 + filesModifiedProps = 0 totalLinesAdded = 0 totalLinesRemoved = 0 file_count = 0 @@ -1475,24 +1592,26 @@ $fileEntries.each do |file| unless file.repository == last_repository last_repository = file.repository - mail.print("") + mail.print("") if last_repository.has_multiple_tags mail.print("Mixed-tag commit") else mail.print("Commit") end mail.print(" in #{htmlEncode(last_repository.common_prefix)}") - if last_repository.trunk_only? - mail.print(" on MAIN") - else - mail.print(" on ") - tagCount = 0 - last_repository.each_tag do |tag| - tagCount += 1 - if tagCount > 1 - mail.print tagCount on MAIN") + else + mail.print(" on ") + tagCount = 0 + last_repository.each_tag do |tag| + tagCount += 1 + if tagCount > 1 + mail.print tagCountMAIN" end - mail.print tag ? htmlEncode(tag) : "MAIN" end end mail.puts("") @@ -1507,8 +1626,12 @@ filesAdded += 1 elsif file.removal? filesRemoved += 1 + elsif file.copied? + filesCopied += 1 elsif file.modification? filesModified += 1 + elsif file.modifiedprops? + filesModifiedProps += 1 end name = htmlEncode(file.name_after_common_prefix) slashPos = name.rindex("/") @@ -1528,17 +1651,29 @@ name = "#{name}" elsif file.removal? name = "#{name}" + elsif file.copied? + name = "#{name}" end + mail.print("") if file.has_diff? - mail.print("#{prefix}#{name}") + mail.print("#{prefix}#{name}") else - mail.print("#{prefix}#{name}") + mail.print("#{prefix}#{name}") end - if file.isEmpty - mail.print("[empty]") + mail.print(" #{$frontend.log(file)}") + mail.print("") + if file.copied? + mail.print("[copied]") + elsif file.isEmpty + mail.print("[empty]") elsif file.isBinary - mail.print("[binary]") + mail.print("[binary]") else + if file.modifiedprops? + mail.print("[props]") + else + mail.print("") + end if file.lineAdditions>0 totalLinesAdded += file.lineAdditions mail.print("+#{file.lineAdditions}") @@ -1565,15 +1700,19 @@ mail.print("added #{$frontend.version(file.path,file.toVer)}") elsif file.removal? mail.print("#{$frontend.version(file.path,file.fromVer)} removed") + elsif file.copied? + mail.print("#{$frontend.version(file.path,file.fromVer)} #{$frontend.diff(file)} #{$frontend.version(file.path,file.toVer)}") elsif file.modification? mail.print("#{$frontend.version(file.path,file.fromVer)} #{$frontend.diff(file)} #{$frontend.version(file.path,file.toVer)}") + elsif file.modifiedprops? + mail.print("#{$frontend.version(file.path,file.fromVer)} #{$frontend.diff(file)} #{$frontend.version(file.path,file.toVer)}") end mail.puts("") end if $fileEntries.size>1 && (totalLinesAdded+totalLinesRemoved)>0 # give total number of lines added/removed accross all files - mail.print("") + mail.print("") if totalLinesAdded>0 mail.print("+#{totalLinesAdded}") else @@ -1590,7 +1729,7 @@ mail.puts("") - totalFilesChanged = filesAdded+filesRemoved+filesModified + totalFilesChanged = filesAdded+filesRemoved+filesCopied+filesModified+filesModifiedProps if totalFilesChanged > 1 mail.print("") changeKind = 0 @@ -1603,11 +1742,21 @@ mail.print("#{filesRemoved} removed") changeKind += 1 end + if filesCopied>0 + mail.print(" + ") if changeKind>0 + mail.print("#{filesCopied} copied") + changeKind += 1 + end if filesModified>0 mail.print(" + ") if changeKind>0 mail.print("#{filesModified} modified") changeKind += 1 end + if filesModifiedProps>0 + mail.print(" + ") if changeKind>0 + mail.print("#{filesModifiedProps} modified properties") + changeKind += 1 + end mail.print(", total #{totalFilesChanged}") if changeKind > 1 mail.puts(" files
") end @@ -1742,7 +1891,7 @@ from = EmailAddress.new(ENV['USER'] || ENV['USERNAME'] || 'cvsspam') end unless from.address =~ /@/ - from.address = "#{from.address}@#{ENV['HOSTNAME']||'localhost'}" + from.address = "#{from.address}@#{$hostname}" end smtp = Net::SMTP.new(@smtp_host) blah("connecting to '#{@smtp_host}'") @@ -1758,6 +1907,40 @@ end end + +def make_msg_id(localpart, hostpart) + "" +end + + +# replaces control characters, and a selection of other characters that +# may not appear unquoted in an RFC822 'word', with underscores. (It +# doesn't actually zap '.' though.) +def zap_header_special_chars(text) + text.gsub(/<>()\[\]@,;:\\[\000-\037\177]/, "_") +end + + +# Mail clients will try to 'thread' together a conversation over +# several email messages by inspecting the In-Reply-To and References headers, +# which should refer to previous emails in the conversation by mentioning +# the value of the previous message's Message-Id header. This function invents +# values for these headers so that, in the special case where a *single* file +# is committed to repeatedly, the emails giving notification of these commits +# can be threaded together automatically by the mail client. +def inject_threading_headers(mail) + return unless $fileEntries.length == 1 + file = $fileEntries[0] + name = zap_header_special_chars(file.path) + if file.fromVer + mail.header("References", make_msg_id("#{name}.#{file.fromVer}", $hostname)) + end + if file.toVer + mail.header("Message-ID", make_msg_id("#{name}.#{file.toVer}", $hostname)) + end +end + + if $smtp_host require 'net/smtp' mailer = SMTPMailer.new($smtp_host) @@ -1769,6 +1952,7 @@ mailer.send($from_address, $recipients) do |mail| mail.header("Subject", mailSubject) + inject_threading_headers(mail) mail.header("MIME-Version", "1.0") mail.header("Content-Type", "text/html" + ($charset.nil? ? "" : "; charset=\"#{$charset}\"")) if ENV['REMOTE_HOST'] --- cvsspam-0.2.12/svn_post_commit_hook.rb 2005-07-11 18:53:29.000000000 +0300 +++ cvsspam/cvsspam-svn/svn_post_commit_hook.rb 2008-08-07 17:27:52.628725224 +0300 @@ -34,7 +34,7 @@ def send_email cmd = File.dirname($0) + "/cvsspam.rb" - unless system(cmd, "#{$datadir}/logfile", *$passthrough_args) + unless system(cmd,"--svn","#{$datadir}/logfile", *$passthrough_args) fail "problem running '#{cmd}'" end end @@ -86,6 +86,8 @@ unless FileTest.directory?($repository) usage("no such directory: #{$repository.inspect}") end + $repository =~ /([^\/]+$)/ + $shortrepo = $1 end # runs the given svnlook subcommand @@ -123,16 +125,6 @@ end -def each_changed - svnlook("changed", $revision) do |io| - io.each_line do |line| - line =~ /^(.)(.) (.*)$/ - yield Change.new($1, $2, $3) - end - end -end - - # Line-oriented access to an underlying IO object. Remembers 'current' line # for lookahead during parsing. @@ -149,10 +141,15 @@ (@line = @io.gets) != nil end + def assert_current(re) + raise "unexpected #{current.inspect}" unless @line =~ re + $~ + end + def assert_next(re=nil) raise "unexpected end of text" unless next_line unless re.nil? - raise "unexpected #{lines.current.inspect}" unless @line =~ re + raise "unexpected #{current.inspect}" unless @line =~ re end $~ end @@ -161,14 +158,34 @@ def read_modified_diff(out, lines, path) lines.assert_next(/^=+$/) - m = lines.assert_next(/^---.*\(rev (\d+)\)$/) + lines.assert_next + if lines.current =~ /\(Binary files differ\)/ + process_modified_binary_diff(out, lines, path) + else + process_modified_text_diff(out, lines, path) + end +end + + +def process_modified_binary_diff(out, lines, path) + prev_rev= $revision-1 + next_rev= $revision + out.puts "#V #{prev_rev},#{next_rev}" + out.puts "#M #{$shortrepo}/#{path}" + out.puts "#U diff x x" + out.puts "#U Binary files x and y differ" +end + + +def process_modified_text_diff(out, lines, path) + m = lines.assert_current(/^---.*\(rev (\d+)\)$/) prev_rev = m[1].to_i diff1 = lines.current m = lines.assert_next(/^\+\+\+.*\(rev (\d+)\)$/) next_rev = m[1].to_i diff2 = lines.current out.puts "#V #{prev_rev},#{next_rev}" - out.puts "#M #{path}" + out.puts "#M #{$shortrepo}/#{path}" out.puts "#U #{diff1}" out.puts "#U #{diff2}" while lines.next_line && lines.current =~ /^[-\+ @\\]/ @@ -178,14 +195,31 @@ def read_added_diff(out, lines, path) lines.assert_next(/^=+$/) - m = lines.assert_next(/^---.*\(rev (\d+)\)$/) + lines.assert_next + if lines.current =~ /\(Binary files differ\)/ + process_added_binary_diff(out, lines, path) + else + process_added_text_diff(out, lines, path) + end +end + +def process_added_binary_diff(out, lines, path) + next_rev= $revision + out.puts "#V NONE,#{next_rev}" + out.puts "#A #{$shortrepo}/#{path}" + out.puts "#U diff x x" + out.puts "#U Binary file x added" +end + +def process_added_text_diff(out, lines, path) + m = lines.assert_current(/^---.*\(rev (\d+)\)$/) prev_rev = m[1].to_i diff1 = lines.current m = lines.assert_next(/^\+\+\+.*\(rev (\d+)\)$/) next_rev = m[1].to_i diff2 = lines.current out.puts "#V NONE,#{next_rev}" - out.puts "#A #{path}" + out.puts "#A #{$shortrepo}/#{path}" out.puts "#U #{diff1}" out.puts "#U #{diff2}" while lines.next_line && lines.current =~ /^[-\+ @\\]/ @@ -202,7 +236,7 @@ next_rev = m[1].to_i diff2 = lines.current out.puts "#V #{prev_rev},NONE" - out.puts "#R #{path}" + out.puts "#R #{$shortrepo}/#{path}" out.puts "#U #{diff1}" out.puts "#U #{diff2}" while lines.next_line && lines.current =~ /^[-\+ @\\]/ @@ -221,13 +255,23 @@ end def assert_prop_match(a, b) - if a != b + if !b.nil? && a != b raise "property mismatch: #{a.inspect}!=#{b.inspect}" end end +# We need to read the property change from the output of svnlook, but have +# a difficulty in that there's no unambiguous delimiter marking the end of +# a potentially multi-line property value. Therefore, we do a seperate +# svn propget on the given file to get the value of the property on its own, +# and then use that value as a guide as to how much data to read from the +# svnlook output. def munch_prop_text(path, prop_name, revision, lines, line0) prop = read_property_lines(path, prop_name, revision) + if prop.empty? + assert_prop_match(line0, "") + return + end assert_prop_match(line0, prop.shift) prop.each do |prop_line| lines.assert_next @@ -236,8 +280,16 @@ end def read_properties_changed(out, lines, path) + prev_rev= $revision-1 + next_rev= $revision lines.assert_next(/^_+$/) return unless lines.next_line + out.puts "#V #{prev_rev},#{next_rev}" + out.puts "#P #{$shortrepo}/#{path}" +# The first three get consumed and not highlighted + out.puts "#U " + out.puts "#U Property changes:" + out.puts "#U " while true break unless lines.current =~ /^Name: (.+)$/ prop_name = $1 @@ -254,13 +306,49 @@ munch_prop_text(path, prop_name, $revision, lines, line0) lines.next_line end + out.puts "#U #{m[1]} #{prop_name}:#{m[2]}" end + out.puts "#U " end def handle_copy(out, lines, path, from_ref, from_file) - # TODO: handle file copies in email + prev_rev= $revision-1 + next_rev= $revision + out.puts "#V #{$shortrepo}/#{from_file}:#{prev_rev},#{next_rev}" + out.puts "#C #{$shortrepo}/#{path}" + if lines.next_line && lines.current =~ /^=+$/ + m = lines.assert_next(/^---.*\(rev (\d+)\)$/) + prev_rev = m[1].to_i + diff1 = lines.current + m = lines.assert_next(/^\+\+\+.*\(rev (\d+)\)$/) + next_rev = m[1].to_i + diff2 = lines.current + out.puts "#U #{diff1}" + out.puts "#U #{diff2}" + while lines.next_line && lines.current =~ /^[-\+ @\\]/ + out.puts "#U #{lines.current}" + end + else + out.puts "#U " + out.puts "#U Copied from #{$shortrepo}/#{from_file}:#{from_ref}" + out.puts "#U " + end end +def svnlook_author + svnlook("author", $revision) do |io| + return io.readline + end + nil +end + +def find_author + return if $passthrough_args.include?("--from") + author = svnlook_author + if author + $passthrough_args << "--from" << author + end +end def process_svnlook_log(file) svnlook("log", $revision) do |io| @@ -294,16 +382,17 @@ end def process_commit() - File.open("#{$datadir}/logfile", File::WRONLY|File::CREAT) do |file| - process_svnlook_log(file) - process_svnlook_diff(file) - end + File.open("#{$datadir}/logfile", File::WRONLY|File::CREAT) do |file| + process_svnlook_log(file) + process_svnlook_diff(file) + end end def main init() process_args() + find_author() process_commit() send_email() cleanup()