]> git.pld-linux.org Git - packages/cvsspam.git/blobdiff - cvsspam-branch.diff
- fixed branch diff
[packages/cvsspam.git] / cvsspam-branch.diff
index fe57ff45f6fecd6199667cdf30922afc52e13c29..11ce663c21bb4766c7a5ccad9c16cd0ee883af83 100644 (file)
@@ -1,7 +1,7 @@
 Index: cvsspam.conf
 ===================================================================
---- cvsspam.conf       (revision 223)
-+++ cvsspam.conf       (revision 253)
+--- cvsspam.conf       (.../tags/RELEASE-0_2_12)       (revision 277)
++++ cvsspam.conf       (.../trunk)     (revision 277)
 @@ -34,11 +34,19 @@
  #
  #     When $jiraURL is given, text of the form 'project-1234' will be linked
@@ -22,11 +22,30 @@ Index: cvsspam.conf
  
  # Link to Wiki systems
  # 
-@@ -125,6 +133,15 @@
+@@ -71,6 +79,7 @@
+ #$cvswebURL = "http://localhost/cgi-bin/cvsweb.cgi"
++#$tracURL = "http://localhost/trac/project"
+ # Additional SMTP Headers                                            (Optional)
+@@ -116,15 +125,32 @@
+-# cvsdiff keyword ignoring                  (Default: show changes in keywords)
++# cvsdiff keyword ignoring                (Default: show changes in keywords)
+ #
+ #     Changes in CVS keywords can be distracting.  For instance, the
+-#   $Revision: 1.12 $ keyword will change on each commit.  Set this value to true
++#   $Revision$ keyword will change on each commit.  Set this value to true
+ #   to exclude changes in keyword fields (adds the -kk option to cvs diff).
  #$diff_ignore_keywords = true
  
  
-+# cvsdiff whitespace ignoring               (Default: show whitespace-only changes)
++# cvsdiff whitespace ignoring         (Default: show whitespace-only changes)
 +#
 +#   Whitespace-only changes can distract from the rest of a diff. Set this
 +#   value to true to exclude changes in the amount of whitespace (adds the -b
@@ -34,16 +53,66 @@ Index: cvsspam.conf
 +
 +$diff_ignore_whitespace = true
 +
++
++# cvs diff files ignoring                                    (Default: empty)
++#
++#   Make CVSspam ignore certain files.
++#
++#     Can contain file masks, separated by whitespace.
++
++#$ignore_files = "*.al *.gif"
 +
  # $no_removed_file_diff and $no_added_file_diff
  #
  #     Set both these options, and emails will only include diffs for files
-@@ -177,3 +194,13 @@
+@@ -132,7 +158,7 @@
+ #   deleted...
+-# Don't show diff for removed files             (Default: show file's contents)
++# Don't show diff for removed files           (Default: show file's contents)
+ #
+ #     If you aren't interested in seeing the contents of a file that was
+ #   removed, set this option to true.  The files will still appear in the index
+@@ -166,14 +192,46 @@
+ #     Allows the specification of a character set for all generated emails.
+ #   The files CVS is dealing with should already be in the character set you
+ #   specify -- no transcoding is done.
++#
++#   Note that you can override this with --charset argument per module, etc.
+ #$charset="ISO-8859-1"
++# Users file                                  (Default: $CVSROOT/CVSROOT/users)
++#
++#      Specify users file to lookup From addresses for commites
++
++#$users_file = "/srv/svn/users"
++
++# Users file charset                                        (Default: $charset)
++#
++#      If the users file is encoded differently than $charset, You can override
++#   it here. Especially useful if you use --charset argument. See above.
++
++#$users_file_charset = "ISO-8859-1"
++
++
+ # File names in Subject                      (Default: no filenames in Subject)
+ #
+ #     Some people like file names to appear in the email subject.  To make
  #   them happy, you can say $files_in_subject = true here.
  
  #$files_in_subject = false
 +
 +
++# Module Path email header           (Default: no X-CVSspam-Module-Path header)
++#
++#     Sets 'X-CVSspam-Module-Path' header to contain common path of files commited.
++#   Useful for server side mail filtering.
++
++#$cvsroot_email_header = true
 +
 +# Email size limit                                        (Default: around 2MB)
 +#
