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