2006-03-18 05:07:36 +03:00
|
|
|
# -*-python-*-
|
2001-08-21 23:24:40 +04:00
|
|
|
#
|
2006-03-18 05:07:36 +03:00
|
|
|
# Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved.
|
2001-08-21 23:24:40 +04:00
|
|
|
#
|
|
|
|
# By using this file, you agree to the terms and conditions set forth in
|
2005-12-17 20:19:28 +03:00
|
|
|
# the LICENSE.html file which can be found at the top level of the ViewVC
|
|
|
|
# distribution or at http://viewvc.org/license-1.html.
|
2001-08-21 23:24:40 +04:00
|
|
|
#
|
2006-03-18 05:07:36 +03:00
|
|
|
# For more information, visit http://viewvc.org/
|
2001-08-21 23:24:40 +04:00
|
|
|
#
|
|
|
|
# -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import string
|
|
|
|
import time
|
2004-07-18 09:41:53 +04:00
|
|
|
import fnmatch
|
2004-10-09 06:32:15 +04:00
|
|
|
import re
|
2001-08-21 23:24:40 +04:00
|
|
|
|
|
|
|
import dbi
|
|
|
|
|
2001-11-18 13:29:31 +03:00
|
|
|
|
2001-08-21 23:24:40 +04:00
|
|
|
## error
|
|
|
|
error = "cvsdb error"
|
|
|
|
|
|
|
|
## CheckinDatabase provides all interfaces needed to the SQL database
|
|
|
|
## back-end; it needs to be subclassed, and have its "Connect" method
|
|
|
|
## defined to actually be complete; it should run well off of any DBI 2.0
|
|
|
|
## complient database interface
|
|
|
|
|
|
|
|
class CheckinDatabase:
|
2005-12-22 06:07:31 +03:00
|
|
|
def __init__(self, host, port, user, passwd, database, row_limit):
|
2001-08-21 23:24:40 +04:00
|
|
|
self._host = host
|
2005-12-22 06:07:31 +03:00
|
|
|
self._port = port
|
2001-08-21 23:24:40 +04:00
|
|
|
self._user = user
|
|
|
|
self._passwd = passwd
|
|
|
|
self._database = database
|
Tweak logic used to find installed paths ("lib" directory, configuration
file, templates, etc). Specifically, make the following changes:
- Get rid of hardcoded paths in module files ("lib" directory) and
configuration files (viewcvs.conf and mod_python's .htaccess)
- Allow stub scripts (for asp, cgi, and mod_python) to specify
configuration files so it's possible to have multiple configurations of
viewcvs running off a single installation.
- Be more consistent about resolving paths when they aren't hardcoded
(when ViewCVS is running from a source directory). In particular, try
not to depend on the working directory. That way it's legal to run
./standalone.py instead of bin/standalone.py without getting an
ImportError.
- Get rid of global cfg variables in viewcvs.py and cvsdb.py. They led to
all sorts of hacks in other files to pilfer and reset them. They were
also possible sources of races in multithreaded environments like
mod_python and asp.
- Rewrite mod_python handler so library paths can be specified inside the
stub files.
* lib/apache.py
removed, contained old mod_python handler
* lib/config.py
(Config.load_config):
remove sys.argv voodoo, just load the configuration path specified in
the pathname argument
(Config.set_defaults):
use relative path for cvsgraph_conf setting instead of hardcoded
<VIEWCVS_INSTALL_DIRECTORY> literal
* lib/cvsdb.py
(CONF_PATHNAME, config, cfg):
removed, configuration stuff
(CheckinDatabase.__init__, CheckinDatabase.CreateSQLQueryString):
add "_row_limit" member instead of using cfg object
(CreateCheckinDatabase):
removed, a do-nothing function
(ConnectDatabaseReadOnly, ConnectDatabase):
add cfg arguments
(GetUnrecordedCommitList):
add db argument
* lib/query.py
(CONF_PATHAME):
removed
(build_commit, run_query, main):
add cfg arguments, use new viewcvs.get_template function
* lib/viewcvs.py
(CONF_PATHNAME, cfg, g_install_dir):
removed
(Request.__init__):
add cfg member
(Request.run_viewcvs, Request.get_link, check_freshness,
get_view_template, generate_page, default_view, get_file_view_info,
format_log, common_template_data, MarkupEnscript.__init__,
markup_stream_python, markup_stream_php, make_time_string, view_markup,
sort_file_data, view_directory, paging, view_log, view_annotate,
view_cvsgraph_image, view_cvsgraph, view_doc, rcsdiff_date_reformat,
spaced_html_text, DiffSource.__init__, DiffSource._get_row, view_patch,
view_diff, generate_tarball, download_tarball, view_revision,
is_query_supported, english_query, build_commit, view_query,
view_error, main):
stop using global config, use cfg arguments or request member instead
(_install_path):
new, use __file__ to locate template and configuation paths
(get_view_template):
use _install_path and return compiled template instead of path
(is_viewable, default_view):
rename is_viewable to default_view and return view instead of boolean
(handle_config, load_config):
rename handle_config to load_config and return config object instead
of setting cfg global
* bin/cgi/viewcvs.cgi
* bin/cgi/query.cgi
* bin/cvsdbadmin
* bin/loginfo-handler
* bin/standalone.py
* bin/svndbadmin
look for library relative to sys.argv[0] when no hardcoded library
path is available. Also add configuration loading code.
* bin/asp/viewcvs.asp
* bin/asp/query.asp
add configuration loading code
* bin/mod_python/.htaccess
specify new mod_python handler, remove reference to
<VIEWCVS_APACHE_LIBRARY_DIRECTORY>
* bin/mod_python/handler.py
added, holds new mod_python handler
* bin/mod_python/viewcvs.py
* bin/mod_python/query.py
rewrite for new handler, add hardcoded library and configuration paths
* viewcvs.conf.dist
remove references to <VIEWCVS_INSTALL_DIRECTORY>
* viewcvs-install
(FILE_INFO_LIST):
stop hardcoding paths in config files
(SetPythonPaths,):
get rid of <VIEWCVS_INSTALL_DIRECTORY> and
<VIEWCVS_APACHE_LIBRARY_DIRECTORY> substitutions
(ApacheEscape, _re_apache):
removed
(InstallTree):
stop hardcoding paths in lib directory
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@1173 8cb11bc2-c004-0410-86c3-e597b4017df7
2005-12-06 07:04:14 +03:00
|
|
|
self._row_limit = row_limit
|
2001-08-21 23:24:40 +04:00
|
|
|
|
|
|
|
## database lookup caches
|
|
|
|
self._get_cache = {}
|
|
|
|
self._get_id_cache = {}
|
|
|
|
self._desc_id_cache = {}
|
|
|
|
|
|
|
|
def Connect(self):
|
|
|
|
self.db = dbi.connect(
|
2005-12-22 06:07:31 +03:00
|
|
|
self._host, self._port, self._user, self._passwd, self._database)
|
2007-04-10 09:56:17 +04:00
|
|
|
cursor = self.db.cursor()
|
|
|
|
cursor.execute("SET AUTOCOMMIT=1")
|
2001-08-21 23:24:40 +04:00
|
|
|
|
|
|
|
def sql_get_id(self, table, column, value, auto_set):
|
|
|
|
sql = "SELECT id FROM %s WHERE %s=%%s" % (table, column)
|
|
|
|
sql_args = (value, )
|
|
|
|
|
|
|
|
cursor = self.db.cursor()
|
|
|
|
cursor.execute(sql, sql_args)
|
|
|
|
try:
|
|
|
|
(id, ) = cursor.fetchone()
|
|
|
|
except TypeError:
|
|
|
|
if not auto_set:
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
return str(int(id))
|
|
|
|
|
|
|
|
## insert the new identifier
|
|
|
|
sql = "INSERT INTO %s(%s) VALUES(%%s)" % (table, column)
|
|
|
|
sql_args = (value, )
|
|
|
|
cursor.execute(sql, sql_args)
|
|
|
|
|
|
|
|
return self.sql_get_id(table, column, value, 0)
|
|
|
|
|
|
|
|
def get_id(self, table, column, value, auto_set):
|
|
|
|
## attempt to retrieve from cache
|
|
|
|
try:
|
|
|
|
return self._get_id_cache[table][column][value]
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
id = self.sql_get_id(table, column, value, auto_set)
|
|
|
|
if id == None:
|
|
|
|
return None
|
|
|
|
|
|
|
|
## add to cache
|
|
|
|
try:
|
|
|
|
temp = self._get_id_cache[table]
|
|
|
|
except KeyError:
|
|
|
|
temp = self._get_id_cache[table] = {}
|
|
|
|
|
|
|
|
try:
|
|
|
|
temp2 = temp[column]
|
|
|
|
except KeyError:
|
|
|
|
temp2 = temp[column] = {}
|
|
|
|
|
|
|
|
temp2[value] = id
|
|
|
|
return id
|
|
|
|
|
|
|
|
def sql_get(self, table, column, id):
|
|
|
|
sql = "SELECT %s FROM %s WHERE id=%%s" % (column, table)
|
|
|
|
sql_args = (id, )
|
|
|
|
|
|
|
|
cursor = self.db.cursor()
|
|
|
|
cursor.execute(sql, sql_args)
|
|
|
|
try:
|
|
|
|
(value, ) = cursor.fetchone()
|
|
|
|
except TypeError:
|
|
|
|
return None
|
|
|
|
|
|
|
|
return value
|
|
|
|
|
|
|
|
def get(self, table, column, id):
|
|
|
|
## attempt to retrieve from cache
|
|
|
|
try:
|
|
|
|
return self._get_cache[table][column][id]
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
value = self.sql_get(table, column, id)
|
|
|
|
if value == None:
|
|
|
|
return None
|
|
|
|
|
|
|
|
## add to cache
|
|
|
|
try:
|
|
|
|
temp = self._get_cache[table]
|
|
|
|
except KeyError:
|
|
|
|
temp = self._get_cache[table] = {}
|
|
|
|
|
|
|
|
try:
|
|
|
|
temp2 = temp[column]
|
|
|
|
except KeyError:
|
|
|
|
temp2 = temp[column] = {}
|
|
|
|
|
|
|
|
temp2[id] = value
|
|
|
|
return value
|
|
|
|
|
|
|
|
def get_list(self, table, field_index):
|
|
|
|
sql = "SELECT * FROM %s" % (table)
|
|
|
|
cursor = self.db.cursor()
|
|
|
|
cursor.execute(sql)
|
|
|
|
|
|
|
|
list = []
|
|
|
|
while 1:
|
|
|
|
row = cursor.fetchone()
|
|
|
|
if row == None:
|
|
|
|
break
|
|
|
|
list.append(row[field_index])
|
|
|
|
|
|
|
|
return list
|
|
|
|
|
|
|
|
def GetBranchID(self, branch, auto_set = 1):
|
|
|
|
return self.get_id("branches", "branch", branch, auto_set)
|
|
|
|
|
|
|
|
def GetBranch(self, id):
|
|
|
|
return self.get("branches", "branch", id)
|
|
|
|
|
|
|
|
def GetDirectoryID(self, dir, auto_set = 1):
|
|
|
|
return self.get_id("dirs", "dir", dir, auto_set)
|
|
|
|
|
|
|
|
def GetDirectory(self, id):
|
|
|
|
return self.get("dirs", "dir", id)
|
|
|
|
|
|
|
|
def GetFileID(self, file, auto_set = 1):
|
|
|
|
return self.get_id("files", "file", file, auto_set)
|
|
|
|
|
|
|
|
def GetFile(self, id):
|
|
|
|
return self.get("files", "file", id)
|
|
|
|
|
|
|
|
def GetAuthorID(self, author, auto_set = 1):
|
|
|
|
return self.get_id("people", "who", author, auto_set)
|
|
|
|
|
|
|
|
def GetAuthor(self, id):
|
|
|
|
return self.get("people", "who", id)
|
|
|
|
|
|
|
|
def GetRepositoryID(self, repository, auto_set = 1):
|
|
|
|
return self.get_id("repositories", "repository", repository, auto_set)
|
|
|
|
|
|
|
|
def GetRepository(self, id):
|
|
|
|
return self.get("repositories", "repository", id)
|
|
|
|
|
|
|
|
def SQLGetDescriptionID(self, description, auto_set = 1):
|
|
|
|
## lame string hash, blame Netscape -JMP
|
|
|
|
hash = len(description)
|
|
|
|
|
|
|
|
sql = "SELECT id FROM descs WHERE hash=%s AND description=%s"
|
|
|
|
sql_args = (hash, description)
|
|
|
|
|
|
|
|
cursor = self.db.cursor()
|
|
|
|
cursor.execute(sql, sql_args)
|
|
|
|
try:
|
|
|
|
(id, ) = cursor.fetchone()
|
|
|
|
except TypeError:
|
|
|
|
if not auto_set:
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
return str(int(id))
|
|
|
|
|
|
|
|
sql = "INSERT INTO descs (hash,description) values (%s,%s)"
|
|
|
|
sql_args = (hash, description)
|
|
|
|
cursor.execute(sql, sql_args)
|
|
|
|
|
|
|
|
return self.GetDescriptionID(description, 0)
|
|
|
|
|
|
|
|
def GetDescriptionID(self, description, auto_set = 1):
|
|
|
|
## attempt to retrieve from cache
|
|
|
|
hash = len(description)
|
|
|
|
try:
|
|
|
|
return self._desc_id_cache[hash][description]
|
|
|
|
except KeyError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
id = self.SQLGetDescriptionID(description, auto_set)
|
|
|
|
if id == None:
|
|
|
|
return None
|
|
|
|
|
|
|
|
## add to cache
|
|
|
|
try:
|
|
|
|
temp = self._desc_id_cache[hash]
|
|
|
|
except KeyError:
|
|
|
|
temp = self._desc_id_cache[hash] = {}
|
|
|
|
|
|
|
|
temp[description] = id
|
|
|
|
return id
|
|
|
|
|
|
|
|
def GetDescription(self, id):
|
|
|
|
return self.get("descs", "description", id)
|
|
|
|
|
|
|
|
def GetRepositoryList(self):
|
|
|
|
return self.get_list("repositories", 1)
|
|
|
|
|
|
|
|
def GetBranchList(self):
|
|
|
|
return self.get_list("branches", 1)
|
|
|
|
|
|
|
|
def GetAuthorList(self):
|
|
|
|
return self.get_list("people", 1)
|
|
|
|
|
|
|
|
def AddCommitList(self, commit_list):
|
|
|
|
for commit in commit_list:
|
|
|
|
self.AddCommit(commit)
|
|
|
|
|
|
|
|
def AddCommit(self, commit):
|
2007-02-26 23:20:04 +03:00
|
|
|
ci_when = dbi.DateTimeFromTicks(commit.GetTime() or 0.0)
|
2001-08-21 23:24:40 +04:00
|
|
|
ci_type = commit.GetTypeString()
|
|
|
|
who_id = self.GetAuthorID(commit.GetAuthor())
|
|
|
|
repository_id = self.GetRepositoryID(commit.GetRepository())
|
|
|
|
directory_id = self.GetDirectoryID(commit.GetDirectory())
|
|
|
|
file_id = self.GetFileID(commit.GetFile())
|
|
|
|
revision = commit.GetRevision()
|
|
|
|
sticky_tag = "NULL"
|
|
|
|
branch_id = self.GetBranchID(commit.GetBranch())
|
2007-01-06 02:05:14 +03:00
|
|
|
plus_count = commit.GetPlusCount() or '0'
|
|
|
|
minus_count = commit.GetMinusCount() or '0'
|
2001-08-21 23:24:40 +04:00
|
|
|
description_id = self.GetDescriptionID(commit.GetDescription())
|
|
|
|
|
|
|
|
sql = "REPLACE INTO checkins"\
|
|
|
|
" (type,ci_when,whoid,repositoryid,dirid,fileid,revision,"\
|
|
|
|
" stickytag,branchid,addedlines,removedlines,descid)"\
|
|
|
|
"VALUES(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"
|
|
|
|
sql_args = (ci_type, ci_when, who_id, repository_id,
|
|
|
|
directory_id, file_id, revision, sticky_tag, branch_id,
|
|
|
|
plus_count, minus_count, description_id)
|
|
|
|
|
|
|
|
cursor = self.db.cursor()
|
2007-01-05 02:05:39 +03:00
|
|
|
try:
|
|
|
|
cursor.execute(sql, sql_args)
|
|
|
|
except Exception, e:
|
|
|
|
raise Exception("Error adding commit: '%s'\n"
|
|
|
|
"Values were:\n"
|
2007-01-05 19:07:05 +03:00
|
|
|
"\ttype = %s\n"
|
|
|
|
"\tci_when = %s\n"
|
|
|
|
"\twhoid = %s\n"
|
|
|
|
"\trepositoryid = %s\n"
|
|
|
|
"\tdirid = %s\n"
|
|
|
|
"\tfileid = %s\n"
|
|
|
|
"\trevision = %s\n"
|
|
|
|
"\tstickytag = %s\n"
|
|
|
|
"\tbranchid = %s\n"
|
|
|
|
"\taddedlines = %s\n"
|
|
|
|
"\tremovedlines = %s\n"
|
|
|
|
"\tdescid = %s\n"
|
|
|
|
% ((str(e), ) + sql_args))
|
2001-08-21 23:24:40 +04:00
|
|
|
|
2004-08-01 18:33:00 +04:00
|
|
|
def SQLQueryListString(self, field, query_entry_list):
|
2001-08-21 23:24:40 +04:00
|
|
|
sqlList = []
|
|
|
|
|
|
|
|
for query_entry in query_entry_list:
|
2004-07-18 09:41:53 +04:00
|
|
|
data = query_entry.data
|
2001-08-21 23:24:40 +04:00
|
|
|
## figure out the correct match type
|
|
|
|
if query_entry.match == "exact":
|
|
|
|
match = "="
|
|
|
|
elif query_entry.match == "like":
|
|
|
|
match = " LIKE "
|
2004-07-18 09:41:53 +04:00
|
|
|
elif query_entry.match == "glob":
|
|
|
|
match = " REGEXP "
|
|
|
|
# use fnmatch to translate the glob into a regexp
|
|
|
|
data = fnmatch.translate(data)
|
|
|
|
if data[0] != '^': data = '^' + data
|
2001-08-21 23:24:40 +04:00
|
|
|
elif query_entry.match == "regex":
|
|
|
|
match = " REGEXP "
|
2004-07-18 09:41:53 +04:00
|
|
|
elif query_entry.match == "notregex":
|
|
|
|
match = " NOT REGEXP "
|
2001-08-21 23:24:40 +04:00
|
|
|
|
2005-12-17 18:40:52 +03:00
|
|
|
sqlList.append("%s%s%s" % (field, match, self.db.literal(data)))
|
2001-08-21 23:24:40 +04:00
|
|
|
|
|
|
|
return "(%s)" % (string.join(sqlList, " OR "))
|
|
|
|
|
|
|
|
def CreateSQLQueryString(self, query):
|
2004-07-18 09:41:53 +04:00
|
|
|
tableList = [("checkins", None)]
|
2001-08-21 23:24:40 +04:00
|
|
|
condList = []
|
|
|
|
|
|
|
|
if len(query.repository_list):
|
2004-07-18 09:41:53 +04:00
|
|
|
tableList.append(("repositories",
|
|
|
|
"(checkins.repositoryid=repositories.id)"))
|
2004-08-01 18:33:00 +04:00
|
|
|
temp = self.SQLQueryListString("repositories.repository",
|
|
|
|
query.repository_list)
|
2001-08-21 23:24:40 +04:00
|
|
|
condList.append(temp)
|
|
|
|
|
|
|
|
if len(query.branch_list):
|
2004-07-18 09:41:53 +04:00
|
|
|
tableList.append(("branches", "(checkins.branchid=branches.id)"))
|
2004-08-01 18:33:00 +04:00
|
|
|
temp = self.SQLQueryListString("branches.branch",
|
|
|
|
query.branch_list)
|
2001-08-21 23:24:40 +04:00
|
|
|
condList.append(temp)
|
|
|
|
|
|
|
|
if len(query.directory_list):
|
2004-07-18 09:41:53 +04:00
|
|
|
tableList.append(("dirs", "(checkins.dirid=dirs.id)"))
|
2004-08-01 18:33:00 +04:00
|
|
|
temp = self.SQLQueryListString("dirs.dir", query.directory_list)
|
2001-08-21 23:24:40 +04:00
|
|
|
condList.append(temp)
|
|
|
|
|
|
|
|
if len(query.file_list):
|
2004-07-18 09:41:53 +04:00
|
|
|
tableList.append(("files", "(checkins.fileid=files.id)"))
|
2004-08-01 18:33:00 +04:00
|
|
|
temp = self.SQLQueryListString("files.file", query.file_list)
|
2001-08-21 23:24:40 +04:00
|
|
|
condList.append(temp)
|
|
|
|
|
|
|
|
if len(query.author_list):
|
2004-07-18 09:41:53 +04:00
|
|
|
tableList.append(("people", "(checkins.whoid=people.id)"))
|
2004-08-01 18:33:00 +04:00
|
|
|
temp = self.SQLQueryListString("people.who", query.author_list)
|
2001-08-21 23:24:40 +04:00
|
|
|
condList.append(temp)
|
|
|
|
|
2007-01-23 19:22:40 +03:00
|
|
|
if len(query.comment_list):
|
|
|
|
tableList.append(("descs", "(checkins.descid=descs.id)"))
|
|
|
|
temp = self.SQLQueryListString("descs.description",
|
|
|
|
query.comment_list)
|
|
|
|
condList.append(temp)
|
|
|
|
|
2001-08-21 23:24:40 +04:00
|
|
|
if query.from_date:
|
|
|
|
temp = "(checkins.ci_when>=\"%s\")" % (str(query.from_date))
|
|
|
|
condList.append(temp)
|
|
|
|
|
|
|
|
if query.to_date:
|
|
|
|
temp = "(checkins.ci_when<=\"%s\")" % (str(query.to_date))
|
|
|
|
condList.append(temp)
|
|
|
|
|
|
|
|
if query.sort == "date":
|
2003-02-18 14:55:26 +03:00
|
|
|
order_by = "ORDER BY checkins.ci_when DESC,descid"
|
2001-08-21 23:24:40 +04:00
|
|
|
elif query.sort == "author":
|
2004-07-18 09:41:53 +04:00
|
|
|
tableList.append(("people", "(checkins.whoid=people.id)"))
|
2004-07-17 10:03:33 +04:00
|
|
|
order_by = "ORDER BY people.who,descid"
|
2001-08-21 23:24:40 +04:00
|
|
|
elif query.sort == "file":
|
2004-07-18 09:41:53 +04:00
|
|
|
tableList.append(("files", "(checkins.fileid=files.id)"))
|
2004-07-17 10:03:33 +04:00
|
|
|
order_by = "ORDER BY files.file,descid"
|
2001-08-21 23:24:40 +04:00
|
|
|
|
2004-07-18 09:41:53 +04:00
|
|
|
## exclude duplicates from the table list, and split out join
|
|
|
|
## conditions from table names. In future, the join conditions
|
|
|
|
## might be handled by INNER JOIN statements instead of WHERE
|
|
|
|
## clauses, but MySQL 3.22 apparently doesn't support them well.
|
|
|
|
tables = []
|
|
|
|
joinConds = []
|
|
|
|
for (table, cond) in tableList:
|
|
|
|
if table not in tables:
|
|
|
|
tables.append(table)
|
|
|
|
if cond is not None: joinConds.append(cond)
|
|
|
|
|
|
|
|
tables = string.join(tables, ",")
|
|
|
|
conditions = string.join(joinConds + condList, " AND ")
|
2004-10-14 06:07:07 +04:00
|
|
|
conditions = conditions and "WHERE %s" % conditions
|
2001-08-21 23:24:40 +04:00
|
|
|
|
|
|
|
## limit the number of rows requested or we could really slam
|
|
|
|
## a server with a large database
|
|
|
|
limit = ""
|
2005-12-22 08:16:23 +03:00
|
|
|
if query.limit:
|
|
|
|
limit = "LIMIT %s" % (str(query.limit))
|
|
|
|
elif self._row_limit:
|
Tweak logic used to find installed paths ("lib" directory, configuration
file, templates, etc). Specifically, make the following changes:
- Get rid of hardcoded paths in module files ("lib" directory) and
configuration files (viewcvs.conf and mod_python's .htaccess)
- Allow stub scripts (for asp, cgi, and mod_python) to specify
configuration files so it's possible to have multiple configurations of
viewcvs running off a single installation.
- Be more consistent about resolving paths when they aren't hardcoded
(when ViewCVS is running from a source directory). In particular, try
not to depend on the working directory. That way it's legal to run
./standalone.py instead of bin/standalone.py without getting an
ImportError.
- Get rid of global cfg variables in viewcvs.py and cvsdb.py. They led to
all sorts of hacks in other files to pilfer and reset them. They were
also possible sources of races in multithreaded environments like
mod_python and asp.
- Rewrite mod_python handler so library paths can be specified inside the
stub files.
* lib/apache.py
removed, contained old mod_python handler
* lib/config.py
(Config.load_config):
remove sys.argv voodoo, just load the configuration path specified in
the pathname argument
(Config.set_defaults):
use relative path for cvsgraph_conf setting instead of hardcoded
<VIEWCVS_INSTALL_DIRECTORY> literal
* lib/cvsdb.py
(CONF_PATHNAME, config, cfg):
removed, configuration stuff
(CheckinDatabase.__init__, CheckinDatabase.CreateSQLQueryString):
add "_row_limit" member instead of using cfg object
(CreateCheckinDatabase):
removed, a do-nothing function
(ConnectDatabaseReadOnly, ConnectDatabase):
add cfg arguments
(GetUnrecordedCommitList):
add db argument
* lib/query.py
(CONF_PATHAME):
removed
(build_commit, run_query, main):
add cfg arguments, use new viewcvs.get_template function
* lib/viewcvs.py
(CONF_PATHNAME, cfg, g_install_dir):
removed
(Request.__init__):
add cfg member
(Request.run_viewcvs, Request.get_link, check_freshness,
get_view_template, generate_page, default_view, get_file_view_info,
format_log, common_template_data, MarkupEnscript.__init__,
markup_stream_python, markup_stream_php, make_time_string, view_markup,
sort_file_data, view_directory, paging, view_log, view_annotate,
view_cvsgraph_image, view_cvsgraph, view_doc, rcsdiff_date_reformat,
spaced_html_text, DiffSource.__init__, DiffSource._get_row, view_patch,
view_diff, generate_tarball, download_tarball, view_revision,
is_query_supported, english_query, build_commit, view_query,
view_error, main):
stop using global config, use cfg arguments or request member instead
(_install_path):
new, use __file__ to locate template and configuation paths
(get_view_template):
use _install_path and return compiled template instead of path
(is_viewable, default_view):
rename is_viewable to default_view and return view instead of boolean
(handle_config, load_config):
rename handle_config to load_config and return config object instead
of setting cfg global
* bin/cgi/viewcvs.cgi
* bin/cgi/query.cgi
* bin/cvsdbadmin
* bin/loginfo-handler
* bin/standalone.py
* bin/svndbadmin
look for library relative to sys.argv[0] when no hardcoded library
path is available. Also add configuration loading code.
* bin/asp/viewcvs.asp
* bin/asp/query.asp
add configuration loading code
* bin/mod_python/.htaccess
specify new mod_python handler, remove reference to
<VIEWCVS_APACHE_LIBRARY_DIRECTORY>
* bin/mod_python/handler.py
added, holds new mod_python handler
* bin/mod_python/viewcvs.py
* bin/mod_python/query.py
rewrite for new handler, add hardcoded library and configuration paths
* viewcvs.conf.dist
remove references to <VIEWCVS_INSTALL_DIRECTORY>
* viewcvs-install
(FILE_INFO_LIST):
stop hardcoding paths in config files
(SetPythonPaths,):
get rid of <VIEWCVS_INSTALL_DIRECTORY> and
<VIEWCVS_APACHE_LIBRARY_DIRECTORY> substitutions
(ApacheEscape, _re_apache):
removed
(InstallTree):
stop hardcoding paths in lib directory
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@1173 8cb11bc2-c004-0410-86c3-e597b4017df7
2005-12-06 07:04:14 +03:00
|
|
|
limit = "LIMIT %s" % (str(self._row_limit))
|
2001-08-21 23:24:40 +04:00
|
|
|
|
2004-10-14 06:07:07 +04:00
|
|
|
sql = "SELECT checkins.* FROM %s %s %s %s" % (
|
2001-08-21 23:24:40 +04:00
|
|
|
tables, conditions, order_by, limit)
|
|
|
|
|
|
|
|
return sql
|
|
|
|
|
|
|
|
def RunQuery(self, query):
|
|
|
|
sql = self.CreateSQLQueryString(query)
|
|
|
|
cursor = self.db.cursor()
|
|
|
|
cursor.execute(sql)
|
|
|
|
|
|
|
|
while 1:
|
|
|
|
row = cursor.fetchone()
|
|
|
|
if not row:
|
|
|
|
break
|
|
|
|
|
|
|
|
(dbType, dbCI_When, dbAuthorID, dbRepositoryID, dbDirID,
|
|
|
|
dbFileID, dbRevision, dbStickyTag, dbBranchID, dbAddedLines,
|
|
|
|
dbRemovedLines, dbDescID) = row
|
|
|
|
|
2005-12-23 16:24:26 +03:00
|
|
|
commit = LazyCommit(self)
|
2004-07-17 10:03:33 +04:00
|
|
|
if dbType == 'Add':
|
|
|
|
commit.SetTypeAdd()
|
|
|
|
elif dbType == 'Remove':
|
|
|
|
commit.SetTypeRemove()
|
|
|
|
else:
|
|
|
|
commit.SetTypeChange()
|
2003-02-09 12:33:12 +03:00
|
|
|
commit.SetTime(dbi.TicksFromDateTime(dbCI_When))
|
2005-12-23 16:24:26 +03:00
|
|
|
commit.SetFileID(dbFileID)
|
|
|
|
commit.SetDirectoryID(dbDirID)
|
2001-08-21 23:24:40 +04:00
|
|
|
commit.SetRevision(dbRevision)
|
2005-12-23 16:24:26 +03:00
|
|
|
commit.SetRepositoryID(dbRepositoryID)
|
|
|
|
commit.SetAuthorID(dbAuthorID)
|
|
|
|
commit.SetBranchID(dbBranchID)
|
2001-08-21 23:24:40 +04:00
|
|
|
commit.SetPlusCount(dbAddedLines)
|
|
|
|
commit.SetMinusCount(dbRemovedLines)
|
2005-12-23 16:24:26 +03:00
|
|
|
commit.SetDescriptionID(dbDescID)
|
2001-08-21 23:24:40 +04:00
|
|
|
|
|
|
|
query.AddCommit(commit)
|
|
|
|
|
|
|
|
def CheckCommit(self, commit):
|
|
|
|
repository_id = self.GetRepositoryID(commit.GetRepository(), 0)
|
|
|
|
if repository_id == None:
|
|
|
|
return None
|
|
|
|
|
|
|
|
dir_id = self.GetDirectoryID(commit.GetDirectory(), 0)
|
|
|
|
if dir_id == None:
|
|
|
|
return None
|
|
|
|
|
|
|
|
file_id = self.GetFileID(commit.GetFile(), 0)
|
|
|
|
if file_id == None:
|
|
|
|
return None
|
|
|
|
|
|
|
|
sql = "SELECT * FROM checkins WHERE "\
|
|
|
|
" repositoryid=%s AND dirid=%s AND fileid=%s AND revision=%s"
|
|
|
|
sql_args = (repository_id, dir_id, file_id, commit.GetRevision())
|
|
|
|
|
|
|
|
cursor = self.db.cursor()
|
|
|
|
cursor.execute(sql, sql_args)
|
|
|
|
try:
|
|
|
|
(ci_type, ci_when, who_id, repository_id,
|
|
|
|
dir_id, file_id, revision, sticky_tag, branch_id,
|
|
|
|
plus_count, minus_count, description_id) = cursor.fetchone()
|
|
|
|
except TypeError:
|
|
|
|
return None
|
|
|
|
|
|
|
|
return commit
|
|
|
|
|
For Issue #271, implement 'purge' commands for both cvsdbadmin and
svndbadmin. Teach the 'rebuild' commands to first purge existing data
and then crawl the repository. Also, drop support for the 'rev'
parameter to 'svndbadmin rebuild', adding instead a '--force' option
to 'svndbadmin update'.
Suggested, and SQL commands offered, by Mark <mark@mitsein.net>.
* lib/cvsdb.py
(CheckinDatabase.sql_delete, CheckinDatabase.PurgeRepository): New.
* bin/svndbadmin
(handle_revision): Add 'force' parameter, used to force update of
commits already recorded in the database.
(main): Add 'force' parameter, passed on to handle_revision().
Handle the new 'purge' command, and teach 'rebuild' to also purge.
(usage): Update usage info.
(__main__): Add support for 'update --force' and 'purge', and drop
support for 'rebuild rev'. Add a KeyboardInterrupt handler.
* bin/cvsdbadmin
(usage): Add 'purge' usage info.
(__main__): Rework command-line parameter handling. Add support for
'purge' command, and make 'rebuild' first do a purge.
* CHANGES
Note this change.
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@1493 8cb11bc2-c004-0410-86c3-e597b4017df7
2006-12-21 23:51:24 +03:00
|
|
|
def sql_delete(self, table, key, value):
|
|
|
|
sql = "DELETE FROM %s WHERE %s=%%s" % (table, key)
|
|
|
|
sql_args = (value, )
|
|
|
|
cursor = self.db.cursor()
|
|
|
|
cursor.execute(sql, sql_args)
|
|
|
|
|
|
|
|
def PurgeRepository(self, repository):
|
|
|
|
rep_id = self.GetRepositoryID(repository)
|
|
|
|
if not rep_id:
|
|
|
|
raise Exception, "Unknown repository '%s'" % (repository)
|
|
|
|
|
|
|
|
sql = "SELECT * FROM checkins WHERE repositoryid=%s"
|
|
|
|
sql_args = (rep_id, )
|
|
|
|
cursor = self.db.cursor()
|
|
|
|
cursor.execute(sql, sql_args)
|
|
|
|
checkins = []
|
|
|
|
while 1:
|
|
|
|
try:
|
|
|
|
(ci_type, ci_when, who_id, repository_id,
|
|
|
|
dir_id, file_id, revision, sticky_tag, branch_id,
|
|
|
|
plus_count, minus_count, description_id) = cursor.fetchone()
|
|
|
|
except TypeError:
|
|
|
|
break
|
|
|
|
checkins.append([file_id, dir_id, branch_id, description_id])
|
|
|
|
|
|
|
|
#self.sql_delete('repositories', 'id', rep_id)
|
|
|
|
self.sql_delete('checkins', 'repositoryid', rep_id)
|
|
|
|
for checkin in checkins:
|
|
|
|
self.sql_delete('files', 'id', checkin[0])
|
|
|
|
self.sql_delete('dirs', 'id', checkin[1])
|
|
|
|
self.sql_delete('branches', 'id', checkin[2])
|
|
|
|
self.sql_delete('descs', 'id', checkin[3])
|
|
|
|
|
|
|
|
|
2001-08-21 23:24:40 +04:00
|
|
|
## the Commit class holds data on one commit, the representation is as
|
|
|
|
## close as possible to how it should be committed and retrieved to the
|
|
|
|
## database engine
|
|
|
|
class Commit:
|
|
|
|
## static constants for type of commit
|
|
|
|
CHANGE = 0
|
|
|
|
ADD = 1
|
|
|
|
REMOVE = 2
|
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
self.__directory = ''
|
|
|
|
self.__file = ''
|
|
|
|
self.__repository = ''
|
|
|
|
self.__revision = ''
|
|
|
|
self.__author = ''
|
|
|
|
self.__branch = ''
|
|
|
|
self.__pluscount = ''
|
|
|
|
self.__minuscount = ''
|
|
|
|
self.__description = ''
|
|
|
|
self.__gmt_time = 0.0
|
|
|
|
self.__type = Commit.CHANGE
|
|
|
|
|
|
|
|
def SetRepository(self, repository):
|
|
|
|
self.__repository = repository
|
|
|
|
|
|
|
|
def GetRepository(self):
|
|
|
|
return self.__repository
|
|
|
|
|
|
|
|
def SetDirectory(self, dir):
|
|
|
|
self.__directory = dir
|
|
|
|
|
|
|
|
def GetDirectory(self):
|
|
|
|
return self.__directory
|
|
|
|
|
|
|
|
def SetFile(self, file):
|
|
|
|
self.__file = file
|
|
|
|
|
|
|
|
def GetFile(self):
|
|
|
|
return self.__file
|
|
|
|
|
|
|
|
def SetRevision(self, revision):
|
|
|
|
self.__revision = revision
|
|
|
|
|
|
|
|
def GetRevision(self):
|
|
|
|
return self.__revision
|
|
|
|
|
|
|
|
def SetTime(self, gmt_time):
|
2007-02-26 23:20:04 +03:00
|
|
|
if gmt_time is None:
|
|
|
|
### We're just going to assume that a datestamp of The Epoch
|
|
|
|
### ain't real.
|
|
|
|
self.__gmt_time = 0.0
|
|
|
|
else:
|
|
|
|
self.__gmt_time = float(gmt_time)
|
2001-08-21 23:24:40 +04:00
|
|
|
|
|
|
|
def GetTime(self):
|
2007-02-26 23:20:04 +03:00
|
|
|
return self.__gmt_time and self.__gmt_time or None
|
2001-08-21 23:24:40 +04:00
|
|
|
|
|
|
|
def SetAuthor(self, author):
|
|
|
|
self.__author = author
|
|
|
|
|
|
|
|
def GetAuthor(self):
|
|
|
|
return self.__author
|
|
|
|
|
|
|
|
def SetBranch(self, branch):
|
2004-10-16 06:40:10 +04:00
|
|
|
self.__branch = branch or ''
|
2001-08-21 23:24:40 +04:00
|
|
|
|
|
|
|
def GetBranch(self):
|
|
|
|
return self.__branch
|
|
|
|
|
|
|
|
def SetPlusCount(self, pluscount):
|
|
|
|
self.__pluscount = pluscount
|
|
|
|
|
|
|
|
def GetPlusCount(self):
|
|
|
|
return self.__pluscount
|
|
|
|
|
|
|
|
def SetMinusCount(self, minuscount):
|
|
|
|
self.__minuscount = minuscount
|
|
|
|
|
|
|
|
def GetMinusCount(self):
|
|
|
|
return self.__minuscount
|
|
|
|
|
|
|
|
def SetDescription(self, description):
|
|
|
|
self.__description = description
|
|
|
|
|
|
|
|
def GetDescription(self):
|
|
|
|
return self.__description
|
|
|
|
|
|
|
|
def SetTypeChange(self):
|
|
|
|
self.__type = Commit.CHANGE
|
|
|
|
|
|
|
|
def SetTypeAdd(self):
|
|
|
|
self.__type = Commit.ADD
|
|
|
|
|
|
|
|
def SetTypeRemove(self):
|
|
|
|
self.__type = Commit.REMOVE
|
|
|
|
|
|
|
|
def GetType(self):
|
|
|
|
return self.__type
|
|
|
|
|
|
|
|
def GetTypeString(self):
|
|
|
|
if self.__type == Commit.CHANGE:
|
|
|
|
return 'Change'
|
|
|
|
elif self.__type == Commit.ADD:
|
|
|
|
return 'Add'
|
|
|
|
elif self.__type == Commit.REMOVE:
|
|
|
|
return 'Remove'
|
|
|
|
|
2005-12-23 16:24:26 +03:00
|
|
|
## LazyCommit overrides a few methods of Commit to only retrieve
|
|
|
|
## it's properties as they are needed
|
|
|
|
class LazyCommit(Commit):
|
|
|
|
def __init__(self, db):
|
|
|
|
Commit.__init__(self)
|
|
|
|
self.__db = db
|
|
|
|
|
|
|
|
def SetFileID(self, dbFileID):
|
|
|
|
self.__dbFileID = dbFileID
|
|
|
|
|
|
|
|
def GetFileID(self):
|
|
|
|
return self.__dbFileID
|
|
|
|
|
|
|
|
def GetFile(self):
|
|
|
|
return self.__db.GetFile(self.__dbFileID)
|
|
|
|
|
|
|
|
def SetDirectoryID(self, dbDirID):
|
|
|
|
self.__dbDirID = dbDirID
|
|
|
|
|
|
|
|
def GetDirectoryID(self):
|
|
|
|
return self.__dbDirID
|
|
|
|
|
|
|
|
def GetDirectory(self):
|
|
|
|
return self.__db.GetDirectory(self.__dbDirID)
|
|
|
|
|
|
|
|
def SetRepositoryID(self, dbRepositoryID):
|
|
|
|
self.__dbRepositoryID = dbRepositoryID
|
|
|
|
|
|
|
|
def GetRepositoryID(self):
|
|
|
|
return self.__dbRepositoryID
|
|
|
|
|
|
|
|
def GetRepository(self):
|
|
|
|
return self.__db.GetRepository(self.__dbRepositoryID)
|
|
|
|
|
|
|
|
def SetAuthorID(self, dbAuthorID):
|
|
|
|
self.__dbAuthorID = dbAuthorID
|
|
|
|
|
|
|
|
def GetAuthorID(self):
|
|
|
|
return self.__dbAuthorID
|
|
|
|
|
|
|
|
def GetAuthor(self):
|
|
|
|
return self.__db.GetAuthor(self.__dbAuthorID)
|
|
|
|
|
|
|
|
def SetBranchID(self, dbBranchID):
|
|
|
|
self.__dbBranchID = dbBranchID
|
|
|
|
|
|
|
|
def GetBranchID(self):
|
|
|
|
return self.__dbBranchID
|
|
|
|
|
|
|
|
def GetBranch(self):
|
|
|
|
return self.__db.GetBranch(self.__dbBranchID)
|
|
|
|
|
|
|
|
def SetDescriptionID(self, dbDescID):
|
|
|
|
self.__dbDescID = dbDescID
|
|
|
|
|
|
|
|
def GetDescriptionID(self):
|
|
|
|
return self.__dbDescID
|
|
|
|
|
|
|
|
def GetDescription(self):
|
|
|
|
return self.__db.GetDescription(self.__dbDescID)
|
|
|
|
|
2001-08-21 23:24:40 +04:00
|
|
|
## QueryEntry holds data on one match-type in the SQL database
|
|
|
|
## match is: "exact", "like", or "regex"
|
|
|
|
class QueryEntry:
|
|
|
|
def __init__(self, data, match):
|
|
|
|
self.data = data
|
|
|
|
self.match = match
|
|
|
|
|
|
|
|
## CheckinDatabaseQueryData is a object which contains the search parameters
|
|
|
|
## for a query to the CheckinDatabase
|
|
|
|
class CheckinDatabaseQuery:
|
|
|
|
def __init__(self):
|
|
|
|
## sorting
|
|
|
|
self.sort = "date"
|
|
|
|
|
|
|
|
## repository to query
|
|
|
|
self.repository_list = []
|
|
|
|
self.branch_list = []
|
|
|
|
self.directory_list = []
|
|
|
|
self.file_list = []
|
|
|
|
self.author_list = []
|
2007-01-23 19:22:40 +03:00
|
|
|
self.comment_list = []
|
2001-08-21 23:24:40 +04:00
|
|
|
|
|
|
|
## date range in DBI 2.0 timedate objects
|
|
|
|
self.from_date = None
|
|
|
|
self.to_date = None
|
|
|
|
|
2005-12-22 08:16:23 +03:00
|
|
|
## limit on number of rows to return
|
|
|
|
self.limit = None
|
|
|
|
|
2001-08-21 23:24:40 +04:00
|
|
|
## list of commits -- filled in by CVS query
|
|
|
|
self.commit_list = []
|
|
|
|
|
|
|
|
## commit_cb provides a callback for commits as they
|
|
|
|
## are added
|
|
|
|
self.commit_cb = None
|
|
|
|
|
|
|
|
def SetRepository(self, repository, match = "exact"):
|
2004-10-16 04:47:42 +04:00
|
|
|
self.repository_list.append(QueryEntry(repository, match))
|
2001-08-21 23:24:40 +04:00
|
|
|
|
|
|
|
def SetBranch(self, branch, match = "exact"):
|
|
|
|
self.branch_list.append(QueryEntry(branch, match))
|
|
|
|
|
|
|
|
def SetDirectory(self, directory, match = "exact"):
|
2004-10-16 04:47:42 +04:00
|
|
|
self.directory_list.append(QueryEntry(directory, match))
|
2001-08-21 23:24:40 +04:00
|
|
|
|
|
|
|
def SetFile(self, file, match = "exact"):
|
|
|
|
self.file_list.append(QueryEntry(file, match))
|
|
|
|
|
|
|
|
def SetAuthor(self, author, match = "exact"):
|
|
|
|
self.author_list.append(QueryEntry(author, match))
|
|
|
|
|
2007-01-23 19:22:40 +03:00
|
|
|
def SetComment(self, comment, match = "exact"):
|
|
|
|
self.comment_list.append(QueryEntry(comment, match))
|
|
|
|
|
2001-08-21 23:24:40 +04:00
|
|
|
def SetSortMethod(self, sort):
|
|
|
|
self.sort = sort
|
|
|
|
|
|
|
|
def SetFromDateObject(self, ticks):
|
2003-02-09 12:33:12 +03:00
|
|
|
self.from_date = dbi.DateTimeFromTicks(ticks)
|
2001-08-21 23:24:40 +04:00
|
|
|
|
|
|
|
def SetToDateObject(self, ticks):
|
2003-02-09 12:33:12 +03:00
|
|
|
self.to_date = dbi.DateTimeFromTicks(ticks)
|
2001-08-21 23:24:40 +04:00
|
|
|
|
|
|
|
def SetFromDateHoursAgo(self, hours_ago):
|
|
|
|
ticks = time.time() - (3600 * hours_ago)
|
2003-02-09 12:33:12 +03:00
|
|
|
self.from_date = dbi.DateTimeFromTicks(ticks)
|
2001-08-21 23:24:40 +04:00
|
|
|
|
|
|
|
def SetFromDateDaysAgo(self, days_ago):
|
|
|
|
ticks = time.time() - (86400 * days_ago)
|
2003-02-09 12:33:12 +03:00
|
|
|
self.from_date = dbi.DateTimeFromTicks(ticks)
|
2001-08-21 23:24:40 +04:00
|
|
|
|
|
|
|
def SetToDateDaysAgo(self, days_ago):
|
|
|
|
ticks = time.time() - (86400 * days_ago)
|
2003-02-09 12:33:12 +03:00
|
|
|
self.to_date = dbi.DateTimeFromTicks(ticks)
|
2001-08-21 23:24:40 +04:00
|
|
|
|
2005-12-22 08:16:23 +03:00
|
|
|
def SetLimit(self, limit):
|
|
|
|
self.limit = limit;
|
|
|
|
|
2001-08-21 23:24:40 +04:00
|
|
|
def AddCommit(self, commit):
|
|
|
|
self.commit_list.append(commit)
|
|
|
|
|
|
|
|
|
|
|
|
##
|
|
|
|
## entrypoints
|
|
|
|
##
|
|
|
|
def CreateCommit():
|
|
|
|
return Commit()
|
|
|
|
|
|
|
|
def CreateCheckinQuery():
|
|
|
|
return CheckinDatabaseQuery()
|
|
|
|
|
2007-04-09 17:45:18 +04:00
|
|
|
def ConnectDatabase(cfg, readonly=0):
|
|
|
|
if readonly:
|
|
|
|
user = cfg.cvsdb.readonly_user
|
|
|
|
passwd = cfg.cvsdb.readonly_passwd
|
|
|
|
else:
|
|
|
|
user = cfg.cvsdb.user
|
|
|
|
passwd = cfg.cvsdb.passwd
|
|
|
|
db = CheckinDatabase(cfg.cvsdb.host, cfg.cvsdb.port, user, passwd,
|
|
|
|
cfg.cvsdb.database_name, cfg.cvsdb.row_limit)
|
|
|
|
db.Connect()
|
|
|
|
return db
|
|
|
|
|
Tweak logic used to find installed paths ("lib" directory, configuration
file, templates, etc). Specifically, make the following changes:
- Get rid of hardcoded paths in module files ("lib" directory) and
configuration files (viewcvs.conf and mod_python's .htaccess)
- Allow stub scripts (for asp, cgi, and mod_python) to specify
configuration files so it's possible to have multiple configurations of
viewcvs running off a single installation.
- Be more consistent about resolving paths when they aren't hardcoded
(when ViewCVS is running from a source directory). In particular, try
not to depend on the working directory. That way it's legal to run
./standalone.py instead of bin/standalone.py without getting an
ImportError.
- Get rid of global cfg variables in viewcvs.py and cvsdb.py. They led to
all sorts of hacks in other files to pilfer and reset them. They were
also possible sources of races in multithreaded environments like
mod_python and asp.
- Rewrite mod_python handler so library paths can be specified inside the
stub files.
* lib/apache.py
removed, contained old mod_python handler
* lib/config.py
(Config.load_config):
remove sys.argv voodoo, just load the configuration path specified in
the pathname argument
(Config.set_defaults):
use relative path for cvsgraph_conf setting instead of hardcoded
<VIEWCVS_INSTALL_DIRECTORY> literal
* lib/cvsdb.py
(CONF_PATHNAME, config, cfg):
removed, configuration stuff
(CheckinDatabase.__init__, CheckinDatabase.CreateSQLQueryString):
add "_row_limit" member instead of using cfg object
(CreateCheckinDatabase):
removed, a do-nothing function
(ConnectDatabaseReadOnly, ConnectDatabase):
add cfg arguments
(GetUnrecordedCommitList):
add db argument
* lib/query.py
(CONF_PATHAME):
removed
(build_commit, run_query, main):
add cfg arguments, use new viewcvs.get_template function
* lib/viewcvs.py
(CONF_PATHNAME, cfg, g_install_dir):
removed
(Request.__init__):
add cfg member
(Request.run_viewcvs, Request.get_link, check_freshness,
get_view_template, generate_page, default_view, get_file_view_info,
format_log, common_template_data, MarkupEnscript.__init__,
markup_stream_python, markup_stream_php, make_time_string, view_markup,
sort_file_data, view_directory, paging, view_log, view_annotate,
view_cvsgraph_image, view_cvsgraph, view_doc, rcsdiff_date_reformat,
spaced_html_text, DiffSource.__init__, DiffSource._get_row, view_patch,
view_diff, generate_tarball, download_tarball, view_revision,
is_query_supported, english_query, build_commit, view_query,
view_error, main):
stop using global config, use cfg arguments or request member instead
(_install_path):
new, use __file__ to locate template and configuation paths
(get_view_template):
use _install_path and return compiled template instead of path
(is_viewable, default_view):
rename is_viewable to default_view and return view instead of boolean
(handle_config, load_config):
rename handle_config to load_config and return config object instead
of setting cfg global
* bin/cgi/viewcvs.cgi
* bin/cgi/query.cgi
* bin/cvsdbadmin
* bin/loginfo-handler
* bin/standalone.py
* bin/svndbadmin
look for library relative to sys.argv[0] when no hardcoded library
path is available. Also add configuration loading code.
* bin/asp/viewcvs.asp
* bin/asp/query.asp
add configuration loading code
* bin/mod_python/.htaccess
specify new mod_python handler, remove reference to
<VIEWCVS_APACHE_LIBRARY_DIRECTORY>
* bin/mod_python/handler.py
added, holds new mod_python handler
* bin/mod_python/viewcvs.py
* bin/mod_python/query.py
rewrite for new handler, add hardcoded library and configuration paths
* viewcvs.conf.dist
remove references to <VIEWCVS_INSTALL_DIRECTORY>
* viewcvs-install
(FILE_INFO_LIST):
stop hardcoding paths in config files
(SetPythonPaths,):
get rid of <VIEWCVS_INSTALL_DIRECTORY> and
<VIEWCVS_APACHE_LIBRARY_DIRECTORY> substitutions
(ApacheEscape, _re_apache):
removed
(InstallTree):
stop hardcoding paths in lib directory
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@1173 8cb11bc2-c004-0410-86c3-e597b4017df7
2005-12-06 07:04:14 +03:00
|
|
|
def ConnectDatabaseReadOnly(cfg):
|
2007-04-09 17:45:18 +04:00
|
|
|
return ConnectDatabase(cfg, 1)
|
2001-08-21 23:24:40 +04:00
|
|
|
|
Get rid of rlog module, make CVSdb use bincvs instead.
* lib/rlog.py
deleted
* lib/cvsdb.py
(RLogDataToCommitList):
removed
(GetCommitListFromRCSFile):
change to use vclib instead of rlog module
(GetCommitListFromRCSFile, GetUnrecordedCommitList):
update to accept vclib-style path_parts arguments
* lib/vclib/bincvs/__init__.py
(BinCVSRepository.filelog):
accept "cvs_pass_rev" option for passing -r arguments to rlog
(_match_revs_tags):
make this function work with an incomplete list of revisions
instead of crashing. This is neccessary when an -r argument
is passed to rlog.
(_add_tag):
make this function work when we need to create a tag with no
revision object. We need this to create a HEAD tag when an -r
argument is passed to rlog and we don't know what the HEAD
revision is.
(fetch_log):
removed
* tools/cvsdbadmin
(RebuildFile):
removed, merged into UpdateFile
(UpdateFile)
accept new argument to reinsert all commits
(RecurseRebuild):
removed, merged into RecurseUpdate
(RecurseUpdate):
update to use vclib's listdir instead of os.listdir
(CommandRebuild, CommandUpdate):
removed, code moved into __main__ section
* tools/loginfo-handler
(FileData, CommitFromFileData, GetUnrecordedCommitList):
removed, these were just wrappers over the cvsdb interface
(HeuristicArgParse, CvsNtArgParse)
return tuples describing file data instead of file data objects
(ProcessLoginfo):
update to use cvsdb interface
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@953 8cb11bc2-c004-0410-86c3-e597b4017df7
2004-10-16 06:23:23 +04:00
|
|
|
def GetCommitListFromRCSFile(repository, path_parts, revision=None):
|
2001-08-21 23:24:40 +04:00
|
|
|
commit_list = []
|
|
|
|
|
Get rid of rlog module, make CVSdb use bincvs instead.
* lib/rlog.py
deleted
* lib/cvsdb.py
(RLogDataToCommitList):
removed
(GetCommitListFromRCSFile):
change to use vclib instead of rlog module
(GetCommitListFromRCSFile, GetUnrecordedCommitList):
update to accept vclib-style path_parts arguments
* lib/vclib/bincvs/__init__.py
(BinCVSRepository.filelog):
accept "cvs_pass_rev" option for passing -r arguments to rlog
(_match_revs_tags):
make this function work with an incomplete list of revisions
instead of crashing. This is neccessary when an -r argument
is passed to rlog.
(_add_tag):
make this function work when we need to create a tag with no
revision object. We need this to create a HEAD tag when an -r
argument is passed to rlog and we don't know what the HEAD
revision is.
(fetch_log):
removed
* tools/cvsdbadmin
(RebuildFile):
removed, merged into UpdateFile
(UpdateFile)
accept new argument to reinsert all commits
(RecurseRebuild):
removed, merged into RecurseUpdate
(RecurseUpdate):
update to use vclib's listdir instead of os.listdir
(CommandRebuild, CommandUpdate):
removed, code moved into __main__ section
* tools/loginfo-handler
(FileData, CommitFromFileData, GetUnrecordedCommitList):
removed, these were just wrappers over the cvsdb interface
(HeuristicArgParse, CvsNtArgParse)
return tuples describing file data instead of file data objects
(ProcessLoginfo):
update to use cvsdb interface
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@953 8cb11bc2-c004-0410-86c3-e597b4017df7
2004-10-16 06:23:23 +04:00
|
|
|
directory = string.join(path_parts[:-1], "/")
|
|
|
|
file = path_parts[-1]
|
2001-08-21 23:24:40 +04:00
|
|
|
|
2005-04-09 02:57:02 +04:00
|
|
|
revs = repository.itemlog(path_parts, revision, {"cvs_pass_rev": 1})
|
Get rid of rlog module, make CVSdb use bincvs instead.
* lib/rlog.py
deleted
* lib/cvsdb.py
(RLogDataToCommitList):
removed
(GetCommitListFromRCSFile):
change to use vclib instead of rlog module
(GetCommitListFromRCSFile, GetUnrecordedCommitList):
update to accept vclib-style path_parts arguments
* lib/vclib/bincvs/__init__.py
(BinCVSRepository.filelog):
accept "cvs_pass_rev" option for passing -r arguments to rlog
(_match_revs_tags):
make this function work with an incomplete list of revisions
instead of crashing. This is neccessary when an -r argument
is passed to rlog.
(_add_tag):
make this function work when we need to create a tag with no
revision object. We need this to create a HEAD tag when an -r
argument is passed to rlog and we don't know what the HEAD
revision is.
(fetch_log):
removed
* tools/cvsdbadmin
(RebuildFile):
removed, merged into UpdateFile
(UpdateFile)
accept new argument to reinsert all commits
(RecurseRebuild):
removed, merged into RecurseUpdate
(RecurseUpdate):
update to use vclib's listdir instead of os.listdir
(CommandRebuild, CommandUpdate):
removed, code moved into __main__ section
* tools/loginfo-handler
(FileData, CommitFromFileData, GetUnrecordedCommitList):
removed, these were just wrappers over the cvsdb interface
(HeuristicArgParse, CvsNtArgParse)
return tuples describing file data instead of file data objects
(ProcessLoginfo):
update to use cvsdb interface
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@953 8cb11bc2-c004-0410-86c3-e597b4017df7
2004-10-16 06:23:23 +04:00
|
|
|
for rev in revs:
|
2001-08-21 23:24:40 +04:00
|
|
|
commit = CreateCommit()
|
Get rid of rlog module, make CVSdb use bincvs instead.
* lib/rlog.py
deleted
* lib/cvsdb.py
(RLogDataToCommitList):
removed
(GetCommitListFromRCSFile):
change to use vclib instead of rlog module
(GetCommitListFromRCSFile, GetUnrecordedCommitList):
update to accept vclib-style path_parts arguments
* lib/vclib/bincvs/__init__.py
(BinCVSRepository.filelog):
accept "cvs_pass_rev" option for passing -r arguments to rlog
(_match_revs_tags):
make this function work with an incomplete list of revisions
instead of crashing. This is neccessary when an -r argument
is passed to rlog.
(_add_tag):
make this function work when we need to create a tag with no
revision object. We need this to create a HEAD tag when an -r
argument is passed to rlog and we don't know what the HEAD
revision is.
(fetch_log):
removed
* tools/cvsdbadmin
(RebuildFile):
removed, merged into UpdateFile
(UpdateFile)
accept new argument to reinsert all commits
(RecurseRebuild):
removed, merged into RecurseUpdate
(RecurseUpdate):
update to use vclib's listdir instead of os.listdir
(CommandRebuild, CommandUpdate):
removed, code moved into __main__ section
* tools/loginfo-handler
(FileData, CommitFromFileData, GetUnrecordedCommitList):
removed, these were just wrappers over the cvsdb interface
(HeuristicArgParse, CvsNtArgParse)
return tuples describing file data instead of file data objects
(ProcessLoginfo):
update to use cvsdb interface
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@953 8cb11bc2-c004-0410-86c3-e597b4017df7
2004-10-16 06:23:23 +04:00
|
|
|
commit.SetRepository(repository.rootpath)
|
2001-08-21 23:24:40 +04:00
|
|
|
commit.SetDirectory(directory)
|
|
|
|
commit.SetFile(file)
|
Get rid of rlog module, make CVSdb use bincvs instead.
* lib/rlog.py
deleted
* lib/cvsdb.py
(RLogDataToCommitList):
removed
(GetCommitListFromRCSFile):
change to use vclib instead of rlog module
(GetCommitListFromRCSFile, GetUnrecordedCommitList):
update to accept vclib-style path_parts arguments
* lib/vclib/bincvs/__init__.py
(BinCVSRepository.filelog):
accept "cvs_pass_rev" option for passing -r arguments to rlog
(_match_revs_tags):
make this function work with an incomplete list of revisions
instead of crashing. This is neccessary when an -r argument
is passed to rlog.
(_add_tag):
make this function work when we need to create a tag with no
revision object. We need this to create a HEAD tag when an -r
argument is passed to rlog and we don't know what the HEAD
revision is.
(fetch_log):
removed
* tools/cvsdbadmin
(RebuildFile):
removed, merged into UpdateFile
(UpdateFile)
accept new argument to reinsert all commits
(RecurseRebuild):
removed, merged into RecurseUpdate
(RecurseUpdate):
update to use vclib's listdir instead of os.listdir
(CommandRebuild, CommandUpdate):
removed, code moved into __main__ section
* tools/loginfo-handler
(FileData, CommitFromFileData, GetUnrecordedCommitList):
removed, these were just wrappers over the cvsdb interface
(HeuristicArgParse, CvsNtArgParse)
return tuples describing file data instead of file data objects
(ProcessLoginfo):
update to use cvsdb interface
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@953 8cb11bc2-c004-0410-86c3-e597b4017df7
2004-10-16 06:23:23 +04:00
|
|
|
commit.SetRevision(rev.string)
|
|
|
|
commit.SetAuthor(rev.author)
|
|
|
|
commit.SetDescription(rev.log)
|
|
|
|
commit.SetTime(rev.date)
|
|
|
|
|
|
|
|
if rev.changed:
|
|
|
|
# extract the plus/minus and drop the sign
|
|
|
|
plus, minus = string.split(rev.changed)
|
|
|
|
commit.SetPlusCount(plus[1:])
|
|
|
|
commit.SetMinusCount(minus[1:])
|
|
|
|
|
|
|
|
if rev.dead:
|
|
|
|
commit.SetTypeRemove()
|
|
|
|
else:
|
|
|
|
commit.SetTypeChange()
|
|
|
|
else:
|
2001-08-21 23:24:40 +04:00
|
|
|
commit.SetTypeAdd()
|
|
|
|
|
|
|
|
commit_list.append(commit)
|
|
|
|
|
Get rid of rlog module, make CVSdb use bincvs instead.
* lib/rlog.py
deleted
* lib/cvsdb.py
(RLogDataToCommitList):
removed
(GetCommitListFromRCSFile):
change to use vclib instead of rlog module
(GetCommitListFromRCSFile, GetUnrecordedCommitList):
update to accept vclib-style path_parts arguments
* lib/vclib/bincvs/__init__.py
(BinCVSRepository.filelog):
accept "cvs_pass_rev" option for passing -r arguments to rlog
(_match_revs_tags):
make this function work with an incomplete list of revisions
instead of crashing. This is neccessary when an -r argument
is passed to rlog.
(_add_tag):
make this function work when we need to create a tag with no
revision object. We need this to create a HEAD tag when an -r
argument is passed to rlog and we don't know what the HEAD
revision is.
(fetch_log):
removed
* tools/cvsdbadmin
(RebuildFile):
removed, merged into UpdateFile
(UpdateFile)
accept new argument to reinsert all commits
(RecurseRebuild):
removed, merged into RecurseUpdate
(RecurseUpdate):
update to use vclib's listdir instead of os.listdir
(CommandRebuild, CommandUpdate):
removed, code moved into __main__ section
* tools/loginfo-handler
(FileData, CommitFromFileData, GetUnrecordedCommitList):
removed, these were just wrappers over the cvsdb interface
(HeuristicArgParse, CvsNtArgParse)
return tuples describing file data instead of file data objects
(ProcessLoginfo):
update to use cvsdb interface
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@953 8cb11bc2-c004-0410-86c3-e597b4017df7
2004-10-16 06:23:23 +04:00
|
|
|
# if revision is on a branch which has at least one tag
|
|
|
|
if len(rev.number) > 2 and rev.branches:
|
|
|
|
commit.SetBranch(rev.branches[0].name)
|
2001-08-21 23:24:40 +04:00
|
|
|
|
|
|
|
return commit_list
|
|
|
|
|
Tweak logic used to find installed paths ("lib" directory, configuration
file, templates, etc). Specifically, make the following changes:
- Get rid of hardcoded paths in module files ("lib" directory) and
configuration files (viewcvs.conf and mod_python's .htaccess)
- Allow stub scripts (for asp, cgi, and mod_python) to specify
configuration files so it's possible to have multiple configurations of
viewcvs running off a single installation.
- Be more consistent about resolving paths when they aren't hardcoded
(when ViewCVS is running from a source directory). In particular, try
not to depend on the working directory. That way it's legal to run
./standalone.py instead of bin/standalone.py without getting an
ImportError.
- Get rid of global cfg variables in viewcvs.py and cvsdb.py. They led to
all sorts of hacks in other files to pilfer and reset them. They were
also possible sources of races in multithreaded environments like
mod_python and asp.
- Rewrite mod_python handler so library paths can be specified inside the
stub files.
* lib/apache.py
removed, contained old mod_python handler
* lib/config.py
(Config.load_config):
remove sys.argv voodoo, just load the configuration path specified in
the pathname argument
(Config.set_defaults):
use relative path for cvsgraph_conf setting instead of hardcoded
<VIEWCVS_INSTALL_DIRECTORY> literal
* lib/cvsdb.py
(CONF_PATHNAME, config, cfg):
removed, configuration stuff
(CheckinDatabase.__init__, CheckinDatabase.CreateSQLQueryString):
add "_row_limit" member instead of using cfg object
(CreateCheckinDatabase):
removed, a do-nothing function
(ConnectDatabaseReadOnly, ConnectDatabase):
add cfg arguments
(GetUnrecordedCommitList):
add db argument
* lib/query.py
(CONF_PATHAME):
removed
(build_commit, run_query, main):
add cfg arguments, use new viewcvs.get_template function
* lib/viewcvs.py
(CONF_PATHNAME, cfg, g_install_dir):
removed
(Request.__init__):
add cfg member
(Request.run_viewcvs, Request.get_link, check_freshness,
get_view_template, generate_page, default_view, get_file_view_info,
format_log, common_template_data, MarkupEnscript.__init__,
markup_stream_python, markup_stream_php, make_time_string, view_markup,
sort_file_data, view_directory, paging, view_log, view_annotate,
view_cvsgraph_image, view_cvsgraph, view_doc, rcsdiff_date_reformat,
spaced_html_text, DiffSource.__init__, DiffSource._get_row, view_patch,
view_diff, generate_tarball, download_tarball, view_revision,
is_query_supported, english_query, build_commit, view_query,
view_error, main):
stop using global config, use cfg arguments or request member instead
(_install_path):
new, use __file__ to locate template and configuation paths
(get_view_template):
use _install_path and return compiled template instead of path
(is_viewable, default_view):
rename is_viewable to default_view and return view instead of boolean
(handle_config, load_config):
rename handle_config to load_config and return config object instead
of setting cfg global
* bin/cgi/viewcvs.cgi
* bin/cgi/query.cgi
* bin/cvsdbadmin
* bin/loginfo-handler
* bin/standalone.py
* bin/svndbadmin
look for library relative to sys.argv[0] when no hardcoded library
path is available. Also add configuration loading code.
* bin/asp/viewcvs.asp
* bin/asp/query.asp
add configuration loading code
* bin/mod_python/.htaccess
specify new mod_python handler, remove reference to
<VIEWCVS_APACHE_LIBRARY_DIRECTORY>
* bin/mod_python/handler.py
added, holds new mod_python handler
* bin/mod_python/viewcvs.py
* bin/mod_python/query.py
rewrite for new handler, add hardcoded library and configuration paths
* viewcvs.conf.dist
remove references to <VIEWCVS_INSTALL_DIRECTORY>
* viewcvs-install
(FILE_INFO_LIST):
stop hardcoding paths in config files
(SetPythonPaths,):
get rid of <VIEWCVS_INSTALL_DIRECTORY> and
<VIEWCVS_APACHE_LIBRARY_DIRECTORY> substitutions
(ApacheEscape, _re_apache):
removed
(InstallTree):
stop hardcoding paths in lib directory
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@1173 8cb11bc2-c004-0410-86c3-e597b4017df7
2005-12-06 07:04:14 +03:00
|
|
|
def GetUnrecordedCommitList(repository, path_parts, db):
|
Get rid of rlog module, make CVSdb use bincvs instead.
* lib/rlog.py
deleted
* lib/cvsdb.py
(RLogDataToCommitList):
removed
(GetCommitListFromRCSFile):
change to use vclib instead of rlog module
(GetCommitListFromRCSFile, GetUnrecordedCommitList):
update to accept vclib-style path_parts arguments
* lib/vclib/bincvs/__init__.py
(BinCVSRepository.filelog):
accept "cvs_pass_rev" option for passing -r arguments to rlog
(_match_revs_tags):
make this function work with an incomplete list of revisions
instead of crashing. This is neccessary when an -r argument
is passed to rlog.
(_add_tag):
make this function work when we need to create a tag with no
revision object. We need this to create a HEAD tag when an -r
argument is passed to rlog and we don't know what the HEAD
revision is.
(fetch_log):
removed
* tools/cvsdbadmin
(RebuildFile):
removed, merged into UpdateFile
(UpdateFile)
accept new argument to reinsert all commits
(RecurseRebuild):
removed, merged into RecurseUpdate
(RecurseUpdate):
update to use vclib's listdir instead of os.listdir
(CommandRebuild, CommandUpdate):
removed, code moved into __main__ section
* tools/loginfo-handler
(FileData, CommitFromFileData, GetUnrecordedCommitList):
removed, these were just wrappers over the cvsdb interface
(HeuristicArgParse, CvsNtArgParse)
return tuples describing file data instead of file data objects
(ProcessLoginfo):
update to use cvsdb interface
git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@953 8cb11bc2-c004-0410-86c3-e597b4017df7
2004-10-16 06:23:23 +04:00
|
|
|
commit_list = GetCommitListFromRCSFile(repository, path_parts)
|
2001-08-21 23:24:40 +04:00
|
|
|
|
|
|
|
unrecorded_commit_list = []
|
|
|
|
for commit in commit_list:
|
|
|
|
result = db.CheckCommit(commit)
|
|
|
|
if not result:
|
|
|
|
unrecorded_commit_list.append(commit)
|
|
|
|
|
|
|
|
return unrecorded_commit_list
|
2003-02-11 05:57:21 +03:00
|
|
|
|
2004-10-09 06:32:15 +04:00
|
|
|
_re_likechars = re.compile(r"([_%\\])")
|
|
|
|
|
|
|
|
def EscapeLike(literal):
|
|
|
|
"""Escape literal string for use in a MySQL LIKE pattern"""
|
|
|
|
return re.sub(_re_likechars, r"\\\1", literal)
|
2004-10-16 04:47:42 +04:00
|
|
|
|
2006-02-22 07:26:54 +03:00
|
|
|
def FindRepository(db, path):
|
|
|
|
"""Find repository path in database given path to subdirectory
|
|
|
|
Returns normalized repository path and relative directory path"""
|
|
|
|
path = os.path.normpath(path)
|
|
|
|
dirs = []
|
|
|
|
while path:
|
|
|
|
rep = os.path.normcase(path)
|
|
|
|
if db.GetRepositoryID(rep, 0) is None:
|
|
|
|
path, pdir = os.path.split(path)
|
|
|
|
if not pdir:
|
|
|
|
return None, None
|
|
|
|
dirs.append(pdir)
|
|
|
|
else:
|
|
|
|
break
|
|
|
|
dirs.reverse()
|
|
|
|
return rep, dirs
|
|
|
|
|
2004-10-16 04:47:42 +04:00
|
|
|
def CleanRepository(path):
|
2006-02-22 07:26:54 +03:00
|
|
|
"""Return normalized top-level repository path"""
|
2004-10-16 04:47:42 +04:00
|
|
|
return os.path.normcase(os.path.normpath(path))
|
2006-02-22 07:26:54 +03:00
|
|
|
|