@@ -52,13 +121,27 @@ Index: cvsspam.conf
 +#   more diffs onto.  Specify the number of bytes.
 +
 +#$mail_size_limit = 2097152
+
+Property changes on: cvsspam.conf
+___________________________________________________________________
+Deleted: svn:executable
+   - *
+Modified: svn:keywords
+   - Author Date Id Revision
+   + Author Date Id
+
 Index: collect_diffs.rb
 ===================================================================
---- collect_diffs.rb   (revision 223)
-+++ collect_diffs.rb   (revision 253)
-@@ -27,6 +27,13 @@
+--- collect_diffs.rb   (.../tags/RELEASE-0_2_12)       (revision 277)
++++ collect_diffs.rb   (.../trunk)     (revision 277)
+@@ -26,7 +26,18 @@
+ $tmpdir = ENV["TMPDIR"] || "/tmp"
  $dirtemplate = "#cvsspam.#{Process.getpgrp}.#{Process.uid}"
  
++def shell_mask2regex(mask)
++  '^' + mask.gsub('.', '\.').gsub('?', '.').gsub('*', '.*') + '$'
++end
++
  def find_data_dir
 +  if $from_address
 +    safe_from = make_fromaddr_safe_for_filename($from_address)
@@ -70,7 +153,7 @@ Index: collect_diffs.rb
    Dir["#{$tmpdir}/#{$dirtemplate}-*"].each do |dir|
      stat = File.stat(dir)
      return dir if stat.owned?
-@@ -35,6 +42,14 @@
+@@ -35,6 +46,14 @@
  end
  
  
@@ -85,7 +168,24 @@ Index: collect_diffs.rb
  def blah(msg)
    if $debug
      $stderr.puts "collect_diffs.rb: #{msg}"
