Change vclib interface to accept 'rev' parameters wherever it accepts

path parameters so it is possible to use a single Repository object
to request information about any path at any revision.

Note: I wasn't able to test the svn_ra.get_location function because
I only have subversion 1.2.3.

* lib/vclib/__init__.py
  (Repository.itemtype, Repository.openfile, Repository.listdir,
   Repository.dirlogs, Repository.itemlog, Repository.annotate):
    add rev arguments, update docstrings

* lib/vclib/bincvs/__init__.py
  (CVSRepository.itemtype, CVSRepository.listdir, BinCVSRepository.openfile,
   BinCVSRepository.dirlogs, BinCVSRepository.itemlog):
    same

* lib/vclib/ccvs/__init__.py
  (CCVSRepository.dirlogs, CCVSRepository.itemlog):
    same

* lib/vclib/svn/__init__.py
  (SubversionRepository.__init__):
    remove rev argument

  (get_location, created_rev, _get_history, get_revision_info, _fetch_log,
   _get_last_history_rev, get_logs, do_diff,
   SubversionRepository.itemtype, SubversionRepository.openfile,
   SubversionRepository.listdir, SubversionRepository.dirlogs,
   SubversionRepository.itemlog, SubversionRepository.annotate):
    add rev arguments, use new _getrev and _getroot functions to handle
    all revision string parsing and revision_root creation

  (SubversionRepository._getrev, SubversionRepository._getroot):
    new functions

* lib/vclib/svn_ra/__init__.py
  (_rev2optrev):
    accept integers instead of strings, and eliminate head/unspecified
    revision handling which is already taken care of by _getrev

  (SubversionRepository.__init__):
    remove rev argument

  (get_location, created_rev, get_revision_info, get_logs,
   SubversionRepository.itemtype, SubversionRepository.openfile,
   SubversionRepository.listdir, SubversionRepository.dirlogs,
   SubversionRepository.itemlog, SubversionRepository.annotate,
   SubversionRepository.rawdiff, SubversionRepository._get_dirents):
    add rev arguments, use new _getrev function to handle all revision
    string parsing

  (SubversionRepository._getrev):
    new function

* lib/blame.py
  (link_includes):
    update call to repos.itemtype

* tools/cvsdbadmin
  (RecurseUpdate):
    update call to repos.listdir

* lib/viewcvs.py
  (Request.run_viewcvs):
    update calls to SubversionRepository constructors and _strip_suffix

  (_strip_suffix):
    add rev parameter, remove redundant where parameter

  (_repos_pathtype, view_markup, search_files, _get_diff_path_parts,
   generate_tarball):
    update calls to vclib methods

  (nav_path, view_directory, view_log, setup_diff, download_tarball,
   view_revision):
    explicitly read 'rev' query param instead of relying on
    SubversionRepository.rev, and update vclib calls


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@1144 8cb11bc2-c004-0410-86c3-e597b4017df7
remotes/tags/1.0.0-rc1
rey4 2005-11-03 13:23:42 +00:00
parent 7787585540
commit 5204e26666
8 changed files with 248 additions and 210 deletions

View File

@ -55,7 +55,7 @@ def link_includes(text, repos, path_parts, include_url):
include_path = path_parts[:depth] + [incfile]
try:
# will throw if path doesn't exist
if repos.itemtype(include_path) == vclib.FILE:
if repos.itemtype(include_path, None) == vclib.FILE:
break
except vclib.ItemNotFound:
pass

View File

