class SVNRepo

Public Class Methods

new(root) click to toggle source
# File lib/vcs-ann/svn.rb, line 2
def initialize(root)
  @root = root
  @type = {}
  @cat = {}
  @ann = {}
end

Public Instance Methods

format_commit(list) click to toggle source
# File lib/vcs-ann/svn.rb, line 144
def format_commit(list)
  rev = list[0]
  log_out, log_err, log_status = Open3.capture3({"LC_ALL"=>"C"}, "svn", "log", "-r#{rev}", "#{@root}")
  diff_out, diff_err, diff_status = Open3.capture3({"LC_ALL"=>"C"}, "svn", "diff", "-c#{rev}", "#{@root}")
  log_out.force_encoding('locale').scrub!
  log_err.force_encoding('locale').scrub!
  diff_out.force_encoding('locale').scrub!
  diff_err.force_encoding('locale').scrub!

  rev_hash = {}
  diff_out.each_line {|line|
    next if /\A(?:---|\+\+\+)\s+(\S+)\s+\(revision (\d+)\)/ !~ line
    filename = $1
    rev = $2
    rev_hash[rev] = true
  }
  rev_maxwidth = rev_hash.keys.map {|rev| rev.length }.max
  rev_space = " " * rev_maxwidth
  rev_fmt = "%.#{rev_maxwidth}s"

  result = ''

  result << '<pre>'
  log_out.each_line {|line|
    result << (h line.chomp) << "\n"
  }
  result << '</pre>'

  rev1 = (rev.to_i-1).to_s
  rev2 = rev
  filename1 = filename2 = '?'
  result << '<pre>'
  scan_udiff(diff_out) {|tag, *rest|
    case tag
    when :filename1
      line, filename1 = rest
      result << " "
      result << (h line.chomp) << "\n"
    when :filename2
      line, filename2 = rest
      result << " "
      result << (h line.chomp) << "\n"
    when :hunk_header
      line, ln_cur1, ln_num1, ln_cur2, ln_num2 = rest
      result << " "
      result << (h line.chomp) << "\n"
    when :del
      line, content_line, ln_cur1 = rest
      content_line = content_line.chomp.expand_tab
      rev1_url = "/file/#{rev1}/#{filename1}\##{ln_cur1}"
      result << %{<a name="#{h(u(rev1.to_s+"/"+filename1+":"+ln_cur1.to_s))}"></a>}
      result << %{<a name="#{h(u(rev1.to_s+":"+content_line.chomp))}"></a>}
      result << %{<a href="#{h rev1_url}"> -</a>}
      result << (h content_line) << "\n"
    when :add
      line, content_line, ln_cur2 = rest
      content_line = content_line.chomp.expand_tab
      rev2_url = "/file/#{rev2}/#{filename2}\##{ln_cur2}"
      result << %{<a name="#{h(u(rev2.to_s+"/"+filename2+":"+ln_cur2.to_s))}"></a>}
      result << %{<a name="#{h(u(rev2.to_s+":"+content_line.chomp))}"></a>}
      result << %{<a href="#{h rev2_url}"> +</a>}
      result << (h content_line) << "\n"
    when :com
      line, content_line, ln_cur1, ln_cur2 = rest
      content_line = content_line.chomp.expand_tab
      rev1_url = "/file/#{rev1}/#{filename1}\##{ln_cur1}"
      rev2_url = "/file/#{rev2}/#{filename2}\##{ln_cur2}"
      result << %{<a name="#{h(u(rev1.to_s+"/"+filename1+":"+ln_cur1.to_s))}"></a>}
      result << %{<a name="#{h(u(rev2.to_s+"/"+filename2+":"+ln_cur2.to_s))}"></a>}
      result << %{<a name="#{h(u(rev1.to_s+":"+content_line.chomp))}"></a>}
      result << %{<a name="#{h(u(rev2.to_s+":"+content_line.chomp))}"></a>}
      result << %{<a href="#{h rev1_url}"> </a>}
      result << %{<a href="#{h rev2_url}"> </a>}
      result << (h content_line) << "\n"
    when :other
      line, = rest
      result << " "
      result << (h line.chomp) << "\n"
    else
      raise "unexpected udiff line tag"
    end
  }
  result << '</pre>'

  result
