+++ /dev/null
---- svn_post_commit_hook.rb (.../trunk) (revision 0)
-+++ svn_post_commit_hook.rb (.../branches/svn_support) (revision 269)
-@@ -0,0 +1,412 @@
-+#!/usr/bin/ruby -w
-+
-+$svnlook_exe = "svnlook" # default assumes the program is in $PATH
-+
-+def usage(msg)
-+ $stderr.puts(msg)
-+ exit(1)
-+end
-+
-+def blah(msg)
-+ if $debug
-+ $stderr.puts "svn_post_commit_hook.rb: #{msg}"
-+ end
-+end
-+
-+$debug = false
-+$tmpdir = ENV["TMPDIR"] || "/tmp"
-+$dirtemplate = "#svnspam.#{Process.getpgrp}.#{Process.uid}"
-+# arguments to pass though to 'cvsspam.rb'
-+$passthrough_args = []
-+
-+def make_data_dir
-+ dir = "#{$tmpdir}/#{$dirtemplate}-#{rand(99999999)}"
-+ Dir.mkdir(dir, 0700)
-+ dir
-+end
-+
-+def init
-+ $datadir = make_data_dir
-+
-+ # set PWD so that svnlook can create its .svnlook directory
-+ Dir.chdir($datadir)
-+end
-+
-+def cleanup
-+ unless $debug
-+ File.unlink("#{$datadir}/logfile")
-+ Dir.rmdir($datadir)
-+ end
-+end
-+
-+def send_email
-+ cmd = File.dirname($0) + "/cvsspam.rb"
-+ unless system(cmd,"--svn","#{$datadir}/logfile", *$passthrough_args)
-+ fail "problem running '#{cmd}'"
-+ end
-+end
-+
-+# Like IO.popen, but accepts multiple arguments like Kernel.exec
-+# (So no need to escape shell metacharacters)
-+def safer_popen(*args)
-+ IO.popen("-") do |pipe|
-+ if pipe==nil
-+ exec(*args)
-+ else
-+ yield pipe
-+ end
-+ end
-+end
-+
-+
-+# Process the command-line arguments in the given list
-+def process_args
-+ require 'getoptlong'
-+
-+ opts = GetoptLong.new(
-+ [ "--to", "-t", GetoptLong::REQUIRED_ARGUMENT ],
-+ [ "--config", "-c", GetoptLong::REQUIRED_ARGUMENT ],
-+ [ "--debug", "-d", GetoptLong::NO_ARGUMENT ],
-+ [ "--from", "-u", GetoptLong::REQUIRED_ARGUMENT ],
-+ [ "--charset", GetoptLong::REQUIRED_ARGUMENT ]
-+ )
-+
-+ opts.each do |opt, arg|
-+ if ["--to", "--config", "--from", "--charset"].include?(opt)
-+ $passthrough_args << opt << arg
-+ end
-+ if ["--debug"].include?(opt)
-+ $passthrough_args << opt
-+ end
-+ $config = arg if opt=="--config"
-+ $debug = true if opt == "--debug"
-+ end
-+
-+ $repository = ARGV[0]
-+ $revision = ARGV[1]
-+
-+ unless $revision =~ /^\d+$/
-+ usage("revision must be an integer: #{revision.inspect}")
-+ end
-+ $revision = $revision.to_i
-+
-+ unless FileTest.directory?($repository)
-+ usage("no such directory: #{$repository.inspect}")
-+ end
-+ $repository =~ /([^\/]+$)/
-+ $shortrepo = $1
-+end
-+
-+# runs the given svnlook subcommand
-+def svnlook(cmd, revision, *args)
-+ rev = revision.to_s
-+ safer_popen($svnlook_exe, cmd, $repository, "-r", rev, *args) do |io|
-+ yield io
-+ end
-+end
-+
-+class Change
-+ def initialize(filechange, propchange, path)
-+ @filechange = filechange
-+ @propchange = propchange
-+ @path = path
-+ end
-+
-+ attr_accessor :filechange, :propchange, :path
-+
-+ def property_change?
-+ @propchange != " "
-+ end
-+
-+ def file_change?
-+ @filechange != "_"
-+ end
-+
-+ def addition?
-+ @filechange == "A"
-+ end
-+
-+ def deletion?
-+ @filechange == "D"
-+ end
-+end
-+
-+
-+
-+# Line-oriented access to an underlying IO object. Remembers 'current' line
-+# for lookahead during parsing.
-+class LineReader
-+ def initialize(io)
-+ @io = io
-+ end
-+
-+ def current
-+ @line
-+ end
-+
-+ def next_line
-+ (@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 #{current.inspect}" unless @line =~ re
-+ end
-+ $~
-+ end
-+end
-+
-+
-+def read_modified_diff(out, lines, path)
-+ lines.assert_next(/^=+$/)
-+ 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 #{$shortrepo}/#{path}"
-+ out.puts "#U #{diff1}"
-+ out.puts "#U #{diff2}"
-+ while lines.next_line && lines.current =~ /^[-\+ @\\]/
-+ out.puts "#U #{lines.current}"
-+ end
-+end
-+
-+def read_added_diff(out, lines, path)
-+ lines.assert_next(/^=+$/)
-+ 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 #{$shortrepo}/#{path}"
-+ out.puts "#U #{diff1}"
-+ out.puts "#U #{diff2}"
-+ while lines.next_line && lines.current =~ /^[-\+ @\\]/
-+ out.puts "#U #{lines.current}"
-+ end
-+end
-+
-+def read_deleted_diff(out, lines, path)
-+ lines.assert_next(/^=+$/)
-+ 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 "#V #{prev_rev},NONE"
-+ out.puts "#R #{$shortrepo}/#{path}"
-+ out.puts "#U #{diff1}"
-+ out.puts "#U #{diff2}"
-+ while lines.next_line && lines.current =~ /^[-\+ @\\]/
-+ out.puts "#U #{lines.current}"
-+ end
-+end
-+
-+def read_property_lines(path, prop_name, revision)
-+ lines = []
-+ svnlook("propget", revision, prop_name, path) do |io|
-+ io.each_line do |line|
-+ lines << line.chomp
-+ end
-+ end
-+ lines
-+end
-+
-+def assert_prop_match(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
-+ assert_prop_match(lines.current.chomp, prop_line)
-+ end
-+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|Added|Deleted): (.+)$/
-+
-+ prop_name = $1
-+ m = lines.assert_next(/^ ([-+]) (.*)/)
-+ op = m[1]
-+ line0 = m[2]
-+ if op == "-"
-+ munch_prop_text(path, prop_name, $revision-1, lines, line0)
-+ if lines.next_line && lines.current =~ /^ \+ (.*)/
-+ munch_prop_text(path, prop_name, $revision, lines, $1)
-+ lines.next_line
-+ end
-+ else # op == "+"
-+ 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)
-+ 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.chomp
-+ end
-+ nil
-+end
-+
-+def find_author
-+ return if $passthrough_args.include?("--from")
-+ author = svnlook_author
-+ if author
-+ blah("Author from svnlook: '#{author}'")
-+ $passthrough_args << "--from" << author
-+ end
-+end
-+
-+def process_svnlook_log(file)
-+ svnlook("log", $revision) do |io|
-+ io.each_line do |line|
-+ file.puts("#> #{line}")
-+ end
-+ end
-+end
-+
-+def process_svnlook_diff(file)
-+ svnlook("diff", $revision) do |io|
-+ lines = LineReader.new(io)
-+ while lines.next_line
-+ if lines.current =~ /^Modified:\s+(.*)/
-+ read_modified_diff(file, lines, $1)
-+ elsif lines.current =~ /^Added:\s+(.*)/
-+ read_added_diff(file, lines, $1)
-+ elsif lines.current =~ /^Copied:\s+(.*) \(from rev (\d+), (.*)\)$/
-+ handle_copy(file, lines, $1, $2, $3)
-+ elsif lines.current =~ /^Deleted:\s+(.*)/
-+ read_deleted_diff(file, lines, $1)
-+ elsif lines.current =~ /^Property changes on:\s+(.*)/
-+ read_properties_changed(file, lines, $1)
-+ elsif lines.current == "\n"
-+ # ignore
-+ else
-+ raise "unable to parse line '#{lines.current.inspect}'"
-+ end
-+ end
-+ end
-+end
-+
-+def process_commit()
-+ 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()
-+end
-+
-+
-+main
---- cvsspam.rb (.../trunk) (revision 269)
-+++ cvsspam.rb (.../branches/svn_support) (revision 269)
-@@ -398,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
-@@ -453,17 +453,28 @@
- 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.
- # Once the value is set, it will not be changed
-@@ -889,6 +900,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)
-@@ -898,7 +918,16 @@
- 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.
- class UnifiedDiffStats
-@@ -1064,11 +1093,21 @@
- print($frontend.path($file.basedir, $file.tag))
- println("</span><br />")
- println("<div class=\"fileheader\" id=\"removed\"><big><b>#{htmlEncode($file.file)}</b></big> <small id=\"info\">removed after #{$frontend.version($file.path,$file.fromVer)}</small></div>")
-+ when "C"
-+ print("<span class=\"pathname\" id=\"copied\">")
-+ print($frontend.path($file.basedir, $file.tag))
-+ println("</span><br />")
-+ println("<div class=\"fileheader\" id=\"copied\"><big><b>#{htmlEncode($file.file)}</b></big> <small id=\"info\">copied from #{$frontend.version($file.path,$file.fromVer)}</small></div>")
- when "M"
- print("<span class=\"pathname\">")
- print($frontend.path($file.basedir, $file.tag))
- println("</span><br />")
- println("<div class=\"fileheader\"><big><b>#{htmlEncode($file.file)}</b></big> <small id=\"info\">#{$frontend.version($file.path,$file.fromVer)} #{$frontend.diff($file)} #{$frontend.version($file.path,$file.toVer)}</small></div>")
-+ when "P"
-+ print("<span class=\"pathname\">")
-+ print($frontend.path($file.basedir, $file.tag))
-+ println("</span><br />")
-+ println("<div class=\"fileheader\"><big><b>#{htmlEncode($file.file)}</b></big> <small id=\"info\">#{$frontend.version($file.path,$file.fromVer)} #{$frontend.diff($file)} #{$frontend.version($file.path,$file.toVer)}</small></div>")
- end
- print("<pre class=\"diff\"><small id=\"info\">")
- lines.each do |line|
-@@ -1329,6 +1368,7 @@
- $users_file_charset = nil
-
- $debug = false
-+$svn = false
- $recipients = Array.new
- $sendmail_prog = "/usr/sbin/sendmail"
- $hostname = ENV['HOSTNAME'] || 'localhost'
-@@ -1366,6 +1406,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 ]
- )
-@@ -1374,6 +1415,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"
-@@ -1386,7 +1428,7 @@
- else
- $stderr.puts "missing required file argument"
- end
-- puts "Usage: cvsspam.rb [ --to <email> ] [ --config <file> ] <collect_diffs file>"
-+ puts "Usage: cvsspam.rb [ --svn ] [ --to <email> ] [ --config <file> ] <collect_diffs file>"
- exit(-1)
- end
-
-@@ -1495,12 +1537,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
-@@ -1525,7 +1571,11 @@
- end
-
- if $subjectPrefix == nil
-- $subjectPrefix = "[CVS #{Repository.array.join(',')}]"
-+ if $svn
-+ $subjectPrefix = "[SVN #{Repository.array.join(',')}]"
-+ else
-+ $subjectPrefix = "[CVS #{Repository.array.join(',')}]"
-+ end
- end
-
- if $files_in_subject
-@@ -1572,6 +1622,8 @@
- #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;}
-@@ -1604,7 +1656,9 @@
-
- filesAdded = 0
- filesRemoved = 0
-+ filesCopied = 0
- filesModified = 0
-+ filesModifiedProps = 0
- totalLinesAdded = 0
- totalLinesRemoved = 0
- file_count = 0
-@@ -1613,24 +1667,26 @@
- $fileEntries.each do |file|
- unless file.repository == last_repository
- last_repository = file.repository
-- mail.print("<tr class=\"head\"><td colspan=\"#{last_repository.has_multiple_tags ? 5 : 4}\">")
-+ mail.print("<tr class=\"head\"><td colspan=\"#{last_repository.has_multiple_tags ? 6 : 5}\">")
- if last_repository.has_multiple_tags
- mail.print("Mixed-tag commit")
- else
- mail.print("Commit")
- end
- mail.print(" in <b><tt>#{htmlEncode(last_repository.common_prefix)}</tt></b>")
-- if last_repository.trunk_only?
-- mail.print("<span id=\"info\"> on MAIN</span>")
-- else
-- mail.print(" on ")
-- tagCount = 0
-- last_repository.each_tag do |tag|
-- tagCount += 1
-- if tagCount > 1
-- mail.print tagCount<last_repository.tag_count ? ", " : " & "
-+ if !$svn
-+ if last_repository.trunk_only?
-+ mail.print("<span id=\"info\"> on MAIN</span>")
-+ else
-+ mail.print(" on ")
-+ tagCount = 0
-+ last_repository.each_tag do |tag|
-+ tagCount += 1
-+ if tagCount > 1
-+ mail.print tagCount<last_repository.tag_count ? ", " : " & "
-+ end
-+ mail.print tag ? htmlEncode(tag) : "<span id=\"info\">MAIN</span>"
- end
-- mail.print tag ? htmlEncode(tag) : "<span id=\"info\">MAIN</span>"
- end
- end
- mail.puts("</td></tr>")
-@@ -1645,8 +1701,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("/")
-@@ -1666,6 +1726,8 @@
- name = "<span id=\"added\">#{name}</span>"
- elsif file.removal?
- name = "<span id=\"removed\">#{name}</span>"
-+ elsif file.copied?
-+ name = "<span id=\"copied\">#{name}</span>"
- end
- mail.print("<td>")
- if file.has_diff?
-@@ -1675,11 +1737,18 @@
- end
- mail.print(" #{$frontend.log(file)}")
- mail.print("</td>")
-- if file.isEmpty
-- mail.print("<td colspan=\"2\" align=\"center\"><small id=\"info\">[empty]</small></td>")
-+ if file.copied?
-+ mail.print("<td colspan=\"3\" align=\"center\"><small id=\"info\">[copied]</small></td>")
-+ elsif file.isEmpty
-+ mail.print("<td colspan=\"3\" align=\"center\"><small id=\"info\">[empty]</small></td>")
- elsif file.isBinary
-- mail.print("<td colspan=\"2\" align=\"center\"><small id=\"info\">[binary]</small></td>")
-+ mail.print("<td colspan=\"3\" align=\"center\"><small id=\"info\">[binary]</small></td>")
- else
-+ if file.modifiedprops?
-+ mail.print("<td align=\"right\"><small id=\"info\">[props]</small></td>")
-+ else
-+ mail.print("<td></td>")
-+ end
- if file.lineAdditions>0
- totalLinesAdded += file.lineAdditions
- mail.print("<td align=\"right\" id=\"added\">+#{file.lineAdditions}</td>")
-@@ -1706,15 +1775,19 @@
- mail.print("<td nowrap=\"nowrap\" align=\"right\">added #{$frontend.version(file.path,file.toVer)}</td>")
- elsif file.removal?
- mail.print("<td nowrap=\"nowrap\">#{$frontend.version(file.path,file.fromVer)} removed</td>")
-+ elsif file.copied?
-+ mail.print("<td nowrap=\"nowrap\" align=\"center\">#{$frontend.version(file.path,file.fromVer)} #{$frontend.diff(file)} #{$frontend.version(file.path,file.toVer)}</td>")
- elsif file.modification?
- mail.print("<td nowrap=\"nowrap\" align=\"center\">#{$frontend.version(file.path,file.fromVer)} #{$frontend.diff(file)} #{$frontend.version(file.path,file.toVer)}</td>")
-+ elsif file.modifiedprops?
-+ mail.print("<td nowrap=\"nowrap\" align=\"center\">#{$frontend.version(file.path,file.fromVer)} #{$frontend.diff(file)} #{$frontend.version(file.path,file.toVer)}</td>")
- end
-
- mail.puts("</tr>")
- end
- if $fileEntries.size>1 && (totalLinesAdded+totalLinesRemoved)>0
- # give total number of lines added/removed accross all files
-- mail.print("<tr><td></td>")
-+ mail.print("<tr><td></td><td></td>")
- if totalLinesAdded>0
- mail.print("<td align=\"right\" id=\"added\">+#{totalLinesAdded}</td>")
- else
-@@ -1731,7 +1804,7 @@
-
- mail.puts("</table>")
-
-- totalFilesChanged = filesAdded+filesRemoved+filesModified
-+ totalFilesChanged = filesAdded+filesRemoved+filesCopied+filesModified+filesModifiedProps
- if totalFilesChanged > 1
- mail.print("<small id=\"info\">")
- changeKind = 0
-@@ -1744,11 +1817,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</small><br />")
- end