-@@ -129,7 +144,14 @@
+@@ -81,11 +100,11 @@
+     File.open("#{$datadir}/commitinfo-tags") do |file|
+       $commitinfo_tags = Hash.new
+       file.each_line do |line|
+-      line =~ /([^\t]+)\t(.+)/
+-      key = $2
+-      val = $1
+-      key.sub!(/^#{ENV['CVSROOT']}\//, '')
+-      $commitinfo_tags[key] = val
++        line =~ /([^\t]+)\t(.+)/
++        key = $2
++        val = $1
++        key.sub!(/^#{ENV['CVSROOT']}\//, '')
++        $commitinfo_tags[key] = val
+       end
+     end
+   end
+@@ -129,7 +148,14 @@
    changes = Array.new
    i = 0
    while i < cvs_info.length
@@ -101,7 +201,43 @@ Index: collect_diffs.rb
      i+=1
    end
    return changes
-@@ -222,6 +244,7 @@
+@@ -194,6 +220,8 @@
+     changes.each do |change|
++      next if $ignore_file_regexes and $ignore_file_regexes.any?{|r| change.file =~ /#{r}/}
++
+       # record version information
+       file.puts "#V #{change.fromVer},#{change.toVer}"
+@@ -202,7 +230,7 @@
+       # note if the file is on a branch
+       tag = nil
+       if change.isRemoval
+-      tag = get_commitinfo_tag("#{$repository_path}/#{change.file}")
++        tag = get_commitinfo_tag("#{$repository_path}/#{change.file}")
+       else
+         status = nil
+         safer_popen($cvs_prog, "-nq", "status", change.file) do |io|
+@@ -210,18 +238,19 @@
+         end
+         fail "couldn't get cvs status: #{$!} (exited with #{$?})" unless ($?>>8)==0
+-      if status =~ /^\s*Sticky Tag:\s*(.+) \(branch: +/m
+-        tag = $1
+-      end
++        if status =~ /^\s*Sticky Tag:\s*(.+) \(branch: +/m
++          tag = $1
++        end
+-      if status =~ /^\s*Sticky Options:\s*-kb/m
+-        binary_file = true
+-      end
++        if status =~ /^\s*Sticky Options:\s*-kb/m
++          binary_file = true
++        end
+       end
+       file.puts "#T #{tag}" unless tag.nil?
  
        diff_cmd = Array.new << $cvs_prog << "-nq" << "diff" << "-Nu"
        diff_cmd << "-kk" if $diff_ignore_keywords
@@ -109,7 +245,49 @@ Index: collect_diffs.rb
  
        if change.isAddition
          file.write "#A "
-@@ -333,9 +356,11 @@
+@@ -240,24 +269,24 @@
+       file.puts "#{$repository_path}/#{change.file}"
+       diff_cmd << change.file
+       if binary_file
+-              blah("not diffing #{change.file}; has -kb set")
+-      # fake diff lines that will cause cvsspam.rb to consider this a binary
+-      # file,
+-      file.puts "#U diff x x"
+-      file.puts "#U Binary files x and y differ"
++        blah("not diffing #{change.file}; has -kb set")
++        # fake diff lines that will cause cvsspam.rb to consider this a binary
++        # file,
++        file.puts "#U diff x x"
++        file.puts "#U Binary files x and y differ"
+       else
+-      # do a cvs diff and place the output into our temp file
+-      blah("about to run #{diff_cmd.join(' ')}")
+-      safer_popen(*diff_cmd) do |pipe|
+-        # skip over cvs-diff's preamble
+-        pipe.each do |line|
+-          break if line =~ /^diff /
+-        end
+-        file.puts "#U #{line}"
+-        pipe.each do |line|
+-          file.puts "#U #{line}"
+-        end
+-      end
++        # do a cvs diff and place the output into our temp file
++        blah("about to run #{diff_cmd.join(' ')}")
++        safer_popen(*diff_cmd) do |pipe|
++          # skip over cvs-diff's preamble
++          pipe.each do |line|
++            break if line =~ /^diff /
++          end
++          file.puts "#U #{line}"
++          pipe.each do |line|
++            file.puts "#U #{line}"
++          end
++        end
+       end
+       # TODO: don't how to do this reliably on different systems...
+       #fail "cvsdiff did not give exit status 1 for invocation: #{diff_cmd.join(' ')}" unless ($?>>8)==1
+@@ -333,10 +362,13 @@
  end
  
  $config = nil
@@ -119,9 +297,11 @@ Index: collect_diffs.rb
  $diff_ignore_keywords = false
 +$diff_ignore_whitespace = false
  $task_keywords = []
++$ignore_file_regexes = nil
  
  unless ENV.has_key?('CVSROOT')
-@@ -387,6 +412,7 @@
+   fail "$CVSROOT not defined.  It should be when I am invoked from CVSROOT/loginfo"
+@@ -387,6 +419,7 @@
    end
    $config = arg if opt=="--config"
    $debug = true if opt == "--debug"
@@ -129,10 +309,26 @@ Index: collect_diffs.rb
  end
  
  blah("CVSROOT is #{ENV['CVSROOT']}")
+@@ -426,6 +459,9 @@
+     class GUESS
+     end
+     load $config
++    if $ignore_files
++      $ignore_file_regexes = $ignore_files.split(/\s+/).map{|i| shell_mask2regex(i)}
++    end
+   else
+     blah("Config file '#{$config}' not found, ignoring")
+   end
+@@ -447,3 +483,5 @@
+   process_log(ARGV[0])
+ end
+ mailtest
++
++# vim:et:ts=2:sw=2
 Index: record_lastdir.rb
 ===================================================================
---- record_lastdir.rb  (revision 223)
-+++ record_lastdir.rb  (revision 253)
+--- record_lastdir.rb  (.../tags/RELEASE-0_2_12)       (revision 277)
++++ record_lastdir.rb  (.../trunk)     (revision 277)
 @@ -4,7 +4,6 @@
  #   http://www.badgers-in-foil.co.uk/projects/cvsspam/
  # Copyright (c) David Holroyd
@@ -178,12 +374,457 @@ Index: record_lastdir.rb
  $datadir = find_data_dir()
  
  if $datadir==nil
+@@ -78,5 +107,7 @@
+ # email yet.
+ File.open("#{$datadir}/lastdir", "w") { |file|
+-      file.write $repositorydir
++  file.write $repositorydir
+ }
++
++# vim:et:ts=2:sw=2
+
+Property changes on: TODO
+___________________________________________________________________
+Deleted: svn:executable
+   - *
+
 Index: project.xml
 ===================================================================
+Index: svn_post_commit_hook.rb
+===================================================================
+--- svn_post_commit_hook.rb    (.../tags/RELEASE-0_2_12)       (revision 0)
++++ svn_post_commit_hook.rb    (.../trunk)     (revision 277)
+@@ -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
++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 #{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 "#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 #{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 "#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 #{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 #{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 #{from_file}:#{prev_rev},#{next_rev}"
++  out.puts "#C #{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 #{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
++
++# vim:et:ts=2:sw=2
+
+Property changes on: svn_post_commit_hook.rb
+___________________________________________________________________
+Added: svn:mergeinfo
+Added: svn:executable
+   + *
+
+
+Property changes on: COPYING
+___________________________________________________________________
+Deleted: svn:executable
+   - *
+
 Index: CREDITS
 ===================================================================
---- CREDITS    (revision 223)
-+++ CREDITS    (revision 253)
+--- CREDITS    (.../tags/RELEASE-0_2_12)       (revision 277)
++++ CREDITS    (.../trunk)     (revision 277)
 @@ -29,3 +29,10 @@
    Elan Ruusamäe
    Steve Fox
@@ -197,8 +838,8 @@ Index: CREDITS
 +  Charles Duffy
 Index: cvsspam-doc.xml
 ===================================================================
---- cvsspam-doc.xml    (revision 223)
-+++ cvsspam-doc.xml    (revision 253)
+--- cvsspam-doc.xml    (.../tags/RELEASE-0_2_12)       (revision 277)
++++ cvsspam-doc.xml    (.../trunk)     (revision 277)
 @@ -452,6 +452,23 @@
  </screen></informalexample>
        </para>
@@ -223,11 +864,17 @@ Index: cvsspam-doc.xml
  </section>
  
  <section><title>CVS Web Frontends</title>
+
+Property changes on: cvsspam-doc.xml
+___________________________________________________________________
+Deleted: svn:executable
+   - *
+
 Index: cvsspam.rb
 ===================================================================
---- cvsspam.rb (revision 223)
-+++ cvsspam.rb (revision 253)
-@@ -20,6 +20,7 @@
+--- cvsspam.rb (.../tags/RELEASE-0_2_12)       (revision 277)
++++ cvsspam.rb (.../trunk)     (revision 277)
+@@ -20,11 +20,14 @@
  
  $version = "0.2.12"
  
@@ -235,7 +882,16 @@ Index: cvsspam.rb
  
  $maxSubjectLength = 200
  $maxLinesPerDiff = 1000
-@@ -35,10 +36,6 @@
+-$maxDiffLineLength = 1000     # may be set to nil for no limit
+-$charset = nil                        # nil implies 'don't specify a charset'
++# may be set to nil for no limit
++$maxDiffLineLength = 1000
++# nil implies 'don't specify a charset'
++$charset = nil
+ $mailSubject = ''
+ def blah(text)
+@@ -35,10 +38,6 @@
    a<b ? a : b
  end
  
@@ -246,7 +902,16 @@ Index: cvsspam.rb
  # Perform (possibly) multiple global substitutions on a string.
  # the regexps given as keys must not use capturing subexpressions '(...)'
  class MultiSub
-@@ -116,6 +113,8 @@
+@@ -48,7 +47,7 @@
+     @mash = Array.new
+     expr = nil
+     hash.each do |key,val|
+-      if expr == nil ; expr="(" else expr<<"|(" end
++      if expr == nil ; expr="(" else expr << "|(" end
+       expr << key << ")"
+       @mash << val
+     end
+@@ -116,6 +115,8 @@
    UNDERSCORE = chr("_")
    SPACE = chr(" ")
    TAB = chr("\t")
@@ -255,16 +920,30 @@ Index: cvsspam.rb
  
    # encode a header value according to the RFC-2047 quoted-printable spec,
    # allowing non-ASCII characters to appear in header values, and wrapping
-@@ -137,7 +136,7 @@
+@@ -137,8 +138,8 @@
    # return a string representing the given character-code in quoted-printable
    # format
    def quoted_encode_char(b)
 -    if b>126 || b==UNDERSCORE || b==TAB
+-      sprintf("=%02x", b)
 +    if b>126 || b==UNDERSCORE || b==TAB || b==HOOK || b==EQUALS
-       sprintf("=%02x", b)
++      sprintf("=%02X", b)
      elsif b == SPACE
        "_"
-@@ -388,6 +387,7 @@
+     else
+@@ -163,8 +164,9 @@
+   # gives a string starting "=?", and including a charset specification, that
+   # marks the start of a quoted-printable character sequence
+-  def marker_start_quoted
+-    "=?#{@charset}?#{@encoding}?"
++  def marker_start_quoted(charset=nil)
++    charset = @charset if charset.nil?
++    "=?#{charset}?#{@encoding}?"
+   end
+   # test to see of the given string contains non-ASCII characters
+@@ -388,6 +390,7 @@
  class FileEntry
    def initialize(path)
      @path = path
@@ -272,7 +951,46 @@ Index: cvsspam.rb
      @lineAdditions = @lineRemovals = 0
      @repository = Repository.get(path)
      @repository.merge_common_prefix(basedir())
-@@ -533,6 +533,14 @@
+@@ -397,7 +400,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
+@@ -452,17 +455,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
+@@ -533,6 +547,14 @@
  # TODO: consolidate these into a nicer framework,
  mailSub = proc { |match| "<a href=\"mailto:#{match}\">#{match}</a>" }
  urlSub = proc { |match| "<a href=\"#{match}\">#{match}</a>" }
@@ -287,7 +1005,7 @@ Index: cvsspam.rb
  bugzillaSub = proc { |match|
    match =~ /([0-9]+)/
    "<a href=\"#{$bugzillaURL.sub(/%s/, $1)}\">#{match}</a>"
-@@ -544,11 +552,27 @@
+@@ -544,15 +566,31 @@
    match =~ /([0-9]+)/
    "<a href=\"#{$ticketURL.sub(/%s/, $1)}\">#{match}</a>"
  }
@@ -314,9 +1032,16 @@ Index: cvsspam.rb
 +  "<a href=\"#{$xplannerStoryURL.sub(/%s/, $1)}\">#{match}</a>"
 +}
  commentSubstitutions = {
-               '(?:mailto:)?[\w\.\-\+\=]+\@[\w\-]+(?:\.[\w\-]+)+\b' => mailSub,
-               '\b(?:http|https|ftp):[^ \t\n<>"]+[\w/]' => urlSub
-@@ -670,6 +694,12 @@
+-              '(?:mailto:)?[\w\.\-\+\=]+\@[\w\-]+(?:\.[\w\-]+)+\b' => mailSub,
+-              '\b(?:http|https|ftp):[^ \t\n<>"]+[\w/]' => urlSub
+-              }
++  '(?:mailto:)?[\w\.\-\+\=]+\@[\w\-]+(?:\.[\w\-]+)+\b' => mailSub,
++  '\b(?:http|https|ftp):[^ \t\n<>"]+[\w/]' => urlSub
++}
+ # outputs commit log comment text supplied by LogReader as preformatted HTML
+ class CommentHandler < LineConsumer
+@@ -670,6 +708,12 @@
    def diff(file)
      '-&gt;'
    end
@@ -329,7 +1054,7 @@ Index: cvsspam.rb
  end
  
  # Superclass for objects that can link to CVS frontends on the web (ViewCVS,
-@@ -710,6 +740,14 @@
+@@ -710,6 +754,14 @@
      "<a href=\"#{diff_url(file)}\">#{super(file)}</a>"
    end
  
@@ -344,7 +1069,7 @@ Index: cvsspam.rb
   protected
    def add_repo(url)
      if @repository_name
-@@ -722,6 +760,10 @@
+@@ -722,6 +774,10 @@
        url
      end
    end
@@ -355,7 +1080,7 @@ Index: cvsspam.rb
  end
  
  # Link to ViewCVS
-@@ -745,6 +787,15 @@
+@@ -745,6 +801,15 @@
    def diff_url(file)
      add_repo("#{@base_url}#{urlEncode(file.path)}.diff?r1=#{file.fromVer}&amp;r2=#{file.toVer}")
    end
@@ -371,7 +1096,7 @@ Index: cvsspam.rb
  end
  
  # Link to Chora, from the Horde framework
-@@ -767,9 +818,9 @@
+@@ -767,9 +832,9 @@
  class CVSwebFrontend < WebFrontend
    def path_url(path, tag)
      if tag == nil
@@ -383,7 +1108,7 @@ Index: cvsspam.rb
      end
    end
  
-@@ -780,6 +831,17 @@
+@@ -780,9 +845,45 @@
    def diff_url(file)
      add_repo("#{@base_url}#{urlEncode(file.path)}.diff?r1=text&amp;tr1=#{file.fromVer}&amp;r2=text&amp;tr2=#{file.toVer}&amp;f=h")
    end
@@ -400,8 +1125,90 @@ Index: cvsspam.rb
 +  end
  end
  
++# Link to Trac
++class TracFrontend < WebFrontend
++  def path_url(path, tag)
++    add_repo("#{@base_url}browser/#{urlEncode(path)}")
++  end
++  def version_url(path, version)
++    add_repo("#{@base_url}browser/#{urlEncode(path)}?rev=#{version}")
++  end
++
++  def diff_url(file)
++    add_repo("#{@base_url}changeset/#{file.toVer}")
++  end
++
++  protected
++
++  def log_url(file)
++    if file.toVer
++      log_anchor = "?rev=#{file.toVer}"
++    else
++      log_anchor = ""
++    end
++    add_repo("#{@base_url}log/#{urlEncode(file.path)}#{log_anchor}")
++  end
++end
++
+ # in need of refactoring...
+ # Note when LogReader finds record of a file that was added in this commit
+@@ -801,6 +902,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)
+@@ -810,7 +920,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
  
-@@ -958,7 +1020,7 @@
++
+ # Used by UnifiedDiffHandler to record the number of added and removed lines
+ # appearing in a unidiff.
+ class UnifiedDiffStats
+@@ -873,7 +992,10 @@
+         addInfixSize = line.length - (prefixLen+suffixLen)
+         oversize_change = deleteInfixSize*100/@lineJustDeleted.length>33 || addInfixSize*100/line.length>33
+-        if prefixLen==1 && suffixLen==0 || deleteInfixSize<=0 || oversize_change
++        # avoid doing 'within-a-line highlighting' if a multibyte encoding
++        # is suspected, as all the suffix/prefix stuff above is byte, not
++        # character based
++        if multibyte_encoding? || prefixLen==1 && suffixLen==0 || deleteInfixSize<=0 || oversize_change
+           print(htmlEncode(@lineJustDeleted))
+         else
+           print(htmlEncode(@lineJustDeleted[0,prefixLen]))
+@@ -905,7 +1027,7 @@
+         @lineJustDeleted = nil
+       end
+       shift(initial)
+-      if prefixLen==1 && suffixLen==0 || addInfixSize<=0 || oversize_change
++      if multibyte_encoding? || prefixLen==1 && suffixLen==0 || addInfixSize<=0 || oversize_change
+         encoded = htmlEncode(line)
+       else
+         encoded = htmlEncode(line[0,prefixLen]) +
+@@ -958,7 +1080,7 @@
      end
      shift(nil)
      if @truncatedLineCount>0
@@ -410,9 +1217,95 @@ Index: cvsspam.rb
      end
    end
  
-@@ -1247,10 +1309,16 @@
+@@ -976,11 +1098,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|
+@@ -1078,7 +1210,7 @@
+           @colour.teardown
+         end
+         println("</div>") # end of "file" div
+-      $file.has_diff = true
++        $file.has_diff = true
+       end
+     end
+   end
+@@ -1181,7 +1313,7 @@
+ # an RFC 822 email address
+ class EmailAddress
+-  def initialize(text)
++  def initialize(text, charset=nil)
+     if text =~ /^\s*([^<]+?)\s*<\s*([^>]+?)\s*>\s*$/
+       @personal_name = $1
+       @address = $2
+@@ -1189,9 +1321,10 @@
+       @personal_name = nil
+       @address = text
+     end
++    @charset=charset
+   end
+-  attr_accessor :personal_name, :address
++  attr_accessor :personal_name, :address, :charset
+   def has_personal_name?
+     return !@personal_name.nil?
+@@ -1222,9 +1355,9 @@
+   # rfc2047 encode the word, if it contains non-ASCII characters
+   def encode_word(word)
+     if $encoder.requires_rfc2047?(word)
+-      encoded = $encoder.marker_start_quoted
++      encoded = $encoder.marker_start_quoted(@charset)
+       $encoder.each_char_encoded(word) do |code|
+-      encoded << code
++        encoded << code
+       end
+       encoded << $encoder.marker_end_quoted
+       return encoded
+@@ -1233,26 +1366,40 @@
+   end
+ end
++# guess if the users selected encoding is multibyte, since some CVSspam code
++# isn't multibyte-safe, and needs to be disabled.
++def multibyte_encoding?
++  $charset && ["utf-8", "utf-16"].include?($charset.downcase)
++end
+ cvsroot_dir = "#{ENV['CVSROOT']}/CVSROOT"
+ $config = "#{cvsroot_dir}/cvsspam.conf"
+ $users_file = "#{cvsroot_dir}/users"
++$users_file_charset = nil
+ $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']
++$task_keywords = ['TODO', 'FIXME', 'FIXIT', 'todo']
  $bugzillaURL = nil
 +$gforgeBugURL = nil
 +$gforgeTaskURL = nil
@@ -426,8 +1319,11 @@ Index: cvsspam.rb
 +$xplannerStoryURL = nil
  $choraURL = nil
  $cvswebURL = nil
++$tracURL = nil
  $from_address = nil
-@@ -1261,6 +1329,7 @@
+ $subjectPrefix = nil
+ $files_in_subject = false;
+@@ -1261,6 +1408,7 @@
  # 2MiB limit on attached diffs,
  $mail_size_limit = 1024 * 1024 * 2
  $arg_charset = nil
@@ -435,12 +1331,56 @@ Index: cvsspam.rb
  
  require 'getoptlong'
  
-@@ -1353,17 +1422,35 @@
+@@ -1268,6 +1416,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 ]
+ )
+@@ -1276,6 +1425,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"
+@@ -1288,7 +1438,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
+@@ -1321,6 +1471,8 @@
+   blah("Config file '#{$config}' not found, ignoring")
+ end
++blah("Users file: '#{$users_file}'")
++
+ unless $arg_charset.nil?
+   $charset = $arg_charset
+ end
+@@ -1337,6 +1489,9 @@
+ elsif $cvswebURL !=nil
+   $cvswebURL << "/" unless $cvswebURL =~ /\/$/
+   $frontend = CVSwebFrontend.new($cvswebURL)
++elsif $tracURL !=nil
++  $tracURL << "/" unless $tracURL =~ /\/$/
++  $frontend = TracFrontend.new($tracURL)
+ else
+   $frontend = NoFrontend.new
+ end
+@@ -1353,33 +1508,57 @@
  
  
  if $bugzillaURL != nil
 -  commentSubstitutions['\b[Bb][Uu][Gg]\s*#?[0-9]+'] = bugzillaSub