end
format_file(list) click to toggle source
# File lib/vcs-ann/svn.rb, line 77
def format_file(list)
  rev = list[0]
  relpath = list[1..-1].map {|s| "/" + s }.join

  case type = run_info(relpath, rev)
  when :file
  else
    raise "unexpected type #{type}: #{relpath}@#{rev}"
  end

  cat = run_cat(relpath, rev)
  if !cat
    raise "not a plain file: #{relpath}@#{rev}"
  end

  ann = run_ann(relpath, rev)
  ann.open
  lines = []
  ann.each("</entry>\n") {|entry|
    next if /<entry\n/ !~ entry
    entry = $'
    next if /line-number="(\d+)"/ !~ entry
    line_number = $1
    next if /revision="(\d+)"/ !~ entry
    line_rev = $1
    next if %r{<author>(.*)</author>} !~ entry
    line_author = CGI.unescapeHTML($1)
    next if %r{<date>(.*)</date>} !~ entry
    line_date = $1
    lines << [line_number, line_rev, line_author, line_date]
  }
  ann.close

  width_list = lines.map {|line_number, line_rev, line_author, line_date|
    [line_number.length, line_rev.length, line_author.length]
  }
  line_number_width = width_list.map {|ln_width, rev_width, authoer_width| ln_width }.max
  line_rev_width = width_list.map {|ln_width, rev_width, authoer_width| rev_width }.max
  line_author_width = width_list.map {|ln_width, rev_width, author_width| author_width }.max

  result = "<pre>"
  prev_rev = nil
  cat.open
  cat.each.with_index {|line, i|
    line.expand_tab!
    ln = i+1
    line_number, line_rev, line_author, line_date = lines[i]
    commit_url = "/commit/#{line_rev}\##{u line_rev+':'+line.chomp}"
    authorsp = line_author.ljust(line_author_width)
    line_number_anchor = ln.to_s
    line_contents_anchor = u line.chomp
    if prev_rev != line_rev
      revsp = line_rev.rjust(line_rev_width)
      prev_rev = line_rev
    else
      revsp = ' ' * line_rev_width
    end
    result << %{<a name="#{h line_number_anchor}"></a>}
    result << %{<a name="#{h line_contents_anchor}"></a>}
    result << %{<a href="#{h commit_url}" title="#{h line_date}">#{h revsp}</a> }
    result << "#{h authorsp} #{h line.chomp}\n"
  }
  cat.close
  result << "</pre>"
  result
end
run_ann(relpath, rev) click to toggle source
# File lib/vcs-ann/svn.rb, line 59
def run_ann(relpath, rev)
  key = [relpath, rev]
  if !@ann.has_key?(key)
    if run_info(relpath, rev) != :file
      raise "file expected"
    end
    tmpbase = File.basename(relpath) + "-r#{rev}"
    ann = Tempfile.new([tmpbase, ".xml"])
    ann.close
    system "svn", "ann", "--xml", "-r#{rev}", "#{@root}#{relpath}", :out => ann.path
    if !$?.success?
      raise "svn ann failed"
    end
    @ann[key] = ann
  end
  @ann[key]
end
run_cat(relpath, rev) click to toggle source
# File lib/vcs-ann/svn.rb, line 36
def run_cat(relpath, rev)
  key = [relpath, rev]
  if !@cat.has_key?(key)
    if run_info(relpath, rev) != :file
      raise "file expected"
    end
    tmpbase = File.basename(relpath) + "-r#{rev}"
    cat = Tempfile.new([tmpbase, ".txt"])
    cat.close
    out, err, status = Open3.capture3({"LC_ALL"=>"C"}, "svn", "cat", "-r#{rev}", "#{@root}#{relpath}")
    out.force_encoding('locale').scrub!
    err.force_encoding('locale').scrub!
    if !status.success?
      raise "svn cat failed"
    end
    cat.open
    cat << out
    cat.close
    @cat[key] = cat
  end
  @cat[key]
end
run_info(relpath, rev) click to toggle source
# File lib/vcs-ann/svn.rb, line 9
def run_info(relpath, rev)
  key = [relpath, rev]
  if !@type.has_key?(key)
    out, err, status = Open3.capture3({"LC_ALL"=>"C"}, "svn", "info", "-r#{rev}", "#{@root}#{relpath}")
    out.force_encoding('locale').scrub!
    err.force_encoding('locale').scrub!
    if !status.success?
      case err
      when /Unable to find repository location/
        @type[key] = :not_exist
      else
        raise "unexpected failing svn info result: #{err}"
      end
    else
      case out
      when /^Node Kind: file$/
        @type[key] = :file
      when /^Node Kind: directory$/
        @type[key] = :dir
      else
        raise "unexpected succeseed svn info result"
      end
    end
  end
  @type[key]
end