@ -34,17 +34,19 @@ SIDE_BY_SIDE = 3
class Repository:
"""Abstract class representing a repository."""
def itemtype(self, path_parts):
"""Return the type of the item (file or dir) at the given path.
def itemtype(self, path_parts, rev):
"""Return the type of the item (file or dir) at the given path and revision
The result will be vclib.DIR or vclib.FILE
The path is specified as a list of components, relative to the root
of the repository. e.g. ["subdir1", "subdir2", "filename"]
rev is the revision of the item to check
"""
pass
def openfile(self, path_parts, rev=None):
def openfile(self, path_parts, rev):
"""Open a file object to read file contents at a given path and revision.
The return value is a 2-tuple of containg the file object and revision
@ -53,10 +55,10 @@ class Repository:
The path is specified as a list of components, relative to the root
of the repository. e.g. ["subdir1", "subdir2", "filename"]
The revision number can be None to access a default revision.
rev is the revision of the file to check out
"""
def listdir(self, path_parts, options):
def listdir(self, path_parts, rev, options):
"""Return list of files in a directory
The result is a list of DirEntry objects
@ -64,10 +66,12 @@ class Repository:
The path is specified as a list of components, relative to the root
of the repository. e.g. ["subdir1", "subdir2", "filename"]
rev is the revision of the directory to list
options is a dictionary of implementation specific options
"""
def dirlogs(self, path_parts, entries, options):
def dirlogs(self, path_parts, rev, entries, options):
"""Augment directory entries with log information
New properties will be set on all of the DirEntry objects in the entries
@ -78,6 +82,9 @@ class Repository:
The path is specified as a list of components, relative to the root
of the repository. e.g. ["subdir1", "subdir2", "filename"]
rev is the revision of the directory listing and will effect which log
messages are returned
entries is a list of DirEntry objects returned from a previous call to
the listdir() method
@ -92,9 +99,7 @@ class Repository:
The path is specified as a list of components, relative to the root
of the repository. e.g. ["subdir1", "subdir2", "filename"]
The rev parameter can be set to only retrieve log information for a
specified revision, or it can be None to return information on all
file revisions.
rev is the revision of the item to return information about
options is a dictionary of implementation specific options
"""
@ -114,7 +119,7 @@ class Repository:
Return value is a python file object
"""
def annotate(self, path_parts, rev=None):
def annotate(self, path_parts, rev):
"""Return a list of annotate file content lines and a revision.
The annotated lines are an collection of objects with the

View File

@ -38,7 +38,7 @@ class CVSRepository(vclib.Repository):
self.name = name
self.rootpath = rootpath
def itemtype(self, path_parts):
def itemtype(self, path_parts, rev):
basepath = self._getpath(path_parts)
if os.path.isdir(basepath):
return vclib.DIR
@ -49,7 +49,7 @@ class CVSRepository(vclib.Repository):
return vclib.FILE
raise vclib.ItemNotFound(path_parts)
def listdir(self, path_parts, options):
def listdir(self, path_parts, rev, options):
# Only RCS files (*,v) and subdirs are returned.
data = [ ]
@ -124,7 +124,7 @@ class BinCVSRepository(CVSRepository):
return revs[-1]
return None
def openfile(self, path_parts, rev=None):
def openfile(self, path_parts, rev):
if not rev or rev == 'HEAD' or rev == 'MAIN':
rev_flag = '-p'
else:
@ -177,29 +177,27 @@ class BinCVSRepository(CVSRepository):
return fp, revision
def dirlogs(self, path_parts, entries, options):
def dirlogs(self, path_parts, rev, entries, options):
"""see vclib.Repository.dirlogs docstring
rev can be a tag name or None. if set only information from revisions
matching the tag will be retrieved
Option values recognized by this implementation:
cvs_subdirs
boolean. true to fetch logs of the most recently modified file in each
subdirectory
cvs_dir_tag
string set to a tag name. if set only logs from revisions matching the
tag will be retrieved
Option values returned by this implementation:
cvs_tags, cvs_branches
lists of tag and branch names encountered in the directory
"""
subdirs = options.get('cvs_subdirs', 0)
tag = options.get('cvs_dir_tag')
dirpath = self._getpath(path_parts)
alltags = _get_logs(self, dirpath, entries, tag, subdirs)
alltags = _get_logs(self, dirpath, entries, rev, subdirs)
branches = options['cvs_branches'] = []
tags = options['cvs_tags'] = []
@ -212,7 +210,9 @@ class BinCVSRepository(CVSRepository):
def itemlog(self, path_parts, rev, options):
"""see vclib.Repository.itemlog docstring
rev parameter can be a revision number, branch number or tag name
rev parameter can be a revision number, a branch number, a tag name,
or None. If None, will return information about all revisions, otherwise,
will only return information about the specified revision or branch.
Option values recognized by this implementation:

View File