-+  commentSubstitutions['\b[Bb]([Uu][Gg])?\s*[#:]?\s*\[?[0-9]+\]?'] = bugzillaSub
++  commentSubstitutions['\b[Bb](?:[Uu][Gg])?\s*[#:]?\s*\[?[0-9]+\]?'] = bugzillaSub
  end
 +if $gforgeBugURL != nil
 +  commentSubstitutions['\B\[#[0-9]+\]'] = gforgeBugSub
@@ -472,9 +1412,124 @@ Index: cvsspam.rb
  $commentEncoder = MultiSub.new(commentSubstitutions)
  
  
-@@ -1546,11 +1633,14 @@
+ tagHandler = TagHandler.new
+-$handlers = Hash[">" => CommentHandler.new,
+-               "U" => UnifiedDiffHandler.new,
+-               "T" => tagHandler,
+-               "A" => AddedFileHandler.new,
+-               "R" => RemovedFileHandler.new,
+-               "M" => ModifiedFileHandler.new,
+-               "V" => VersionHandler.new]
++$handlers = Hash[
++  ">" => CommentHandler.new,
++  "U" => UnifiedDiffHandler.new,
++  "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
+@@ -1404,7 +1583,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
+@@ -1451,6 +1634,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;}
+@@ -1483,7 +1668,9 @@
+   filesAdded = 0
+   filesRemoved = 0
++  filesCopied = 0
+   filesModified  = 0
++  filesModifiedProps  = 0
+   totalLinesAdded = 0
+   totalLinesRemoved = 0
+   file_count = 0
+@@ -1492,24 +1679,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 ? ", " : " &amp; "
++      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 ? ", " : " &amp; "
++            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>")
+@@ -1524,8 +1713,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("/")
+@@ -1545,17 +1738,29 @@
+       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?
@@ -484,12 +1539,94 @@ Index: cvsspam.rb
 -      mail.print("<td><tt>#{prefix}#{name}</tt></td>")
 +      mail.print("<tt>#{prefix}#{name}</tt>")
      end
