2006-03-18 05:07:36 +03:00
|
|
|
#!/usr/bin/env python
|
2000-04-28 18:01:48 +04:00
|
|
|
# -*-python-*-
|
|
|
|
#
|
2006-03-18 05:07:36 +03:00
|
|
|
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
2006-03-20 06:27:02 +03:00
|
|
|
# Copyright (C) 2000 Curt Hagenlocher <curt@hagenlocher.org>
|
2000-04-28 18:01:48 +04:00
|
|
|
#
|
2000-05-09 12:36:29 +04:00
|
|
|
# By using this file, you agree to the terms and conditions set forth in
|
2006-03-18 03:33:54 +03:00
|
|
|
# the LICENSE.html file which can be found at the top level of the ViewVC
|
2005-12-17 20:19:28 +03:00
|
|
|
# distribution or at http://viewvc.org/license-1.html.
|
2000-04-28 18:01:48 +04:00
|
|
|
#
|
2006-03-18 05:07:36 +03:00
|
|
|
# For more information, visit http://viewvc.org/
|
2000-04-28 18:01:48 +04:00
|
|
|
#
|
2000-05-09 12:36:29 +04:00
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# blame.py: Annotate each line of a CVS file with its author,
|
|
|
|
# revision #, date, etc.
|
2000-04-28 18:01:48 +04:00
|
|
|
#
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
#
|
|
|
|
# This file is based on the cvsblame.pl portion of the Bonsai CVS tool,
|
|
|
|
# developed by Steve Lamm for Netscape Communications Corporation. More
|
|
|
|
# information about Bonsai can be found at
|
|
|
|
# http://www.mozilla.org/bonsai.html
|
|
|
|
#
|
|
|
|
# cvsblame.pl, in turn, was based on Scott Furman's cvsblame script
|
|
|
|
#
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
import string
|
|
|
|
import os
|
|
|
|
import re
|
|
|
|
import time
|
|
|
|
import math
|
|
|
|
import cgi
|
2005-02-03 16:04:03 +03:00
|
|
|
import vclib
|
2005-09-23 22:51:35 +04:00
|
|
|
import vclib.ccvs.blame
|
2000-04-28 18:01:48 +04:00
|
|
|
|
|
|
|
|
|
|
|
re_includes = re.compile('\\#(\\s*)include(\\s*)"(.*?)"')
|
|
|
|
|
2005-02-03 16:04:03 +03:00
|
|
|
def link_includes(text, repos, path_parts, include_url):
|
2000-05-09 15:21:30 +04:00
|
|
|
match = re_includes.match(text)
|
|
|
|
if match:
|
|
|
|
incfile = match.group(3)
|
2006-08-01 20:45:35 +04:00
|
|
|
include_path_parts = path_parts[:-1]
|
|
|
|
for part in filter(None, string.split(incfile, '/')):
|
|
|
|
if part == "..":
|
|
|
|
if not include_path_parts:
|
|
|
|
# nothing left to pop; don't bother marking up this include.
|
|
|
|
return text
|
|
|
|
include_path_parts.pop()
|
|
|
|
elif part and part != ".":
|
|
|
|
include_path_parts.append(part)
|
2005-02-03 16:04:03 +03:00
|
|
|
|
2006-08-01 20:45:35 +04:00
|
|
|
include_path = None
|
|
|
|
try:
|
|
|
|
if repos.itemtype(include_path_parts, None) == vclib.FILE:
|
|
|
|
include_path = string.join(include_path_parts, '/')
|
|
|
|
except vclib.ItemNotFound:
|
|
|
|
pass
|
2005-02-03 16:04:03 +03:00
|
|
|
|
|
|
|
if include_path:
|
2006-08-01 20:45:35 +04:00
|
|
|
return '#%sinclude%s<a href="%s">"%s"</a>' % \
|
|
|
|
(match.group(1), match.group(2),
|
|
|
|
string.replace(include_url, '/WHERE/', include_path), incfile)
|
|
|
|
|
2000-05-09 15:21:30 +04:00
|
|
|
return text
|
2000-04-28 18:01:48 +04:00
|
|
|
|
2004-04-21 09:08:12 +04:00
|
|
|
|
2005-09-23 22:51:35 +04:00
|
|
|
class HTMLBlameSource:
|
|
|
|
"""Wrapper around a the object by the vclib.annotate() which does
|
|
|
|
HTML escaping, diff URL generation, and #include linking."""
|
|
|
|
def __init__(self, repos, path_parts, diff_url, include_url, opt_rev=None):
|
2005-02-03 16:04:03 +03:00
|
|
|
self.repos = repos
|
|
|
|
self.path_parts = path_parts
|
|
|
|
self.diff_url = diff_url
|
|
|
|
self.include_url = include_url
|
2005-09-23 22:51:35 +04:00
|
|
|
self.annotation, self.revision = self.repos.annotate(path_parts, opt_rev)
|
2004-04-21 09:08:12 +04:00
|
|
|
|
|
|
|
def __getitem__(self, idx):
|
2005-09-23 22:51:35 +04:00
|
|
|
item = self.annotation.__getitem__(idx)
|
2004-04-21 09:08:12 +04:00
|
|
|
diff_url = None
|
2005-09-23 22:51:35 +04:00
|
|
|
if item.prev_rev:
|
|
|
|
diff_url = '%sr1=%s&r2=%s' % (self.diff_url, item.prev_rev, item.rev)
|
|
|
|
thisline = link_includes(cgi.escape(item.text), self.repos,
|
|
|
|
self.path_parts, self.include_url)
|
|
|
|
return _item(text=thisline, line_number=item.line_number,
|
|
|
|
rev=item.rev, prev_rev=item.prev_rev,
|
|
|
|
diff_url=diff_url, date=item.date, author=item.author)
|
2005-02-03 16:04:03 +03:00
|
|
|
|
2000-05-09 15:21:30 +04:00
|
|
|
|
2005-09-23 22:51:35 +04:00
|
|
|
def blame(repos, path_parts, diff_url, include_url, opt_rev=None):
|
|
|
|
source = HTMLBlameSource(repos, path_parts, diff_url, include_url, opt_rev)
|
|
|
|
return source, source.revision
|
2000-05-09 15:21:30 +04:00
|
|
|
|
|
|
|
|
2004-04-21 09:08:12 +04:00
|
|
|
class _item:
|
|
|
|
def __init__(self, **kw):
|
|
|
|
vars(self).update(kw)
|
|
|
|
|
2005-09-23 22:51:35 +04:00
|
|
|
|
|
|
|
def make_html(root, rcs_path):
|
|
|
|
bs = vclib.ccvs.blame.BlameSource(os.path.join(root, rcs_path))
|
2004-04-21 09:08:12 +04:00
|
|
|
|
|
|
|
count = bs.num_lines
|
2000-05-09 15:21:30 +04:00
|
|
|
if count == 0:
|
|
|
|
count = 1
|
|
|
|
|
2000-05-11 05:12:33 +04:00
|
|
|
line_num_width = int(math.log10(count)) + 1
|
2000-05-09 15:21:30 +04:00
|
|
|
revision_width = 3
|
|
|
|
author_width = 5
|
|
|
|
line = 0
|
|
|
|
old_revision = 0
|
|
|
|
row_color = ''
|
|
|
|
inMark = 0
|
|
|
|
rev_count = 0
|
|
|
|
|
2005-09-28 21:06:16 +04:00
|
|
|
open_table_tag = '<table cellpadding="0" cellspacing="0">'
|
2004-04-21 09:08:12 +04:00
|
|
|
startOfRow = '<tr><td colspan="3"%s><pre>'
|
|
|
|
endOfRow = '</td></tr>'
|
2000-05-09 15:21:30 +04:00
|
|
|
|
2004-04-21 09:08:12 +04:00
|
|
|
print open_table_tag + (startOfRow % '')
|
2000-05-09 15:21:30 +04:00
|
|
|
|
2004-04-21 09:08:12 +04:00
|
|
|
for line_data in bs:
|
|
|
|
revision = line_data.rev
|
|
|
|
thisline = line_data.text
|
|
|
|
line = line_data.line_number
|
|
|
|
author = line_data.author
|
|
|
|
prev_rev = line_data.prev_rev
|
|
|
|
|
2000-05-09 15:21:30 +04:00
|
|
|
output = ''
|
|
|
|
|
|
|
|
if old_revision != revision and line != 1:
|
|
|
|
if row_color == '':
|
2005-09-28 21:06:16 +04:00
|
|
|
row_color = ' style="background-color:#e7e7e7"'
|
2000-05-09 15:21:30 +04:00
|
|
|
else:
|
|
|
|
row_color = ''
|
|
|
|
|
|
|
|
if not inMark:
|
2004-04-21 09:08:12 +04:00
|
|
|
output = output + endOfRow + (startOfRow % row_color)
|
2000-05-09 15:21:30 +04:00
|
|
|
|
2002-10-10 04:53:14 +04:00
|
|
|
output = output + '<a name="%d">%*d</a>' % (line, line_num_width, line)
|
2000-05-09 15:21:30 +04:00
|
|
|
|
|
|
|
if old_revision != revision or rev_count > 20:
|
|
|
|
revision_width = max(revision_width, len(revision))
|
2004-04-21 09:08:12 +04:00
|
|
|
output = output + ' '
|
2000-05-09 15:21:30 +04:00
|
|
|
author_width = max(author_width, len(author))
|
2002-10-10 04:53:14 +04:00
|
|
|
output = output + ('%-*s ' % (author_width, author))
|
2000-05-12 11:24:13 +04:00
|
|
|
output = output + revision
|
2004-04-21 09:08:12 +04:00
|
|
|
if prev_rev:
|
2000-05-12 11:24:13 +04:00
|
|
|
output = output + '</a>'
|
|
|
|
output = output + (' ' * (revision_width - len(revision) + 1))
|
2000-05-09 15:21:30 +04:00
|
|
|
|
|
|
|
old_revision = revision
|
|
|
|
rev_count = 0
|
|
|
|
else:
|
|
|
|
output = output + ' ' + (' ' * (author_width + revision_width))
|
|
|
|
rev_count = rev_count + 1
|
|
|
|
|
|
|
|
output = output + thisline
|
|
|
|
|
|
|
|
# Close the highlighted section
|
|
|
|
#if (defined $mark_cmd and mark_cmd != 'begin'):
|
2007-04-18 01:07:33 +04:00
|
|
|
# chop($output)
|
|
|
|
# output = output + endOfRow + (startOfRow % row_color)
|
|
|
|
# inMark = 0
|
2000-05-09 15:21:30 +04:00
|
|
|
|
|
|
|
print output
|
2000-05-11 15:15:54 +04:00
|
|
|
print endOfRow + '</table>'
|
2000-04-28 18:01:48 +04:00
|
|
|
|
|
|
|
|
2000-05-11 05:12:33 +04:00
|
|
|
def main():
|
2002-10-10 04:53:14 +04:00
|
|
|
import sys
|
2000-05-11 05:12:33 +04:00
|
|
|
if len(sys.argv) != 3:
|
|
|
|
print 'USAGE: %s cvsroot rcs-file' % sys.argv[0]
|
|
|
|
sys.exit(1)
|
|
|
|
make_html(sys.argv[1], sys.argv[2])
|
|
|
|
|
2000-04-28 18:01:48 +04:00
|
|
|
if __name__ == '__main__':
|
2000-05-11 05:12:33 +04:00
|
|
|
main()
|