]> git.pld-linux.org Git - packages/cvsspam.git/blob - cvsspam-svnspam-branch.diff
- up to r269
[packages/cvsspam.git] / cvsspam-svnspam-branch.diff
1 --- svn_post_commit_hook.rb     (.../trunk)     (revision 0)
2 +++ svn_post_commit_hook.rb     (.../branches/svn_support)      (revision 269)
3 @@ -0,0 +1,412 @@
4 +#!/usr/bin/ruby -w
5 +
6 +$svnlook_exe = "svnlook"  # default assumes the program is in $PATH
7 +
8 +def usage(msg)
9 +  $stderr.puts(msg)
10 +  exit(1)
11 +end
12 +
13 +def blah(msg)
14 +  if $debug
15 +    $stderr.puts "svn_post_commit_hook.rb: #{msg}"
16 +  end
17 +end
18 +
19 +$debug = false
20 +$tmpdir = ENV["TMPDIR"] || "/tmp"
21 +$dirtemplate = "#svnspam.#{Process.getpgrp}.#{Process.uid}"
22 +# arguments to pass though to 'cvsspam.rb'
23 +$passthrough_args = []
24 +
25 +def make_data_dir
26 +  dir = "#{$tmpdir}/#{$dirtemplate}-#{rand(99999999)}"
27 +  Dir.mkdir(dir, 0700)
28 +  dir
29 +end
30 +
31 +def init
32 +  $datadir = make_data_dir
33 +
34 +  # set PWD so that svnlook can create its .svnlook directory
35 +  Dir.chdir($datadir)
36 +end
37 +
38 +def cleanup
39 +  unless $debug
40 +         File.unlink("#{$datadir}/logfile")
41 +         Dir.rmdir($datadir)
42 +  end
43 +end
44 +
45 +def send_email
46 +  cmd = File.dirname($0) + "/cvsspam.rb"
47 +  unless system(cmd,"--svn","#{$datadir}/logfile", *$passthrough_args)
48 +    fail "problem running '#{cmd}'"
49 +  end
50 +end
51 +
52 +# Like IO.popen, but accepts multiple arguments like Kernel.exec
53 +# (So no need to escape shell metacharacters)
54 +def safer_popen(*args)
55 +  IO.popen("-") do |pipe|
56 +    if pipe==nil
57 +      exec(*args)
58 +    else
59 +      yield pipe
60 +    end
61 +  end
62 +end
63 +
64 +
65 +# Process the command-line arguments in the given list
66 +def process_args
67 +  require 'getoptlong'
68 +
69 +  opts = GetoptLong.new(
70 +    [ "--to",     "-t", GetoptLong::REQUIRED_ARGUMENT ],
71 +    [ "--config", "-c", GetoptLong::REQUIRED_ARGUMENT ],
72 +    [ "--debug",  "-d", GetoptLong::NO_ARGUMENT ],
73 +    [ "--from",   "-u", GetoptLong::REQUIRED_ARGUMENT ],
74 +    [ "--charset",      GetoptLong::REQUIRED_ARGUMENT ]
75 +  )
76 +
77 +  opts.each do |opt, arg|
78 +    if ["--to", "--config", "--from", "--charset"].include?(opt)
79 +      $passthrough_args << opt << arg
80 +    end
81 +    if ["--debug"].include?(opt)
82 +      $passthrough_args << opt
83 +    end
84 +    $config = arg if opt=="--config"
85 +    $debug = true if opt == "--debug"
86 +  end
87 +
88 +  $repository = ARGV[0]
89 +  $revision = ARGV[1]
90 +
91 +  unless $revision =~ /^\d+$/
92 +    usage("revision must be an integer: #{revision.inspect}")
93 +  end
94 +  $revision = $revision.to_i
95 +
96 +  unless FileTest.directory?($repository)
97 +    usage("no such directory: #{$repository.inspect}")
98 +  end
99 +  $repository =~ /([^\/]+$)/ 
100 +  $shortrepo = $1
101 +end
102 +
103 +# runs the given svnlook subcommand
104 +def svnlook(cmd, revision, *args)
105 +  rev = revision.to_s
106 +  safer_popen($svnlook_exe, cmd, $repository, "-r", rev, *args) do |io|
107 +    yield io
108 +  end
109 +end
110 +
111 +class Change
112 +  def initialize(filechange, propchange, path)
113 +    @filechange = filechange
114 +    @propchange = propchange
115 +    @path = path
116 +  end
117 +
118 +  attr_accessor :filechange, :propchange, :path
119 +
120 +  def property_change?
121 +    @propchange != " "
122 +  end
123 +
124 +  def file_change?
125 +    @filechange != "_"
126 +  end
127 +
128 +  def addition?
129 +    @filechange == "A"
130 +  end
131 +
132 +  def deletion?
133 +    @filechange == "D"
134 +  end
135 +end
136 +
137 +
138 +
139 +# Line-oriented access to an underlying IO object.  Remembers 'current' line
140 +# for lookahead during parsing.
141 +class LineReader
142 +  def initialize(io)
143 +    @io = io
144 +  end
145 +
146 +  def current
147 +    @line
148 +  end
149 +
150 +  def next_line
151 +    (@line = @io.gets) != nil
152 +  end
153 +
154 +  def assert_current(re)
155 +    raise "unexpected #{current.inspect}" unless @line =~ re
156 +    $~
157 +  end
158 +
159 +  def assert_next(re=nil)
160 +    raise "unexpected end of text" unless next_line
161 +    unless re.nil?
162 +      raise "unexpected #{current.inspect}" unless @line =~ re
163 +    end
164 +    $~
165 +  end
166 +end
167 +
168 +
169 +def read_modified_diff(out, lines, path)
170 +  lines.assert_next(/^=+$/)
171 +  lines.assert_next
172 +  if lines.current =~ /\(Binary files differ\)/
173 +    process_modified_binary_diff(out, lines, path)
174 +  else
175 +    process_modified_text_diff(out, lines, path)
176 +  end
177 +end
178 +
179 +
180 +def process_modified_binary_diff(out, lines, path)
181 +  prev_rev= $revision-1
182 +  next_rev= $revision
183 +  out.puts "#V #{prev_rev},#{next_rev}"
184 +  out.puts "#M #{$shortrepo}/#{path}"
185 +  out.puts "#U diff x x"
186 +  out.puts "#U Binary files x and y differ"
187 +end
188 +
189 +
190 +def process_modified_text_diff(out, lines, path)
191 +  m = lines.assert_current(/^---.*\(rev (\d+)\)$/)
192 +  prev_rev = m[1].to_i
193 +  diff1 = lines.current
194 +  m = lines.assert_next(/^\+\+\+.*\(rev (\d+)\)$/)
195 +  next_rev = m[1].to_i
196 +  diff2 = lines.current
197 +  out.puts "#V #{prev_rev},#{next_rev}"
198 +  out.puts "#M #{$shortrepo}/#{path}"
199 +  out.puts "#U #{diff1}"
200 +  out.puts "#U #{diff2}"
201 +  while lines.next_line && lines.current =~ /^[-\+ @\\]/
202 +    out.puts "#U #{lines.current}"
203 +  end
204 +end
205 +
206 +def read_added_diff(out, lines, path)
207 +  lines.assert_next(/^=+$/)
208 +  lines.assert_next
209 +  if lines.current =~ /\(Binary files differ\)/
210 +    process_added_binary_diff(out, lines, path)
211 +  else
212 +    process_added_text_diff(out, lines, path)
213 +  end
214 +end
215 +
216 +def process_added_binary_diff(out, lines, path)
217 +  next_rev= $revision
218 +  out.puts "#V NONE,#{next_rev}"
219 +  out.puts "#A #{$shortrepo}/#{path}"
220 +  out.puts "#U diff x x"
221 +  out.puts "#U Binary file x added"
222 +end
223 +
224 +def process_added_text_diff(out, lines, path)
225 +  m = lines.assert_current(/^---.*\(rev (\d+)\)$/)
226 +  prev_rev = m[1].to_i
227 +  diff1 = lines.current
228 +  m = lines.assert_next(/^\+\+\+.*\(rev (\d+)\)$/)
229 +  next_rev = m[1].to_i
230 +  diff2 = lines.current
231 +  out.puts "#V NONE,#{next_rev}"
232 +  out.puts "#A #{$shortrepo}/#{path}"
233 +  out.puts "#U #{diff1}"
234 +  out.puts "#U #{diff2}"
235 +  while lines.next_line && lines.current =~ /^[-\+ @\\]/
236 +    out.puts "#U #{lines.current}"
237 +  end
238 +end
239 +
240 +def read_deleted_diff(out, lines, path)
241 +  lines.assert_next(/^=+$/)
242 +  m = lines.assert_next(/^---.*\(rev (\d+)\)$/)
243 +  prev_rev = m[1].to_i
244 +  diff1 = lines.current
245 +  m = lines.assert_next(/^\+\+\+.*\(rev (\d+)\)$/)
246 +  next_rev = m[1].to_i
247 +  diff2 = lines.current
248 +  out.puts "#V #{prev_rev},NONE"
249 +  out.puts "#R #{$shortrepo}/#{path}"
250 +  out.puts "#U #{diff1}"
251 +  out.puts "#U #{diff2}"
252 +  while lines.next_line && lines.current =~ /^[-\+ @\\]/
253 +    out.puts "#U #{lines.current}"
254 +  end
255 +end
256 +
257 +def read_property_lines(path, prop_name, revision)
258 +  lines = []
259 +  svnlook("propget", revision, prop_name, path) do |io|
260 +    io.each_line do |line|
261 +      lines << line.chomp
262 +    end
263 +  end
264 +  lines
265 +end
266 +
267 +def assert_prop_match(a, b)
268 +  if !b.nil? && a != b
269 +    raise "property mismatch: #{a.inspect}!=#{b.inspect}"
270 +  end
271 +end
272 +
273 +# We need to read the property change from the output of svnlook, but have
274 +# a difficulty in that there's no unambiguous delimiter marking the end of
275 +# a potentially multi-line property value.  Therefore, we do a seperate
276 +# svn propget on the given file to get the value of the property on its own,
277 +# and then use that value as a guide as to how much data to read from the
278 +# svnlook output.
279 +def munch_prop_text(path, prop_name, revision, lines, line0)
280 +  prop = read_property_lines(path, prop_name, revision)
281 +  if prop.empty?
282 +    assert_prop_match(line0, "")
283 +    return
284 +  end
285 +  assert_prop_match(line0, prop.shift)
286 +  prop.each do |prop_line|
287 +    lines.assert_next
288 +    assert_prop_match(lines.current.chomp, prop_line)
289 +  end
290 +end
291 +
292 +def read_properties_changed(out, lines, path)
293 +  prev_rev= $revision-1
294 +  next_rev= $revision
295 +  lines.assert_next(/^_+$/)
296 +  return unless lines.next_line
297 +  out.puts "#V #{prev_rev},#{next_rev}"
298 +  out.puts "#P #{$shortrepo}/#{path}"
299 +# The first three get consumed and not highlighted
300 +  out.puts "#U "
301 +  out.puts "#U Property changes:"
302 +  out.puts "#U "
303 +
304 +  while true
305 +    break unless lines.current =~ /^(?:Name|Added|Deleted): (.+)$/
306 +
307 +    prop_name = $1
308 +    m = lines.assert_next(/^   ([-+]) (.*)/)
309 +    op = m[1]
310 +    line0 = m[2]
311 +    if op == "-"
312 +      munch_prop_text(path, prop_name, $revision-1, lines, line0)
313 +      if lines.next_line && lines.current =~ /^   \+ (.*)/
314 +       munch_prop_text(path, prop_name, $revision, lines, $1)
315 +       lines.next_line
316 +      end
317 +    else  # op == "+"
318 +      munch_prop_text(path, prop_name, $revision, lines, line0)
319 +      lines.next_line
320 +    end
321 +    out.puts "#U #{m[1]} #{prop_name}:#{m[2]}"
322 +  end
323 +  out.puts "#U "
324 +end
325 +
326 +def handle_copy(out, lines, path, from_ref, from_file)
327 +  prev_rev= $revision-1
328 +  next_rev= $revision
329 +  out.puts "#V #{$shortrepo}/#{from_file}:#{prev_rev},#{next_rev}"
330 +  out.puts "#C #{$shortrepo}/#{path}"
331 +  if lines.next_line && lines.current =~ /^=+$/
332 +    m = lines.assert_next(/^---.*\(rev (\d+)\)$/)
333 +    prev_rev = m[1].to_i
334 +    diff1 = lines.current
335 +    m = lines.assert_next(/^\+\+\+.*\(rev (\d+)\)$/)
336 +    next_rev = m[1].to_i
337 +    diff2 = lines.current
338 +    out.puts "#U #{diff1}"
339 +    out.puts "#U #{diff2}"
340 +    while lines.next_line && lines.current =~ /^[-\+ @\\]/
341 +      out.puts "#U #{lines.current}"
342 +    end
343 +  else
344 +    out.puts "#U "
345 +    out.puts "#U Copied from #{$shortrepo}/#{from_file}:#{from_ref}"
346 +    out.puts "#U "
347 +  end
348 +end
349 +
350 +def svnlook_author
351 +  svnlook("author", $revision) do |io|
352 +    return io.readline.chomp
353 +  end
354 +  nil
355 +end
356 +
357 +def find_author
358 +  return if $passthrough_args.include?("--from")
359 +  author = svnlook_author
360 +  if author
361 +    blah("Author from svnlook: '#{author}'")
362 +    $passthrough_args << "--from" << author
363 +  end
364 +end
365 +
366 +def process_svnlook_log(file)
367 +  svnlook("log", $revision) do |io|
368 +    io.each_line do |line|
369 +      file.puts("#> #{line}")
370 +    end
371 +  end
372 +end
373 +
374 +def process_svnlook_diff(file)
375 +  svnlook("diff", $revision) do |io|
376 +    lines = LineReader.new(io)
377 +    while lines.next_line
378 +      if lines.current =~ /^Modified:\s+(.*)/
379 +       read_modified_diff(file, lines, $1)
380 +      elsif lines.current =~ /^Added:\s+(.*)/
381 +       read_added_diff(file, lines, $1)
382 +      elsif lines.current =~ /^Copied:\s+(.*) \(from rev (\d+), (.*)\)$/
383 +       handle_copy(file, lines, $1, $2, $3)
384 +      elsif lines.current =~ /^Deleted:\s+(.*)/
385 +       read_deleted_diff(file, lines, $1)
386 +      elsif lines.current =~ /^Property changes on:\s+(.*)/
387 +       read_properties_changed(file, lines, $1)
388 +      elsif lines.current == "\n"
389 +       # ignore
390 +      else
391 +       raise "unable to parse line '#{lines.current.inspect}'"
392 +      end
393 +    end
394 +  end
395 +end
396 +
397 +def process_commit()
398 +  File.open("#{$datadir}/logfile", File::WRONLY|File::CREAT) do |file|
399 +    process_svnlook_log(file)
400 +    process_svnlook_diff(file)
401 +  end
402 +end
403 +
404 +
405 +def main
406 +  init()
407 +  process_args()
408 +  find_author()
409 +  process_commit()
410 +  send_email()
411 +  cleanup()
412 +end
413 +
414 +
415 +main
416 --- cvsspam.rb  (.../trunk)     (revision 269)
417 +++ cvsspam.rb  (.../branches/svn_support)      (revision 269)
418 @@ -398,7 +398,7 @@
419  
420    # the full path and filename within the repository
421    attr_accessor :path
422 -  # the type of change committed 'M'=modified, 'A'=added, 'R'=removed
423 +  # the type of change committed 'M'=modified, 'A'=added, 'R'=removed, 'P'=properties, 'C'=copied
424    attr_accessor :type
425    # records number of 'addition' lines in diff output, once counted
426    attr_accessor :lineAdditions
427 @@ -453,17 +453,28 @@
428    def removal?
429      @type == "R"
430    end
431 -
432 +  
433    # was this file added during the commit?
434    def addition?
435      @type == "A"
436    end
437  
438 +  # was this file copied during the commit?
439 +  def copied?
440 +    @type == "C"
441 +  end
442 +
443    # was this file simply modified during the commit?
444    def modification?
445      @type == "M"
446    end
447 +  
448 +  # was this file simply modified during the commit?
449 +  def modifiedprops?
450 +    @type == "P"
451 +  end
452  
453 +
454    # passing true, this object remembers that a diff will appear in the email,
455    # passing false, this object remembers that no diff will appear in the email.
456    # Once the value is set, it will not be changed
457 @@ -889,6 +900,15 @@
458    end
459  end
460  
461 +# Note when LogReader finds record of a file that was copied in this commit
462 +class CopiedFileHandler < FileHandler
463 +  def handleFile(file)
464 +    file.type="C"
465 +    file.fromVer=$fromVer
466 +    file.toVer=$toVer
467 +  end
468 +end
469 +
470  # Note when LogReader finds record of a file that was modified in this commit
471  class ModifiedFileHandler < FileHandler
472    def handleFile(file)
473 @@ -898,7 +918,16 @@
474    end
475  end
476  
477 +# Note when LogReader finds record of a file whose properties were modified in this commit
478 +class ModifiedPropsFileHandler < FileHandler
479 +  def handleFile(file)
480 +    file.type="P"
481 +    file.fromVer=$fromVer
482 +    file.toVer=$toVer
483 +  end
484 +end
485  
486 +
487  # Used by UnifiedDiffHandler to record the number of added and removed lines
488  # appearing in a unidiff.
489  class UnifiedDiffStats
490 @@ -1064,11 +1093,21 @@
491          print($frontend.path($file.basedir, $file.tag))
492          println("</span><br />")
493          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>")
494 +      when "C"
495 +        print("<span class=\"pathname\" id=\"copied\">")
496 +        print($frontend.path($file.basedir, $file.tag))
497 +        println("</span><br />")
498 +        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>")
499        when "M"
500          print("<span class=\"pathname\">")
501          print($frontend.path($file.basedir, $file.tag))
502          println("</span><br />")
503          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>")
504 +      when "P"
505 +        print("<span class=\"pathname\">")
506 +        print($frontend.path($file.basedir, $file.tag))
507 +        println("</span><br />")
508 +        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>")
509      end
510      print("<pre class=\"diff\"><small id=\"info\">")
511      lines.each do |line|
512 @@ -1329,6 +1368,7 @@
513  $users_file_charset = nil
514  
515  $debug = false
516 +$svn = false
517  $recipients = Array.new
518  $sendmail_prog = "/usr/sbin/sendmail"
519  $hostname = ENV['HOSTNAME'] || 'localhost'
520 @@ -1366,6 +1406,7 @@
521    [ "--to",     "-t", GetoptLong::REQUIRED_ARGUMENT ],
522    [ "--config", "-c", GetoptLong::REQUIRED_ARGUMENT ],
523    [ "--debug",  "-d", GetoptLong::NO_ARGUMENT ],
524 +  [ "--svn",    "-s", GetoptLong::NO_ARGUMENT ],
525    [ "--from",   "-u", GetoptLong::REQUIRED_ARGUMENT ],
526    [ "--charset",      GetoptLong::REQUIRED_ARGUMENT ]
527  )
528 @@ -1374,6 +1415,7 @@
529    $recipients << EmailAddress.new(arg) if opt=="--to"
530    $config = arg if opt=="--config"
531    $debug = true if opt=="--debug"
532 +  $svn = true if opt=="--svn"
533    $from_address = EmailAddress.new(arg) if opt=="--from"
534    # must use different variable as the config is readed later.
535    $arg_charset = arg if opt == "--charset"
536 @@ -1386,7 +1428,7 @@
537    else
538      $stderr.puts "missing required file argument"
539    end
540 -  puts "Usage: cvsspam.rb [ --to <email> ] [ --config <file> ] <collect_diffs file>"
541 +  puts "Usage: cvsspam.rb [ --svn ] [ --to <email> ] [ --config <file> ] <collect_diffs file>"
542    exit(-1)
543  end
544  
545 @@ -1495,12 +1537,16 @@
546                  "T" => tagHandler,
547                  "A" => AddedFileHandler.new,
548                  "R" => RemovedFileHandler.new,
549 +                "C" => CopiedFileHandler.new,
550                  "M" => ModifiedFileHandler.new,
551 +                "P" => ModifiedPropsFileHandler.new,
552                  "V" => VersionHandler.new]
553  
554  $handlers["A"].setTagHandler(tagHandler)
555  $handlers["R"].setTagHandler(tagHandler)
556 +$handlers["C"].setTagHandler(tagHandler)
557  $handlers["M"].setTagHandler(tagHandler)
558 +$handlers["P"].setTagHandler(tagHandler)
559  
560  $fileEntries = Array.new
561  $task_list = Array.new
562 @@ -1525,7 +1571,11 @@
563  end
564  
565  if $subjectPrefix == nil
566 -  $subjectPrefix = "[CVS #{Repository.array.join(',')}]"
567 +  if $svn
568 +    $subjectPrefix = "[SVN #{Repository.array.join(',')}]"
569 +  else
570 +    $subjectPrefix = "[CVS #{Repository.array.join(',')}]"
571 +  end
572  end
573  
574  if $files_in_subject
575 @@ -1572,6 +1622,8 @@
576    #removed {background-color:#ffdddd;}
577    #removedchars {background-color:#ff9999;font-weight:bolder;}
578    tr.alt #removed {background-color:#f7cccc;}
579 +  #copied {background-color:#ccccff;}
580 +  tr.alt #copied {background-color:#bbbbf7;}
581    #info {color:#888888;}
582    #context {background-color:#eeeeee;}
583    td {padding-left:.3em;padding-right:.3em;}
584 @@ -1604,7 +1656,9 @@
585  
586    filesAdded = 0
587    filesRemoved = 0
588 +  filesCopied = 0
589    filesModified  = 0
590 +  filesModifiedProps  = 0
591    totalLinesAdded = 0
592    totalLinesRemoved = 0
593    file_count = 0
594 @@ -1613,24 +1667,26 @@
595    $fileEntries.each do |file|
596      unless file.repository == last_repository
597        last_repository = file.repository
598 -      mail.print("<tr class=\"head\"><td colspan=\"#{last_repository.has_multiple_tags ? 5 : 4}\">")
599 +      mail.print("<tr class=\"head\"><td colspan=\"#{last_repository.has_multiple_tags ? 6 : 5}\">")
600        if last_repository.has_multiple_tags
601          mail.print("Mixed-tag commit")
602        else
603          mail.print("Commit")
604        end
605        mail.print(" in <b><tt>#{htmlEncode(last_repository.common_prefix)}</tt></b>")
606 -      if last_repository.trunk_only?
607 -        mail.print("<span id=\"info\"> on MAIN</span>")
608 -      else
609 -        mail.print(" on ")
610 -        tagCount = 0
611 -        last_repository.each_tag do |tag|
612 -          tagCount += 1
613 -          if tagCount > 1
614 -            mail.print tagCount<last_repository.tag_count ? ", " : " &amp; "
615 +      if !$svn
616 +        if last_repository.trunk_only?
617 +          mail.print("<span id=\"info\"> on MAIN</span>")
618 +        else
619 +          mail.print(" on ")
620 +          tagCount = 0
621 +          last_repository.each_tag do |tag|
622 +            tagCount += 1
623 +            if tagCount > 1
624 +              mail.print tagCount<last_repository.tag_count ? ", " : " &amp; "
625 +            end
626 +            mail.print tag ? htmlEncode(tag) : "<span id=\"info\">MAIN</span>"
627            end
628 -          mail.print tag ? htmlEncode(tag) : "<span id=\"info\">MAIN</span>"
629          end
630        end
631        mail.puts("</td></tr>")
632 @@ -1645,8 +1701,12 @@
633        filesAdded += 1
634      elsif file.removal?
635        filesRemoved += 1
636 +    elsif file.copied?
637 +      filesCopied += 1
638      elsif file.modification?
639        filesModified += 1
640 +    elsif file.modifiedprops?
641 +      filesModifiedProps += 1
642      end
643      name = htmlEncode(file.name_after_common_prefix)
644      slashPos = name.rindex("/")
645 @@ -1666,6 +1726,8 @@
646        name = "<span id=\"added\">#{name}</span>"
647      elsif file.removal?
648        name = "<span id=\"removed\">#{name}</span>"
649 +    elsif file.copied?
650 +      name = "<span id=\"copied\">#{name}</span>"
651      end
652      mail.print("<td>")
653      if file.has_diff?
654 @@ -1675,11 +1737,18 @@
655      end
656      mail.print(" #{$frontend.log(file)}")
657      mail.print("</td>")
658 -    if file.isEmpty
659 -      mail.print("<td colspan=\"2\" align=\"center\"><small id=\"info\">[empty]</small></td>")
660 +    if file.copied?
661 +      mail.print("<td colspan=\"3\" align=\"center\"><small id=\"info\">[copied]</small></td>")
662 +    elsif file.isEmpty
663 +      mail.print("<td colspan=\"3\" align=\"center\"><small id=\"info\">[empty]</small></td>")
664      elsif file.isBinary
665 -      mail.print("<td colspan=\"2\" align=\"center\"><small id=\"info\">[binary]</small></td>")
666 +      mail.print("<td colspan=\"3\" align=\"center\"><small id=\"info\">[binary]</small></td>")
667      else
668 +      if file.modifiedprops?
669 +        mail.print("<td align=\"right\"><small id=\"info\">[props]</small></td>")
670 +      else
671 +        mail.print("<td></td>")
672 +      end
673        if file.lineAdditions>0
674          totalLinesAdded += file.lineAdditions
675          mail.print("<td align=\"right\" id=\"added\">+#{file.lineAdditions}</td>")
676 @@ -1706,15 +1775,19 @@
677        mail.print("<td nowrap=\"nowrap\" align=\"right\">added #{$frontend.version(file.path,file.toVer)}</td>")
678      elsif file.removal?
679        mail.print("<td nowrap=\"nowrap\">#{$frontend.version(file.path,file.fromVer)} removed</td>")
680 +    elsif file.copied?
681 +      mail.print("<td nowrap=\"nowrap\" align=\"center\">#{$frontend.version(file.path,file.fromVer)} #{$frontend.diff(file)} #{$frontend.version(file.path,file.toVer)}</td>")
682      elsif file.modification?
683        mail.print("<td nowrap=\"nowrap\" align=\"center\">#{$frontend.version(file.path,file.fromVer)} #{$frontend.diff(file)} #{$frontend.version(file.path,file.toVer)}</td>")
684 +    elsif file.modifiedprops?
685 +      mail.print("<td nowrap=\"nowrap\" align=\"center\">#{$frontend.version(file.path,file.fromVer)} #{$frontend.diff(file)} #{$frontend.version(file.path,file.toVer)}</td>")
686      end
687  
688      mail.puts("</tr>")
689    end
690    if $fileEntries.size>1 && (totalLinesAdded+totalLinesRemoved)>0
691      # give total number of lines added/removed accross all files
692 -    mail.print("<tr><td></td>")
693 +    mail.print("<tr><td></td><td></td>")
694      if totalLinesAdded>0
695        mail.print("<td align=\"right\" id=\"added\">+#{totalLinesAdded}</td>")
696      else
697 @@ -1731,7 +1804,7 @@
698    
699    mail.puts("</table>")
700  
701 -  totalFilesChanged = filesAdded+filesRemoved+filesModified
702 +  totalFilesChanged = filesAdded+filesRemoved+filesCopied+filesModified+filesModifiedProps
703    if totalFilesChanged > 1
704      mail.print("<small id=\"info\">")
705      changeKind = 0
706 @@ -1744,11 +1817,21 @@
707        mail.print("#{filesRemoved} removed")
708        changeKind += 1
709      end
710 +    if filesCopied>0
711 +      mail.print(" + ") if changeKind>0
712 +      mail.print("#{filesCopied} copied")
713 +      changeKind += 1
714 +    end
715      if filesModified>0
716        mail.print(" + ") if changeKind>0
717        mail.print("#{filesModified} modified")
718        changeKind += 1
719      end
720 +    if filesModifiedProps>0
721 +      mail.print(" + ") if changeKind>0
722 +      mail.print("#{filesModifiedProps} modified properties")
723 +      changeKind += 1
724 +    end
725      mail.print(", total #{totalFilesChanged}") if changeKind > 1
726      mail.puts(" files</small><br />")
727    end
This page took 0.097937 seconds and 3 git commands to generate.