+-    if file.isEmpty
+-      mail.print("<td colspan=\"2\" align=\"center\"><small id=\"info\">[empty]</small></td>")
 +    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
-@@ -1686,6 +1776,8 @@
+-      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>")
+@@ -1582,15 +1787,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
+@@ -1607,7 +1816,7 @@
+   
+   mail.puts("</table>")
+-  totalFilesChanged = filesAdded+filesRemoved+filesModified
++  totalFilesChanged = filesAdded+filesRemoved+filesCopied+filesModified+filesModifiedProps
+   if totalFilesChanged > 1
+     mail.print("<small id=\"info\">")
+     changeKind = 0
+@@ -1620,11 +1829,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
+@@ -1667,12 +1886,13 @@
+ # CVSROOT/users file, if the file exists.  The argument is returned unchanged
+ # if no alias is found.
+ def sender_alias(email)
++  blah("Lookup '#{email}' from users file")
+   if File.exists?($users_file)
+     File.open($users_file) do |io|
+       io.each_line do |line|
+         if line =~ /^([^:]+)\s*:\s*(['"]?)([^\n\r]+)(\2)/
+           if email.address == $1
+-            return EmailAddress.new($3)
++            return EmailAddress.new($3, $users_file_charset)
+           end
+         end
+       end
+@@ -1686,6 +1906,8 @@
  # sensible header formatting, and for ensuring that the body is seperated
  # from the message headers by a blank line (as it is required to be).
  class MailContext
@@ -498,7 +1635,7 @@ Index: cvsspam.rb
    def initialize(io)
      @done_headers = false
      @io = io
-@@ -1695,8 +1787,8 @@
+@@ -1695,8 +1917,8 @@
    # called
    def header(name, value)
      raise "headers already commited" if @done_headers
@@ -509,7 +1646,7 @@ Index: cvsspam.rb
      else
        @io.puts("#{name}: #{value}")
      end
-@@ -1769,7 +1861,7 @@
+@@ -1769,7 +1991,7 @@
        ctx.header("To", recipients.map{|addr| addr.encoded}.join(','))
        blah("Mail From: <#{from}>")
        ctx.header("From", from.encoded) if from
@@ -518,7 +1655,7 @@ Index: cvsspam.rb
        yield ctx
      end
    end
-@@ -1800,10 +1892,10 @@
+@@ -1800,10 +2022,10 @@
    return unless $fileEntries.length == 1
    file = $fileEntries[0]
    name = zap_header_special_chars(file.path)
@@ -531,7 +1668,7 @@ Index: cvsspam.rb
      mail.header("Message-ID", make_msg_id("#{name}.#{file.toVer}", $hostname))
    end
  end
-@@ -1834,6 +1926,14 @@
+@@ -1834,8 +2056,18 @@
      end
    end
    mail.header("X-Mailer", "CVSspam #{$version} <http://www.badgers-in-foil.co.uk/projects/cvsspam/>")
@@ -546,3 +1683,44 @@ Index: cvsspam.rb
  
    mail.body do |body|
      make_html_email(body)
+   end
+ end
++
++# vim:et:ts=2:sw=2
+
+Property changes on: testcases/data/remove.png
+___________________________________________________________________
+Deleted: svn:executable
+   - *
+
+
+Property changes on: testcases/data/fiddlyedits.after
+___________________________________________________________________
+Deleted: svn:executable
+   - *
+
+
+Property changes on: testcases/data/fiddlyedits.before
+___________________________________________________________________
+Deleted: svn:executable
+   - *
+
+
+Property changes on: testcases/data/add.png
+___________________________________________________________________
+Deleted: svn:executable
+   - *
+
+
+Property changes on: testcases/README
+___________________________________________________________________
+Deleted: svn:executable
+   - *
+
+
+Property changes on: .
+___________________________________________________________________
+Added: svn:ignore
+   + .project
+
+
This page took 0.111699 seconds and 4 git commands to generate.