@ -35,26 +35,24 @@ from vclib.bincvs import CVSRepository, Revision, Tag, \
_file_log, _log_path
class CCVSRepository(CVSRepository):
def dirlogs(self, path_parts, entries, options):
def dirlogs(self, path_parts, rev, entries, options):
"""see vclib.Repository.dirlogs docstring
rev can be a tag name or None. if set only information from revisions
matching the tag will be retrieved
Option values recognized by this implementation:
cvs_subdirs
boolean. true to fetch logs of the most recently modified file in each
subdirectory
cvs_dir_tag
string set to a tag name. if set only logs from revisions matching the
tag will be retrieved
Option values returned by this implementation:
cvs_tags, cvs_branches
lists of tag and branch names encountered in the directory
"""
subdirs = options.get('cvs_subdirs', 0)
tag = options.get('cvs_dir_tag')
dirpath = self._getpath(path_parts)
alltags = { # all the tags seen in the files of this dir
@ -67,7 +65,7 @@ class CCVSRepository(CVSRepository):
path = _log_path(entry, dirpath, subdirs)
if path:
try:
rcsparse.Parser().parse(open(path, 'rb'), InfoSink(entry, tag, alltags))
rcsparse.Parser().parse(open(path, 'rb'), InfoSink(entry, rev, alltags))
except IOError, e:
entry.errors.append("rcsparse error: %s" % e)
except RuntimeError, e:
@ -86,7 +84,9 @@ class CCVSRepository(CVSRepository):
def itemlog(self, path_parts, rev, options):
"""see vclib.Repository.itemlog docstring
rev parameter can be a revision number, branch number or tag name
rev parameter can be a revision number, a branch number, a tag name,
or None. If None, will return information about all revisions, otherwise,
will only return information about the specified revision or branch.
Option values returned by this implementation:

View File

@ -108,18 +108,27 @@ def date_from_rev(svnrepos, rev):
return _datestr_to_date(datestr, svnrepos.pool)
def get_location(svnrepos, path, rev):
def get_location(svnrepos, path, rev, old_rev):
try:
results = repos.svn_repos_trace_node_locations(svnrepos.fs_ptr, path,
svnrepos.rev, [int(rev)],
rev, [old_rev],
_allow_all, svnrepos.pool)
return results[int(rev)]
except:
raise vclib.ItemNotFound(filter(None, string.split(path, '/')))
except core.SubversionException, e:
if e.apr_err == core.SVN_ERR_FS_NOT_FOUND:
raise vclib.ItemNotFound(path)
raise
def created_rev(svnrepos, full_name):
return fs.node_created_rev(svnrepos.fsroot, full_name, svnrepos.pool)
try:
old_path = results[old_rev]
except KeyError:
raise vclib.ItemNotFound(path)
return _cleanup_path(old_path)
def created_rev(svnrepos, full_name, rev):
fsroot = svnrepos._getroot(rev)
return fs.node_created_rev(fsroot, full_name, svnrepos.pool)
class Revision(vclib.Revision):
@ -171,11 +180,12 @@ class NodeHistory:
self.histories[revision] = _cleanup_path(path)
def _get_history(svnrepos, full_name, options):
def _get_history(svnrepos, full_name, rev, options):
fsroot = svnrepos._getroot(rev)
show_all_logs = options.get('svn_show_all_dir_logs', 0)
if not show_all_logs:
# See if the path is a file or directory.
kind = fs.check_path(svnrepos.fsroot, full_name, svnrepos.pool)
kind = fs.check_path(fsroot, full_name, svnrepos.pool)
if kind is core.svn_node_file:
show_all_logs = 1
@ -187,7 +197,7 @@ def _get_history(svnrepos, full_name, options):
# Get the history items for PATH.
repos.svn_repos_history(svnrepos.fs_ptr, full_name, history.add_history,
1, svnrepos.rev, cross_copies, svnrepos.pool)
1, rev, cross_copies, svnrepos.pool)
return history.histories
@ -244,15 +254,15 @@ class ChangedPathSet:
return changes
def get_revision_info(svnrepos):
fsroot = fs.revision_root(svnrepos.fs_ptr, svnrepos.rev, svnrepos.pool)
def get_revision_info(svnrepos, rev):
fsroot = svnrepos._getroot(rev)
# Get the changes for the revision
cps = ChangedPathSet()
editor = repos.ChangeCollector(svnrepos.fs_ptr, fsroot,
svnrepos.pool, cps.add_change)
e_ptr, e_baton = delta.make_editor(editor, svnrepos.pool)
repos.svn_repos_replay(svnrepos.fsroot, e_ptr, e_baton, svnrepos.pool)
repos.svn_repos_replay(fsroot, e_ptr, e_baton, svnrepos.pool)
# Now get the revision property info
props = editor.get_root_props()
@ -286,14 +296,12 @@ def _log_helper(svnrepos, rev, path, pool):
def _fetch_log(svnrepos, full_name, which_rev, options, pool):
revs = []
if which_rev is not None:
if (which_rev < 0) or (which_rev > svnrepos.youngest):
raise vclib.InvalidRevision(which_rev)
if options.get('svn_latest_log', 0):
rev = _log_helper(svnrepos, which_rev, full_name, pool)
if rev:
revs.append(rev)
else:
history_set = _get_history(svnrepos, full_name, options)
history_set = _get_history(svnrepos, full_name, which_rev, options)
history_revs = history_set.keys()
history_revs.sort()
history_revs.reverse()
@ -308,19 +316,20 @@ def _fetch_log(svnrepos, full_name, which_rev, options, pool):
return revs
def _get_last_history_rev(svnrepos, path, pool):
history = fs.node_history(svnrepos.fsroot, path, pool)
def _get_last_history_rev(fsroot, path, pool):
history = fs.node_history(fsroot, path, pool)
history = fs.history_prev(history, 0, pool)
history_path, history_rev = fs.history_location(history, pool);
return history_rev
def get_logs(svnrepos, full_name, files):
def get_logs(svnrepos, full_name, rev, files):
fsroot = svnrepos._getroot(rev)
subpool = core.svn_pool_create(svnrepos.pool)
for file in files:
core.svn_pool_clear(subpool)
path = _fs_path_join(full_name, file.name)
rev = _get_last_history_rev(svnrepos, path, subpool)
rev = _get_last_history_rev(fsroot, path, subpool)
datestr, author, msg = _fs_rev_props(svnrepos.fs_ptr, rev, subpool)
date = _datestr_to_date(datestr, subpool)
file.rev = str(rev)
@ -328,7 +337,7 @@ def get_logs(svnrepos, full_name, files):
file.author = author
file.log = msg
if file.kind == vclib.FILE:
file.size = fs.file_length(svnrepos.fsroot, path, subpool)
file.size = fs.file_length(fsroot, path, subpool)
core.svn_pool_destroy(subpool)
@ -337,8 +346,8 @@ def get_youngest_revision(svnrepos):
def do_diff(svnrepos, path1, rev1, path2, rev2, diffoptions):
root1 = fs.revision_root(svnrepos.fs_ptr, rev1, svnrepos.pool)
root2 = fs.revision_root(svnrepos.fs_ptr, rev2, svnrepos.pool)
root1 = svnrepos._getroot(rev1)
root2 = svnrepos._getroot(rev2)
date1 = date_from_rev(svnrepos, rev1)
date2 = date_from_rev(svnrepos, rev2)
@ -475,7 +484,7 @@ class BlameSequencingError(Exception):
class SubversionRepository(vclib.Repository):
def __init__(self, name, rootpath, svn_path, rev=None):
def __init__(self, name, rootpath, svn_path):
if not os.path.isdir(rootpath):
raise vclib.ReposNotFound(name)
@ -484,7 +493,6 @@ class SubversionRepository(vclib.Repository):
self.apr_init = 0
self.rootpath = rootpath
self.name = name
self.rev = rev
self.svn_client_path = os.path.normpath(os.path.join(svn_path, 'svn'))
# Register a handler for SIGTERM so we can have a chance to
@ -514,11 +522,7 @@ class SubversionRepository(vclib.Repository):
self.repos = repos.svn_repos_open(rootpath, self.pool)
self.fs_ptr = repos.svn_repos_fs(self.repos)
self.youngest = fs.youngest_rev(self.fs_ptr, self.pool)
if self.rev is None:
self.rev = self.youngest
if (self.rev < 0) or (self.rev > self.youngest):
raise vclib.InvalidRevision(self.rev)
self.fsroot = fs.revision_root(self.fs_ptr, self.rev, self.pool)
self._fsroots = {}
def __del__(self):
self._close()
@ -533,10 +537,11 @@ class SubversionRepository(vclib.Repository):
def _scratch_clear(self):
core.svn_pool_clear(self.scratch_pool)
def itemtype(self, path_parts):
def itemtype(self, path_parts, rev):
rev = self._getrev(rev)
basepath = self._getpath(path_parts)
kind = fs.check_path(self.fsroot, basepath, self.scratch_pool)
kind = fs.check_path(self._getroot(rev), basepath, self.scratch_pool)
self._scratch_clear()
if kind == core.svn_node_dir:
return vclib.DIR
@ -544,20 +549,23 @@ class SubversionRepository(vclib.Repository):
return vclib.FILE
raise vclib.ItemNotFound(path_parts)
def openfile(self, path_parts, rev=None):
assert rev is None or int(rev) == self.rev
def openfile(self, path_parts, rev):
path = self._getpath(path_parts)
revision = str(_get_last_history_rev(self, path, self.scratch_pool))
rev = self._getrev(rev)
fsroot = self._getroot(rev)
revision = str(_get_last_history_rev(fsroot, path, self.scratch_pool))
self._scratch_clear()
fp = FileContentsPipe(self.fsroot, path, self.pool)
fp = FileContentsPipe(fsroot, path, self.pool)
return fp, revision
def listdir(self, path_parts, options):
def listdir(self, path_parts, rev, options):
basepath = self._getpath(path_parts)
if self.itemtype(path_parts) != vclib.DIR:
if self.itemtype(path_parts, rev) != vclib.DIR:
raise vclib.Error("Path '%s' is not a directory." % basepath)
dirents = fs.dir_entries(self.fsroot, basepath, self.scratch_pool)
rev = self._getrev(rev)
fsroot = self._getroot(rev)
dirents = fs.dir_entries(fsroot, basepath, self.scratch_pool)
entries = [ ]
for entry in dirents.values():
if entry.kind == core.svn_node_dir:
@ -568,19 +576,30 @@ class SubversionRepository(vclib.Repository):
self._scratch_clear()
return entries
def dirlogs(self, path_parts, entries, options):
get_logs(self, self._getpath(path_parts), entries)
def dirlogs(self, path_parts, rev, entries, options):
get_logs(self, self._getpath(path_parts), self._getrev(rev), entries)
def itemlog(self, path_parts, rev, options):
full_name = self._getpath(path_parts)
"""see vclib.Repository.itemlog docstring
if rev is not None:
try:
rev = int(rev)
except ValueError:
vclib.InvalidRevision(rev)
Option values recognized by this implementation
revs = _fetch_log(self, full_name, rev, options, self.scratch_pool)
svn_show_all_dir_logs
boolean, default false. if set for a directory path, will include
revisions where files underneath the directory have changed
svn_cross_copies
boolean, default false. if set for a path created by a copy, will
include revisions from before the copy
svn_latest_log
boolean, default false. if set will return only newest single log
entry
"""
path = self._getpath(path_parts)
rev = self._getrev(rev)
revs = _fetch_log(self, path, rev, options, self.scratch_pool)
self._scratch_clear()
revs.sort()
@ -591,11 +610,11 @@ class SubversionRepository(vclib.Repository):
return revs
def annotate(self, path_parts, rev=None):
if not rev:
rev = self.rev
def annotate(self, path_parts, rev):
path = self._getpath(path_parts)
revision = str(_get_last_history_rev(self, path, self.scratch_pool))
rev = self._getrev(rev)
fsroot = self._getroot(rev)
revision = str(_get_last_history_rev(fsroot, path, self.scratch_pool))
### Something's buggy in BlameSource, and the results are
### catastrophic for users of Mozilla and Firefox (it seems that
@ -615,13 +634,15 @@ class SubversionRepository(vclib.Repository):
"""
p1 = self._getpath(path_parts1)
p2 = self._getpath(path_parts2)
r1 = self._getrev(rev1)
r2 = self._getrev(rev2)
args = vclib._diff_args(type, options)
# Need to keep a reference to the FileDiff object around long
# enough to use. It destroys its underlying temporary files when
# the class is destroyed.
diffobj = options['diffobj'] = \
do_diff(self, p1, int(rev1), p2, int(rev2), args)
do_diff(self, p1, r1, p2, r2, args)
try:
return diffobj.get_pipe()
@ -633,6 +654,23 @@ class SubversionRepository(vclib.Repository):
def _getpath(self, path_parts):
return string.join(path_parts, '/')
def _getrev(self, rev):
if rev is None or rev == 'HEAD':
return self.youngest
try:
rev = int(rev)
except ValueError:
raise vclib.InvalidRevision(rev)
if (rev < 0) or (rev > self.youngest):
raise vclib.InvalidRevision(rev)
return rev
def _getroot(self, rev):
try:
return self._fsroots[rev]
except KeyError:
r = self._fsroots[rev] = fs.revision_root(self.fs_ptr, rev, self.pool)
return r
class _item:
def __init__(self, **kw):

View File

@ -25,7 +25,7 @@ import re
import tempfile
import popen2
import time
from vclib.svn import Revision, ChangedPath, _datestr_to_date, _compare_paths
from vclib.svn import Revision, ChangedPath, _datestr_to_date, _compare_paths, _cleanup_path
from svn import core, delta, client, wc, ra
@ -35,15 +35,10 @@ if (core.SVN_VER_MAJOR, core.SVN_VER_MINOR, core.SVN_VER_PATCH) < (1, 3, 0):
def _rev2optrev(rev):
assert type(rev) is int
rt = core.svn_opt_revision_t()
if rev is not None:
if str(rev) == 'HEAD':
rt.kind = core.svn_opt_revision_head
else:
rt.kind = core.svn_opt_revision_number
rt.value.number = rev
else:
rt.kind = core.svn_opt_revision_unspecified
rt.kind = core.svn_opt_revision_number
rt.value.number = rev
return rt
@ -53,21 +48,29 @@ def date_from_rev(svnrepos, rev):
return _datestr_to_date(datestr, svnrepos.pool)
def get_location(svnrepos, path, rev):
def get_location(svnrepos, path, rev, old_rev):
try:
results = ra.get_locations(svnrepos.ra_session, path, svnrepos.rev,
[int(rev)], svnrepos.pool)
return results[int(rev)]
except:
raise vclib.ItemNotFound(filter(None, string.split(path, '/')))
results = ra.get_locations(svnrepos.ra_session, path, rev,
[old_rev], svnrepos.pool)
except core.SubversionException, e:
if e.apr_err == core.SVN_ERR_FS_NOT_FOUND:
raise vclib.ItemNotFound(path)
raise
try:
old_path = results[old_rev]
except KeyError:
raise vclib.ItemNotFound(path)
return _cleanup_path(old_path)
def created_rev(svnrepos, full_name):
kind = ra.svn_ra_check_path(svnrepos.ra_session, full_name, svnrepos.rev,
def created_rev(svnrepos, full_name, rev):
kind = ra.svn_ra_check_path(svnrepos.ra_session, full_name, rev,
svnrepos.pool)
if kind == core.svn_node_dir:
props = ra.svn_ra_get_dir(svnrepos.ra_session, full_name,
svnrepos.rev, svnrepos.pool)
rev, svnrepos.pool)
return int(props[core.SVN_PROP_ENTRY_COMMITTED_REV])
return core.SVN_INVALID_REVNUM
@ -122,9 +125,9 @@ def _get_rev_details(svnrepos, rev, pool):
return lhc.get_history()
def get_revision_info(svnrepos):
def get_revision_info(svnrepos, rev):
rev, author, date, log, changes = \
_get_rev_details(svnrepos, svnrepos.rev, svnrepos.pool)
_get_rev_details(svnrepos, rev, svnrepos.pool)
return _datestr_to_date(date, svnrepos.pool), author, log, changes
@ -166,9 +169,8 @@ class LogCollector:
self.path = this_path
def get_logs(svnrepos, full_name, files):
parts = filter(None, string.split(full_name, '/'))
dirents = svnrepos.get_dirents(parts, svnrepos.rev)
def get_logs(svnrepos, full_name, rev, files):
dirents = svnrepos._get_dirents(full_name, rev)
subpool = core.svn_pool_create(svnrepos.pool)
rev_info_cache = { }
for file in files:
@ -342,7 +344,7 @@ class SelfCleanFP:
class SubversionRepository(vclib.Repository):
def __init__(self, name, rootpath, rev=None):
def __init__(self, name, rootpath):
# Init the client app
core.apr_initialize()
pool = core.svn_pool_create(None)
@ -370,22 +372,18 @@ class SubversionRepository(vclib.Repository):
self.ra_session = ra.svn_ra_open(self.rootpath, ra_callbacks, None,
ctx.config, pool)
self.youngest = ra.svn_ra_get_latest_revnum(self.ra_session, pool)
if rev is not None:
self.rev = rev
if self.rev > self.youngest:
raise vclib.InvalidRevision(self.rev)
else:
self.rev = self.youngest
self._dirent_cache = { }
def __del__(self):
core.svn_pool_destroy(self.pool)
core.apr_terminate()
def itemtype(self, path_parts):
def itemtype(self, path_parts, rev):
path = self._getpath(path_parts[:-1])
rev = self._getrev(rev)
if not len(path_parts):
return vclib.DIR
dirents = self.get_dirents(path_parts[:-1], self.rev)
dirents = self._get_dirents(path, rev)
try:
entry = dirents[path_parts[-1]]
if entry.kind == core.svn_node_dir:
@ -395,11 +393,8 @@ class SubversionRepository(vclib.Repository):
except KeyError:
raise vclib.ItemNotFound(path_parts)
def openfile(self, path_parts, rev=None):
if rev is None:
rev = self.rev
else:
rev = int(rev)
def openfile(self, path_parts, rev):
rev = self._getrev(rev)
url = self.rootpath
if len(path_parts):
url = self.rootpath + '/' + self._getpath(path_parts)
@ -411,9 +406,11 @@ class SubversionRepository(vclib.Repository):
core.svn_stream_close(stream)
return SelfCleanFP(tmp_file), rev
def listdir(self, path_parts, options):
def listdir(self, path_parts, rev, options):
path = self._getpath(path_parts)
rev = self._getrev(rev)
entries = [ ]
dirents = self.get_dirents(path_parts, self.rev)
dirents = self._get_dirents(path, rev)
for name in dirents.keys():
entry = dirents[name]
if entry.kind == core.svn_node_dir:
@ -423,17 +420,12 @@ class SubversionRepository(vclib.Repository):
entries.append(vclib.DirEntry(name, kind))
return entries
def dirlogs(self, path_parts, entries, options):
get_logs(self, self._getpath(path_parts), entries)
def dirlogs(self, path_parts, rev, entries, options):
get_logs(self, self._getpath(path_parts), self._getrev(rev), entries)
def itemlog(self, path_parts, rev, options):
full_name = self._getpath(path_parts)
if rev is not None:
try:
rev = int(rev)
except ValueError:
vclib.InvalidRevision(rev)
rev = self._getrev(rev)
# It's okay if we're told to not show all logs on a file -- all
# the revisions should match correctly anyway.
@ -443,7 +435,7 @@ class SubversionRepository(vclib.Repository):
dir_url = dir_url + '/' + full_name
cross_copies = options.get('svn_cross_copies', 0)
client.svn_client_log([dir_url], _rev2optrev(self.rev), _rev2optrev(1),
client.svn_client_log([dir_url], _rev2optrev(rev), _rev2optrev(1),
1, not cross_copies, lc.add_log,
self.ctx, self.pool)
revs = lc.logs
@ -455,13 +447,10 @@ class SubversionRepository(vclib.Repository):
return revs
def annotate(self, path_parts, rev=None):
def annotate(self, path_parts, rev):
path = self._getpath(path_parts)
rev = self._getrev(rev)
url = self.rootpath + (path and '/' + path)
if rev is None:
rev = self.rev
else:
rev = int(rev)
blame_data = []
@ -487,13 +476,15 @@ class SubversionRepository(vclib.Repository):
"""
p1 = self._getpath(path_parts1)
p2 = self._getpath(path_parts2)
r1 = self._getrev(rev1)
r2 = self._getrev(rev2)
args = vclib._diff_args(type, options)
# Need to keep a reference to the FileDiff object around long
# enough to use. It destroys its underlying temporary files when
# the class is destroyed.
diffobj = options['diffobj'] = \
do_diff(self, p1, int(rev1), p2, int(rev2), args)
do_diff(self, p1, r1, p2, r2, args)
try:
return diffobj.get_pipe()
@ -505,13 +496,22 @@ class SubversionRepository(vclib.Repository):
def _getpath(self, path_parts):
return string.join(path_parts, '/')
def get_dirents(self, path_parts, rev):
if len(path_parts):
path = self._getpath(path_parts)
def _getrev(self, rev):
if rev is None or rev == 'HEAD':
return self.youngest
try:
rev = int(rev)
except ValueError:
raise vclib.InvalidRevision(rev)
if (rev < 0) or (rev > self.youngest):
raise vclib.InvalidRevision(rev)
return rev
def _get_dirents(self, path, rev):
if path:
key = str(rev) + '/' + path
dir_url = self.rootpath + '/' + path
else:
path = None
key = str(rev)
dir_url = self.rootpath
dirents = self._dirent_cache.get(key)

View File

@ -248,25 +248,19 @@ class Request:
elif cfg.general.svn_roots.has_key(self.rootname):
self.rootpath = cfg.general.svn_roots[self.rootname]
try:
rev = None
if self.query_dict.has_key('rev') \
and self.query_dict['rev'] != 'HEAD':
rev = int(self.query_dict['rev'])
if re.match(_re_rewrite_url, self.rootpath):
# If the rootpath is a URL, we'll use the svn_ra module, but
# lie about its name.
import vclib.svn_ra
vclib.svn = vclib.svn_ra
self.repos = vclib.svn.SubversionRepository(self.rootname,
self.rootpath,
rev)
self.rootpath)
else:
self.rootpath = os.path.normpath(self.rootpath)
import vclib.svn
self.repos = vclib.svn.SubversionRepository(self.rootname,
self.rootpath,
cfg.general.svn_path,
rev)
cfg.general.svn_path)
self.roottype = 'svn'
except vclib.ReposNotFound:
raise debug.ViewCVSException(
@ -283,18 +277,20 @@ class Request:
% self.rootname, "404 Repository not found")
# Make sure path exists
self.pathtype = _repos_pathtype(self.repos, self.path_parts)
rev = self.query_dict.get('rev')
self.pathtype = _repos_pathtype(self.repos, self.path_parts, rev)
if self.pathtype is None:
# path doesn't exist, try stripping known fake suffixes
result = _strip_suffix('.diff', self.where, self.path_parts, \
vclib.FILE, self.repos, view_diff) or \
_strip_suffix('.tar.gz', self.where, self.path_parts, \
vclib.DIR, self.repos, download_tarball) or \
_strip_suffix('root.tar.gz', self.where, self.path_parts, \
vclib.DIR, self.repos, download_tarball)
result = _strip_suffix('.diff', path_parts, rev, vclib.FILE, \
self.repos, view_diff) or \
_strip_suffix('.tar.gz', path_parts, rev, vclib.DIR, \
self.repos, download_tarball) or \
_strip_suffix('root.tar.gz', path_parts, rev, vclib.DIR, \
self.repos, download_tarball)
if result:
self.where, self.path_parts, self.pathtype, self.view_func = result
self.path_parts, self.pathtype, self.view_func = result
self.where = _path_join(path_parts)
else:
raise debug.ViewCVSException('%s: unknown location'
% self.where, '404 Not Found')
@ -647,30 +643,27 @@ _legal_params = {
def _path_join(path_parts):
return string.join(path_parts, '/')
def _strip_suffix(suffix, where, path_parts, pathtype, repos, view_func):
def _strip_suffix(suffix, path_parts, rev, pathtype, repos, view_func):
"""strip the suffix from a repository path if the resulting path
is of the specified type, otherwise return None"""
l = len(suffix)
if where[-l:] == suffix:
if path_parts[-1][-l:] == suffix:
path_parts = path_parts[:]
if len(path_parts[-1]) == l:
del path_parts[-1]
else:
path_parts[-1] = path_parts[-1][:-l]
t = _repos_pathtype(repos, path_parts)
t = _repos_pathtype(repos, path_parts, rev)
if pathtype == t:
return where[:-l], path_parts, t, view_func
return path_parts, t, view_func
return None
def _repos_pathtype(repos, path_parts):
"""return the type of a repository path, or None if the path
does not exist"""
type = None
def _repos_pathtype(repos, path_parts, rev):
"""return the type of a repository path, or None if the path doesn't exist"""
try:
type = repos.itemtype(path_parts)
return repos.itemtype(path_parts, rev)
except vclib.ItemNotFound:
pass
return type
return None
def check_freshness(request, mtime=None, etag=None, weak=0):
# See if we are supposed to disable etags (for debugging, usually)
@ -757,8 +750,8 @@ def nav_path(request):
# set convenient "rev" and "is_dir" values
rev = None
if request.roottype == "svn" and request.query_dict.get('rev'):
rev = request.repos.rev
if request.roottype == 'svn':
rev = request.query_dict.get('rev')
is_dir = request.pathtype == vclib.DIR
# add root item
@ -1301,7 +1294,7 @@ def view_markup(request):
})
if cfg.options.show_log_in_markup:
options = {}
options = {'svn_latest_log': 1}
revs = request.repos.itemlog(request.path_parts, revision, options)
entry = revs[-1]
@ -1429,34 +1422,38 @@ def view_roots(request):
generate_page(request, "roots", data)
def view_directory(request):
if request.roottype == 'svn':
rev = request.query_dict.get('rev')
elif request.roottype == 'cvs':
rev = view_tag = request.query_dict.get('only_with_tag')
# For Subversion repositories, the revision acts as a weak validator for
# the directory listing (to take into account template changes or
# revision property changes).
if request.roottype == 'svn':
revision = str(vclib.svn.created_rev(request.repos, request.where))
revision = str(vclib.svn.created_rev(request.repos, request.where,
request.repos._getrev(rev)))
if check_freshness(request, None, revision, weak=1):
return
# List current directory
options = {}
if request.roottype == 'cvs':
view_tag = request.query_dict.get('only_with_tag')
hideattic = int(request.query_dict.get('hideattic',
cfg.options.hide_attic))
options["cvs_subdirs"] = (cfg.options.show_subdir_lastmod and
cfg.options.show_logs)
options["cvs_dir_tag"] = view_tag
file_data = request.repos.listdir(request.path_parts, options)
file_data = request.repos.listdir(request.path_parts, rev, options)
# Filter file list if a regex is specified
search_re = request.query_dict.get('search', '')
if cfg.options.use_re_search and search_re:
file_data = search_files(request.repos, request.path_parts,
file_data = search_files(request.repos, request.path_parts, rev,
file_data, search_re)
# Retrieve log messages, authors, revision numbers, timestamps
request.repos.dirlogs(request.path_parts, file_data, options)
request.repos.dirlogs(request.path_parts, rev, file_data, options)
# sort with directories first, and using the "sortby" criteria
sortby = request.query_dict.get('sortby', cfg.options.sort_by) or 'file'
@ -1473,7 +1470,7 @@ def view_directory(request):
where = request.where
where_prefix = where and where + '/'
if request.roottype == 'svn':
dir_params = {'rev': request.query_dict.get('rev')}
dir_params = {'rev': rev}
else:
dir_params = {}
@ -1653,10 +1650,7 @@ def view_directory(request):
data['youngest_rev_href'] = request.get_url(view_func=view_revision,
params={},
escape=1)
if request.query_dict.has_key('rev'):
data['rev'] = request.query_dict['rev']
else:
data['rev'] = str(request.repos.rev)
data['rev'] = str(request.repos._getrev(rev))
url, params = request.get_link(params={'rev': None})
data['jump_rev_action'] = urllib.quote(url, _URL_SAFE_CHARS)
data['jump_rev_hidden_values'] = prepare_hidden_values(params)
@ -1664,8 +1658,8 @@ def view_directory(request):
if is_query_supported(request):
params = {}
if options.has_key('cvs_dir_tag'):
params['branch'] = options['cvs_dir_tag']
if request.roottype == 'cvs' and view_tag:
params['branch'] = view_tag
data['queryform_href'] = request.get_url(view_func=view_queryform,
params=params,
escape=1)
@ -1749,7 +1743,7 @@ def view_log(request):
if request.roottype == 'cvs':
rev = view_tag
else:
rev = None
rev = request.query_dict.get('rev')
show_revs = request.repos.itemlog(request.path_parts, rev, options)
if logsort == 'date':
@ -1906,7 +1900,7 @@ def view_log(request):
'r2': entry.rev,
'diff_format': None},
escape=1)
# moves aren't handled here but they are only supported by CVS right now.
if entry.next_main:
entry.diff_to_main_href = \
request.get_url(view_func=view_diff,
@ -2144,7 +2138,7 @@ def view_cvsgraph(request):
request.server.header()
generate_page(request, "graph", data)
def search_files(repos, path_parts, files, search_re):
def search_files(repos, path_parts, rev, files, search_re):
""" Search files in a directory for a regular expression.
Does a check-out of each file in the directory. Only checks for
@ -2179,9 +2173,8 @@ def search_files(repos, path_parts, files, search_re):
# Only text files at this point
# process_checkout will checkout the head version out of the repository
# Assign contents of checked out file to fp.
fp = repos.openfile(path_parts + [file.name])[0]
fp = repos.openfile(path_parts + [file.name], rev)[0]
# Read in each line, use re.search to search line.
# If successful, add file to new_file_list and break.
@ -2459,13 +2452,15 @@ def diff_parse_headers(fp, diff_type, rev1, rev2, sym1=None, sym2=None):
return date1, date2, flag, string.join(header_lines, '')
def _get_diff_path_parts(request, query_key, rev):
def _get_diff_path_parts(request, query_key, rev, base_rev):
if request.query_dict.has_key(query_key):
parts = _path_parts(request.query_dict[query_key])
elif request.roottype == 'svn':
try:
parts = _path_parts(vclib.svn.get_location(request.repos,
request.where, rev))
repos = request.repos
parts = _path_parts(vclib.svn.get_location(repos, request.where,
repos._getrev(base_rev),
repos._getrev(rev)))
except vclib.InvalidRevision:
raise debug.ViewCVSException('Invalid path(s) or revision(s) passed '
'to diff', '400 Bad Request')
@ -2482,6 +2477,7 @@ def setup_diff(request):
rev1 = r1 = query_dict['r1']
rev2 = r2 = query_dict['r2']
base_rev = query_dict.get('rev')
sym1 = sym2 = None
# hack on the diff revisions
@ -2512,8 +2508,8 @@ def setup_diff(request):
rev2 = r2[:idx]
sym2 = r2[idx+1:]
p1 = _get_diff_path_parts(request, 'p1', rev1)
p2 = _get_diff_path_parts(request, 'p2', rev2)
p1 = _get_diff_path_parts(request, 'p1', rev1, base_rev)
p2 = _get_diff_path_parts(request, 'p2', rev2, base_rev)
try:
if revcmp(rev1, rev2) > 0:
@ -2523,7 +2519,6 @@ def setup_diff(request):
except ValueError:
raise debug.ViewCVSException('Invalid revision(s) passed to diff',
'400 Bad Request')
return p1, p2, rev1, rev2, sym1, sym2
@ -2693,11 +2688,11 @@ def generate_tarball_header(out, name, size=0, mode=None, mtime=0,
out.write(block)
def generate_tarball(out, request, options, reldir, stack):
def generate_tarball(out, request, rev, reldir, stack):
# get directory info from repository
rep_path = request.path_parts + reldir
entries = request.repos.listdir(rep_path, options)
request.repos.dirlogs(rep_path, entries, options)
entries = request.repos.listdir(rep_path, rev, {})
request.repos.dirlogs(rep_path, rev, entries, {})
entries.sort(lambda a, b: cmp(a.name, b.name))
# figure out corresponding path in tar file. everything gets put underneath
@ -2764,7 +2759,7 @@ def generate_tarball(out, request, options, reldir, stack):
or (cvs and cfg.options.hide_cvsroot and file.name == 'CVSROOT')):
continue
generate_tarball(out, request, options, reldir + [file.name], stack)
generate_tarball(out, request, rev, reldir + [file.name], stack)
# pop directory (if it's being pruned. otherwise stack is already empty)
del stack[-1:]
@ -2774,10 +2769,10 @@ def download_tarball(request):
raise debug.ViewCVSException('Tarball generation is disabled',
'403 Forbidden')
options = {}
if request.roottype == 'cvs':
tag = request.query_dict.get('only_with_tag')
options['cvs_dir_tag'] = tag
if request.roottype == 'svn':
rev = request.query_dict.get('rev')
elif request.roottype == 'cvs':
rev = request.query_dict.get('only_with_tag')
### look for GZIP binary
@ -2785,7 +2780,7 @@ def download_tarball(request):
sys.stdout.flush()
fp = popen.pipe_cmds([('gzip', '-c', '-n')])
generate_tarball(fp, request, options, [], [])
generate_tarball(fp, request, rev, [], [])
fp.write('\0' * 1024)
fp.close()
@ -2797,9 +2792,9 @@ def view_revision(request):
data = common_template_data(request)
query_dict = request.query_dict
date, author, msg, changes = vclib.svn.get_revision_info(request.repos)
rev = request.repos._getrev(query_dict.get('rev'))
date, author, msg, changes = vclib.svn.get_revision_info(request.repos, rev)
date_str = make_time_string(date)
rev = request.repos.rev
# The revision number acts as a weak validator.
if check_freshness(request, None, str(rev), weak=1):

View File

@ -71,7 +71,7 @@ def UpdateFile(db, repository, path, update):
def RecurseUpdate(db, repository, directory, update):
for entry in repository.listdir(directory, {}):
for entry in repository.listdir(directory, None, {}):
path = directory + [entry.name]
if entry.errors: