You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@bloodhound.apache.org by gj...@apache.org on 2012/10/16 17:55:11 UTC
svn commit: r1398858 [28/33] - in /incubator/bloodhound/vendor/trac/current:
./ contrib/ doc/ doc/api/ doc/utils/ sample-plugins/
sample-plugins/permissions/ sample-plugins/workflow/ trac/ trac/admin/
trac/admin/templates/ trac/admin/tests/ trac/db/ tr...
Modified: incubator/bloodhound/vendor/trac/current/trac/util/tests/text.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/vendor/trac/current/trac/util/tests/text.py?rev=1398858&r1=1398857&r2=1398858&view=diff
==============================================================================
--- incubator/bloodhound/vendor/trac/current/trac/util/tests/text.py (original)
+++ incubator/bloodhound/vendor/trac/current/trac/util/tests/text.py Tue Oct 16 15:55:00 2012
@@ -4,11 +4,12 @@ import unittest
from StringIO import StringIO
from trac.util.text import empty, expandtabs, fix_eol, javascript_quote, \
- normalize_whitespace, to_unicode, \
+ to_js_string, normalize_whitespace, to_unicode, \
text_width, print_table, unicode_quote, \
unicode_quote_plus, unicode_unquote, \
unicode_urlencode, wrap, quote_query_string, \
- unicode_to_base64, unicode_from_base64
+ unicode_to_base64, unicode_from_base64, stripws, \
+ levenshtein_distance
class ToUnicodeTestCase(unittest.TestCase):
@@ -65,6 +66,19 @@ class JavascriptQuoteTestCase(unittest.T
self.assertEqual(r'\u0026\u003c\u003e',
javascript_quote('&<>'))
+
+class ToJsStringTestCase(unittest.TestCase):
+ def test_(self):
+ self.assertEqual(r'"Quote \" in text"',
+ to_js_string('Quote " in text'))
+ self.assertEqual(r'''"\\\"\b\f\n\r\t'"''',
+ to_js_string('\\"\b\f\n\r\t\''))
+ self.assertEqual(r'"\u0002\u001e"',
+ to_js_string('\x02\x1e'))
+ self.assertEqual(r'"\u0026\u003c\u003e"',
+ to_js_string('&<>'))
+
+
class UnicodeQuoteTestCase(unittest.TestCase):
def test_unicode_quote(self):
self.assertEqual(u'the%20%C3%9C%20thing',
@@ -289,12 +303,38 @@ class UnicodeBase64TestCase(unittest.Tes
self.assertEqual(text, unicode_from_base64(text_base64_no_strip))
+class StripwsTestCase(unittest.TestCase):
+ def test_stripws(self):
+ self.assertEquals(u'stripws',
+ stripws(u' \u200b\t\u3000stripws \u200b\t\u2008'))
+ self.assertEquals(u'stripws \u3000\t',
+ stripws(u'\u200b\t\u2008 stripws \u3000\t',
+ trailing=False))
+ self.assertEquals(u' \t\u3000stripws',
+ stripws(u' \t\u3000stripws \u200b\t\u2008',
+ leading=False))
+ self.assertEquals(u' \t\u3000stripws \u200b\t\u2008',
+ stripws(u' \t\u3000stripws \u200b\t\u2008',
+ leading=False, trailing=False))
+
+
+
+class LevenshteinDistanceTestCase(unittest.TestCase):
+ def test_distance(self):
+ self.assertEqual(5, levenshtein_distance('kitten', 'sitting'))
+ self.assertEqual(1, levenshtein_distance('wii', 'wiki'))
+ self.assertEqual(2, levenshtein_distance('comfig', 'config'))
+ self.assertEqual(5, levenshtein_distance('update', 'upgrade'))
+ self.assertEqual(0, levenshtein_distance('milestone', 'milestone'))
+
+
def suite():
suite = unittest.TestSuite()
suite.addTest(unittest.makeSuite(ToUnicodeTestCase, 'test'))
suite.addTest(unittest.makeSuite(ExpandtabsTestCase, 'test'))
suite.addTest(unittest.makeSuite(UnicodeQuoteTestCase, 'test'))
suite.addTest(unittest.makeSuite(JavascriptQuoteTestCase, 'test'))
+ suite.addTest(unittest.makeSuite(ToJsStringTestCase, 'test'))
suite.addTest(unittest.makeSuite(QuoteQueryStringTestCase, 'test'))
suite.addTest(unittest.makeSuite(WhitespaceTestCase, 'test'))
suite.addTest(unittest.makeSuite(TextWidthTestCase, 'test'))
@@ -302,6 +342,8 @@ def suite():
suite.addTest(unittest.makeSuite(WrapTestCase, 'test'))
suite.addTest(unittest.makeSuite(FixEolTestCase, 'test'))
suite.addTest(unittest.makeSuite(UnicodeBase64TestCase, 'test'))
+ suite.addTest(unittest.makeSuite(StripwsTestCase, 'test'))
+ suite.addTest(unittest.makeSuite(LevenshteinDistanceTestCase, 'test'))
return suite
if __name__ == '__main__':
Modified: incubator/bloodhound/vendor/trac/current/trac/util/text.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/vendor/trac/current/trac/util/text.py?rev=1398858&r1=1398857&r2=1398858&view=diff
==============================================================================
--- incubator/bloodhound/vendor/trac/current/trac/util/text.py (original)
+++ incubator/bloodhound/vendor/trac/current/trac/util/text.py Tue Oct 16 15:55:00 2012
@@ -3,7 +3,7 @@
# Copyright (C) 2003-2009 Edgewall Software
# Copyright (C) 2003-2004 Jonas Borgström <jo...@edgewall.com>
# Copyright (C) 2006 Matthew Good <tr...@matt-good.net>
-# Copyright (C) 2005-2006 Christian Boos <cb...@neuf.fr>
+# Copyright (C) 2005-2006 Christian Boos <cb...@edgewall.org>
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
@@ -16,7 +16,7 @@
#
# Author: Jonas Borgström <jo...@edgewall.com>
# Matthew Good <tr...@matt-good.net>
-# Christian Boos <cb...@neuf.fr>
+# Christian Boos <cb...@edgewall.org>
import __builtin__
import locale
@@ -95,15 +95,36 @@ def path_to_unicode(path):
return unicode(path)
+_ws_leading_re = re.compile(ur'\A[\s\u200b]+', re.UNICODE)
+_ws_trailing_re = re.compile(ur'[\s\u200b]+\Z', re.UNICODE)
+
+def stripws(text, leading=True, trailing=True):
+ """Strips unicode white-spaces and ZWSPs from ``text``.
+
+ :param leading: strips leading spaces from ``text`` unless ``leading`` is
+ `False`.
+ :param trailing: strips trailing spaces from ``text`` unless ``trailing``
+ is `False`.
+ """
+ if leading:
+ text = _ws_leading_re.sub('', text)
+ if trailing:
+ text = _ws_trailing_re.sub('', text)
+ return text
+
+
_js_quote = {'\\': '\\\\', '"': '\\"', '\b': '\\b', '\f': '\\f',
'\n': '\\n', '\r': '\\r', '\t': '\\t', "'": "\\'"}
for i in range(0x20) + [ord(c) for c in '&<>']:
_js_quote.setdefault(chr(i), '\\u%04x' % i)
_js_quote_re = re.compile(r'[\x00-\x1f\\"\b\f\n\r\t\'&<>]')
+_js_string_re = re.compile(r'[\x00-\x1f\\"\b\f\n\r\t&<>]')
def javascript_quote(text):
- """Quote strings for inclusion in javascript"""
+ """Quote strings for inclusion in single or double quote delimited
+ Javascript strings
+ """
if not text:
return ''
def replace(match):
@@ -111,6 +132,17 @@ def javascript_quote(text):
return _js_quote_re.sub(replace, text)
+def to_js_string(text):
+ """Embed the given string in a double quote delimited Javascript string
+ (conform to the JSON spec)
+ """
+ if not text:
+ return ''
+ def replace(match):
+ return _js_quote[match.group(0)]
+ return '"%s"' % _js_string_re.sub(replace, text)
+
+
def unicode_quote(value, safe='/'):
"""A unicode aware version of `urllib.quote`
@@ -541,7 +573,7 @@ def normalize_whitespace(text, to_space=
def unquote_label(txt):
"""Remove (one level of) enclosing single or double quotes.
- .. versionadded :: 0.13
+ .. versionadded :: 1.0
"""
return txt[1:-1] if txt and txt[0] in "'\"" and txt[0] == txt[-1] else txt
@@ -626,3 +658,22 @@ def unicode_to_base64(text, strip_newlin
def unicode_from_base64(text):
"""Safe conversion of ``text`` to unicode based on utf-8 bytes."""
return text.decode('base64').decode('utf-8')
+
+
+def levenshtein_distance(lhs, rhs):
+ """Return the Levenshtein distance between two strings."""
+ if len(lhs) > len(rhs):
+ rhs, lhs = lhs, rhs
+ if not lhs:
+ return len(rhs)
+
+ prev = range(len(rhs) + 1)
+ for lidx, lch in enumerate(lhs):
+ curr = [lidx + 1]
+ for ridx, rch in enumerate(rhs):
+ cost = (lch != rch) * 2
+ curr.append(min(prev[ridx + 1] + 1, # deletion
+ curr[ridx] + 1, # insertion
+ prev[ridx] + cost)) # substitution
+ prev = curr
+ return prev[-1]
Modified: incubator/bloodhound/vendor/trac/current/trac/versioncontrol/api.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/vendor/trac/current/trac/versioncontrol/api.py?rev=1398858&r1=1398857&r2=1398858&view=diff
==============================================================================
--- incubator/bloodhound/vendor/trac/current/trac/versioncontrol/api.py (original)
+++ incubator/bloodhound/vendor/trac/current/trac/versioncontrol/api.py Tue Oct 16 15:55:00 2012
@@ -663,7 +663,11 @@ class RepositoryManager(Component):
try:
changeset = repos.get_changeset(rev)
except NoSuchChangeset:
- continue
+ try:
+ repos.sync_changeset(rev)
+ changeset = repos.get_changeset(rev)
+ except NoSuchChangeset:
+ continue
self.log.debug("Event %s on %s for revision %s",
event, repos.reponame or '(default)', rev)
for listener in self.change_listeners:
@@ -1139,7 +1143,7 @@ class Changeset(object):
def get_tags(self):
"""Yield tags associated with this changeset.
- .. versionadded :: 0.13
+ .. versionadded :: 1.0
"""
return []
Modified: incubator/bloodhound/vendor/trac/current/trac/versioncontrol/cache.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/vendor/trac/current/trac/versioncontrol/cache.py?rev=1398858&r1=1398857&r2=1398858&view=diff
==============================================================================
--- incubator/bloodhound/vendor/trac/current/trac/versioncontrol/cache.py (original)
+++ incubator/bloodhound/vendor/trac/current/trac/versioncontrol/cache.py Tue Oct 16 15:55:00 2012
@@ -30,6 +30,12 @@ _actionmap = {'A': Changeset.ADD, 'C': C
'D': Changeset.DELETE, 'E': Changeset.EDIT,
'M': Changeset.MOVE}
+def _invert_dict(d):
+ return dict(zip(d.values(), d.keys()))
+
+_inverted_kindmap = _invert_dict(_kindmap)
+_inverted_actionmap = _invert_dict(_actionmap)
+
CACHE_REPOSITORY_DIR = 'repository_dir'
CACHE_YOUNGEST_REV = 'youngest_rev'
@@ -89,10 +95,13 @@ class CachedRepository(Repository):
""", (self.id, srev)):
old_cset = Changeset(self.repos, cset.rev, message, author,
from_utimestamp(time))
- db("""UPDATE revision SET time=%s, author=%s, message=%s
- WHERE repos=%s AND rev=%s
- """, (to_utimestamp(cset.date), cset.author, cset.message,
- self.id, srev))
+ if old_cset:
+ db("""UPDATE revision SET time=%s, author=%s, message=%s
+ WHERE repos=%s AND rev=%s
+ """, (to_utimestamp(cset.date), cset.author,
+ cset.message, self.id, srev))
+ else:
+ self._insert_changeset(db, rev, cset)
return old_cset
@cached('_metadata_id')
@@ -215,69 +224,69 @@ class CachedRepository(Repository):
self.repos.clear(youngest_rev=youngest)
return
- # 1. prepare for resyncing
- # (there still might be a race condition at this point)
-
- kindmap = dict(zip(_kindmap.values(), _kindmap.keys()))
- actionmap = dict(zip(_actionmap.values(), _actionmap.keys()))
-
+ # prepare for resyncing (there might still be a race
+ # condition at this point)
while next_youngest is not None:
srev = self.db_rev(next_youngest)
-
+
with self.env.db_transaction as db:
-
- # 1.1 Attempt to resync the 'revision' table
self.log.info("Trying to sync revision [%s]",
next_youngest)
cset = self.repos.get_changeset(next_youngest)
try:
- db("""INSERT INTO revision
- (repos, rev, time, author, message)
- VALUES (%s, %s, %s, %s, %s)
- """, (self.id, srev, to_utimestamp(cset.date),
- cset.author, cset.message))
- except Exception, e: # *another* 1.1. resync attempt won
+ # steps 1. and 2.
+ self._insert_changeset(db, next_youngest, cset)
+ except Exception, e: # *another* 1.1. resync attempt won
self.log.warning('Revision %s already cached: %r',
next_youngest, e)
- # also potentially in progress, so keep ''previous''
- # notion of 'youngest'
+ # the other resync attempts is also
+ # potentially still in progress, so for our
+ # process/thread, keep ''previous'' notion of
+ # 'youngest'
self.repos.clear(youngest_rev=youngest)
# FIXME: This aborts a containing transaction
db.rollback()
return
-
- # 1.2. now *only* one process was able to get there
- # (i.e. there *shouldn't* be any race condition here)
-
- for path, kind, action, bpath, brev in cset.get_changes():
- self.log.debug("Caching node change in [%s]: %r",
- next_youngest,
- (path, kind, action, bpath, brev))
- kind = kindmap[kind]
- action = actionmap[action]
- db("""INSERT INTO node_change
- (repos, rev, path, node_type, change_type,
- base_path, base_rev)
- VALUES (%s, %s, %s, %s, %s, %s, %s)
- """, (self.id, srev, path, kind, action, bpath,
- brev))
-
- # 1.3. update 'youngest_rev' metadata
- # (minimize possibility of failures at point 0.)
- db("""UPDATE repository SET value=%s
- WHERE id=%s AND name=%s
- """, (str(next_youngest),
- self.id, CACHE_YOUNGEST_REV))
+
+ # 3. update 'youngest_rev' metadata (minimize
+ # possibility of failures at point 0.)
+ db("""
+ UPDATE repository SET value=%s WHERE id=%s AND name=%s
+ """, (str(next_youngest), self.id, CACHE_YOUNGEST_REV))
del self.metadata
- # 1.4. iterate (1.1 should always succeed now)
+ # 4. iterate (1. should always succeed now)
youngest = next_youngest
next_youngest = self.repos.next_rev(next_youngest)
- # 1.5. provide some feedback
+ # 5. provide some feedback
if feedback:
feedback(youngest)
+ def _insert_changeset(self, db, rev, cset):
+ srev = self.db_rev(rev)
+ # 1. Attempt to resync the 'revision' table. In case of
+ # concurrent syncs, only such insert into the `revision` table
+ # will succeed, the others will fail and raise an exception.
+ db("""
+ INSERT INTO revision (repos,rev,time,author,message)
+ VALUES (%s,%s,%s,%s,%s)
+ """, (self.id, srev, to_utimestamp(cset.date),
+ cset.author, cset.message))
+ # 2. now *only* one process was able to get there (i.e. there
+ # *shouldn't* be any race condition here)
+ for path, kind, action, bpath, brev in cset.get_changes():
+ self.log.debug("Caching node change in [%s]: %r", rev,
+ (path, kind, action, bpath, brev))
+ kind = _inverted_kindmap[kind]
+ action = _inverted_actionmap[action]
+ db("""
+ INSERT INTO node_change
+ (repos,rev,path,node_type,change_type,base_path,
+ base_rev)
+ VALUES (%s,%s,%s,%s,%s,%s,%s)
+ """, (self.id, srev, path, kind, action, bpath, brev))
+
def get_node(self, path, rev=None):
return self.repos.get_node(path, self.normalize_rev(rev))
Modified: incubator/bloodhound/vendor/trac/current/trac/versioncontrol/diff.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/vendor/trac/current/trac/versioncontrol/diff.py?rev=1398858&r1=1398857&r2=1398858&view=diff
==============================================================================
--- incubator/bloodhound/vendor/trac/current/trac/versioncontrol/diff.py (original)
+++ incubator/bloodhound/vendor/trac/current/trac/versioncontrol/diff.py Tue Oct 16 15:55:00 2012
@@ -177,7 +177,7 @@ def filter_ignorable_lines(hunks, fromli
def hdf_diff(*args, **kwargs):
- """:deprecated: use `diff_blocks` (will be removed in 0.14)"""
+ """:deprecated: use `diff_blocks` (will be removed in 1.1.1)"""
return diff_blocks(*args, **kwargs)
def diff_blocks(fromlines, tolines, context=None, tabwidth=8,
@@ -309,7 +309,7 @@ def get_diff_options(req):
pref = int(req.session.get('diff_' + name, default))
arg = int(name in req.args)
if 'update' in req.args and arg != pref:
- req.session['diff_' + name] = arg
+ req.session.set('diff_' + name, arg, default)
else:
arg = pref
return arg
@@ -317,7 +317,7 @@ def get_diff_options(req):
pref = req.session.get('diff_style', 'inline')
style = req.args.get('style', pref)
if 'update' in req.args and style != pref:
- req.session['diff_style'] = style
+ req.session.set('diff_style', style, 'inline')
data['style'] = style
pref = int(req.session.get('diff_contextlines', 2))
@@ -326,7 +326,7 @@ def get_diff_options(req):
except ValueError:
context = -1
if 'update' in req.args and context != pref:
- req.session['diff_contextlines'] = context
+ req.session.set('diff_contextlines', context, 2)
options_data['contextlines'] = context
arg = int(req.args.get('contextall', 0))
Modified: incubator/bloodhound/vendor/trac/current/trac/versioncontrol/svn_fs.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/vendor/trac/current/trac/versioncontrol/svn_fs.py?rev=1398858&r1=1398857&r2=1398858&view=diff
==============================================================================
--- incubator/bloodhound/vendor/trac/current/trac/versioncontrol/svn_fs.py (original)
+++ incubator/bloodhound/vendor/trac/current/trac/versioncontrol/svn_fs.py Tue Oct 16 15:55:00 2012
@@ -1,8 +1,6 @@
# -*- coding: utf-8 -*-
#
-# Copyright (C) 2005-2011 Edgewall Software
-# Copyright (C) 2005 Christopher Lenz <cm...@gmx.de>
-# Copyright (C) 2005-2007 Christian Boos <cb...@neuf.fr>
+# Copyright (C) 2012 Edgewall Software
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
@@ -12,1076 +10,10 @@
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://trac.edgewall.org/log/.
-#
-# Author: Christopher Lenz <cm...@gmx.de>
-# Christian Boos <cb...@neuf.fr>
-
-"""
-
-Note about Unicode
-------------------
-
-The Subversion bindings are not unicode-aware and they expect to
-receive UTF-8 encoded `string` parameters,
-
-On the other hand, all paths manipulated by Trac are `unicode`
-objects.
-
-Therefore:
-
- * before being handed out to SVN, the Trac paths have to be encoded
- to UTF-8, using `_to_svn()`
-
- * before being handed out to Trac, a SVN path has to be decoded from
- UTF-8, using `_from_svn()`
-
-Whenever a value has to be stored as utf8, we explicitly mark the
-variable name with "_utf8", in order to avoid any possible confusion.
-
-Warning:
- `SubversionNode.get_content()` returns an object from which one can
- read a stream of bytes. NO guarantees can be given about what that
- stream of bytes represents. It might be some text, encoded in some
- way or another. SVN properties *might* give some hints about the
- content, but they actually only reflect the beliefs of whomever set
- those properties...
-"""
-
-import os.path
-import weakref
-import posixpath
-
-from trac.config import ListOption
-from trac.core import *
-from trac.env import ISystemInfoProvider
-from trac.versioncontrol import Changeset, Node, Repository, \
- IRepositoryConnector, \
- NoSuchChangeset, NoSuchNode
-from trac.versioncontrol.cache import CachedRepository
-from trac.util import embedded_numbers
-from trac.util.text import exception_to_unicode, to_unicode
-from trac.util.translation import _
-from trac.util.datefmt import from_utimestamp
-
-
-application_pool = None
-
-
-def _import_svn():
- global fs, repos, core, delta, _kindmap
- from svn import fs, repos, core, delta
- _kindmap = {core.svn_node_dir: Node.DIRECTORY,
- core.svn_node_file: Node.FILE}
- # Protect svn.core methods from GC
- Pool.apr_pool_clear = staticmethod(core.apr_pool_clear)
- Pool.apr_pool_destroy = staticmethod(core.apr_pool_destroy)
-
-def _to_svn(pool, *args):
- """Expect a pool and a list of `unicode` path components.
-
- Returns an UTF-8 encoded string suitable for the Subversion python
- bindings (the returned path never starts with a leading "/")
- """
- return core.svn_path_canonicalize('/'.join(args).lstrip('/')
- .encode('utf-8'),
- pool)
-
-def _from_svn(path):
- """Expect an UTF-8 encoded string and transform it to an `unicode` object
-
- But Subversion repositories built from conversion utilities can have
- non-UTF-8 byte strings, so we have to convert using `to_unicode`.
- """
- return path and to_unicode(path, 'utf-8')
-
-# The following 3 helpers deal with unicode paths
-
-def _normalize_path(path):
- """Remove leading "/", except for the root."""
- return path and path.strip('/') or '/'
-
-def _path_within_scope(scope, fullpath):
- """Remove the leading scope from repository paths.
-
- Return `None` if the path is not is scope.
- """
- if fullpath is not None:
- fullpath = fullpath.lstrip('/')
- if scope == '/':
- return _normalize_path(fullpath)
- scope = scope.strip('/')
- if (fullpath + '/').startswith(scope + '/'):
- return fullpath[len(scope) + 1:] or '/'
-
-def _is_path_within_scope(scope, fullpath):
- """Check whether the given `fullpath` is within the given `scope`"""
- if scope == '/':
- return fullpath is not None
- fullpath = fullpath.lstrip('/') if fullpath else ''
- scope = scope.strip('/')
- return (fullpath + '/').startswith(scope + '/')
-
-# svn_opt_revision_t helpers
-
-def _svn_rev(num):
- value = core.svn_opt_revision_value_t()
- value.number = num
- revision = core.svn_opt_revision_t()
- revision.kind = core.svn_opt_revision_number
- revision.value = value
- return revision
-
-def _svn_head():
- revision = core.svn_opt_revision_t()
- revision.kind = core.svn_opt_revision_head
- return revision
-
-# apr_pool_t helpers
-
-def _mark_weakpool_invalid(weakpool):
- if weakpool():
- weakpool()._mark_invalid()
-
-
-class Pool(object):
- """A Pythonic memory pool object"""
-
- def __init__(self, parent_pool=None):
- """Create a new memory pool"""
-
- global application_pool
- self._parent_pool = parent_pool or application_pool
-
- # Create pool
- if self._parent_pool:
- self._pool = core.svn_pool_create(self._parent_pool())
- else:
- # If we are an application-level pool,
- # then initialize APR and set this pool
- # to be the application-level pool
- core.apr_initialize()
- application_pool = self
-
- self._pool = core.svn_pool_create(None)
- self._mark_valid()
-
- def __call__(self):
- return self._pool
-
- def valid(self):
- """Check whether this memory pool and its parents
- are still valid"""
- return hasattr(self,"_is_valid")
-
- def assert_valid(self):
- """Assert that this memory_pool is still valid."""
- assert self.valid()
-
- def clear(self):
- """Clear embedded memory pool. Invalidate all subpools."""
- self.apr_pool_clear(self._pool)
- self._mark_valid()
-
- def destroy(self):
- """Destroy embedded memory pool. If you do not destroy
- the memory pool manually, Python will destroy it
- automatically."""
-
- global application_pool
-
- self.assert_valid()
-
- # Destroy pool
- self.apr_pool_destroy(self._pool)
-
- # Clear application pool and terminate APR if necessary
- if not self._parent_pool:
- application_pool = None
-
- self._mark_invalid()
-
- def __del__(self):
- """Automatically destroy memory pools, if necessary"""
- if self.valid():
- self.destroy()
-
- def _mark_valid(self):
- """Mark pool as valid"""
- if self._parent_pool:
- # Refer to self using a weakreference so that we don't
- # create a reference cycle
- weakself = weakref.ref(self)
-
- # Set up callbacks to mark pool as invalid when parents
- # are destroyed
- self._weakref = weakref.ref(self._parent_pool._is_valid,
- lambda x: \
- _mark_weakpool_invalid(weakself))
-
- # mark pool as valid
- self._is_valid = lambda: 1
-
- def _mark_invalid(self):
- """Mark pool as invalid"""
- if self.valid():
- # Mark invalid
- del self._is_valid
-
- # Free up memory
- del self._parent_pool
- if hasattr(self, "_weakref"):
- del self._weakref
-
-
-class SvnCachedRepository(CachedRepository):
- """Subversion-specific cached repository, zero-pads revision numbers
- in the cache tables.
- """
- has_linear_changesets = True
-
- def db_rev(self, rev):
- return '%010d' % rev
-
- def rev_db(self, rev):
- return int(rev or 0)
-
-
-class SubversionConnector(Component):
-
- implements(ISystemInfoProvider, IRepositoryConnector)
-
- branches = ListOption('svn', 'branches', 'trunk, branches/*', doc=
- """Comma separated list of paths categorized as branches.
- If a path ends with '*', then all the directory entries found below
- that path will be included.
- Example: `/trunk, /branches/*, /projectAlpha/trunk, /sandbox/*`
- """)
-
- tags = ListOption('svn', 'tags', 'tags/*', doc=
- """Comma separated list of paths categorized as tags.
-
- If a path ends with '*', then all the directory entries found below
- that path will be included.
- Example: `/tags/*, /projectAlpha/tags/A-1.0, /projectAlpha/tags/A-v1.1`
- """)
-
- error = None
-
- def __init__(self):
- self._version = None
- try:
- _import_svn()
- self.log.debug('Subversion bindings imported')
- except ImportError, e:
- self.error = e
- self.log.info('Failed to load Subversion bindings', exc_info=True)
- else:
- version = (core.SVN_VER_MAJOR, core.SVN_VER_MINOR,
- core.SVN_VER_MICRO)
- self._version = '%d.%d.%d' % version + core.SVN_VER_TAG
- if version[0] < 1:
- self.error = _("Subversion >= 1.0 required, found %(version)s",
- version=self._version)
- Pool()
-
- # ISystemInfoProvider methods
-
- def get_system_info(self):
- if self._version is not None:
- yield 'Subversion', self._version
-
- # IRepositoryConnector methods
-
- def get_supported_types(self):
- prio = 1
- if self.error:
- prio = -1
- yield ("direct-svnfs", prio * 4)
- yield ("svnfs", prio * 4)
- yield ("svn", prio * 2)
-
- def get_repository(self, type, dir, params):
- """Return a `SubversionRepository`.
-
- The repository is wrapped in a `CachedRepository`, unless `type` is
- 'direct-svnfs'.
- """
- params.update(tags=self.tags, branches=self.branches)
- repos = SubversionRepository(dir, params, self.log)
- if type != 'direct-svnfs':
- repos = SvnCachedRepository(self.env, repos, self.log)
- return repos
-
-
-class SubversionRepository(Repository):
- """Repository implementation based on the svn.fs API."""
-
- has_linear_changesets = True
-
- def __init__(self, path, params, log):
- self.log = log
- self.pool = Pool()
-
- # Remove any trailing slash or else subversion might abort
- if isinstance(path, unicode):
- path_utf8 = path.encode('utf-8')
- else: # note that this should usually not happen (unicode arg expected)
- path_utf8 = to_unicode(path).encode('utf-8')
-
- path_utf8 = os.path.normpath(path_utf8).replace('\\', '/')
- self.path = path_utf8.decode('utf-8')
-
- root_path_utf8 = repos.svn_repos_find_root_path(path_utf8, self.pool())
- if root_path_utf8 is None:
- raise TracError(_("%(path)s does not appear to be a Subversion "
- "repository.", path=to_unicode(path_utf8)))
-
- try:
- self.repos = repos.svn_repos_open(root_path_utf8, self.pool())
- except core.SubversionException, e:
- raise TracError(_("Couldn't open Subversion repository %(path)s: "
- "%(svn_error)s", path=to_unicode(path_utf8),
- svn_error=exception_to_unicode(e)))
- self.fs_ptr = repos.svn_repos_fs(self.repos)
-
- self.uuid = fs.get_uuid(self.fs_ptr, self.pool())
- self.base = 'svn:%s:%s' % (self.uuid, _from_svn(root_path_utf8))
- name = 'svn:%s:%s' % (self.uuid, self.path)
-
- Repository.__init__(self, name, params, log)
-
- # if root_path_utf8 is shorter than the path_utf8, the difference is
- # this scope (which always starts with a '/')
- if root_path_utf8 != path_utf8:
- self.scope = path_utf8[len(root_path_utf8):].decode('utf-8')
- if not self.scope[-1] == '/':
- self.scope += '/'
- else:
- self.scope = '/'
- assert self.scope[0] == '/'
- # we keep root_path_utf8 for RA
- ra_prefix = 'file:///' if os.name == 'nt' else 'file://'
- self.ra_url_utf8 = ra_prefix + root_path_utf8
- self.clear()
-
- def clear(self, youngest_rev=None):
- """Reset notion of `youngest` and `oldest`"""
- self.youngest = None
- if youngest_rev is not None:
- self.youngest = self.normalize_rev(youngest_rev)
- self.oldest = None
-
- def __del__(self):
- self.close()
-
- def has_node(self, path, rev=None, pool=None):
- """Check if `path` exists at `rev` (or latest if unspecified)"""
- if not pool:
- pool = self.pool
- rev = self.normalize_rev(rev)
- rev_root = fs.revision_root(self.fs_ptr, rev, pool())
- node_type = fs.check_path(rev_root, _to_svn(pool(), self.scope, path),
- pool())
- return node_type in _kindmap
-
- def normalize_path(self, path):
- """Take any path specification and produce a path suitable for
- the rest of the API
- """
- return _normalize_path(path)
-
- def normalize_rev(self, rev):
- """Take any revision specification and produce a revision suitable
- for the rest of the API
- """
- if rev is None or isinstance(rev, basestring) and \
- rev.lower() in ('', 'head', 'latest', 'youngest'):
- return self.youngest_rev
- else:
- try:
- rev = int(rev)
- if rev <= self.youngest_rev:
- return rev
- except (ValueError, TypeError):
- pass
- raise NoSuchChangeset(rev)
-
- def close(self):
- """Dispose of low-level resources associated to this repository."""
- if self.pool:
- self.pool.destroy()
- self.repos = self.fs_ptr = self.pool = None
-
- def get_base(self):
- """Retrieve the base path corresponding to the Subversion
- repository itself.
-
- This is the same as the `.path` property minus the
- intra-repository scope, if one was specified.
- """
- return self.base
-
- def _get_tags_or_branches(self, paths):
- """Retrieve known branches or tags."""
- for path in self.params.get(paths, []):
- if path.endswith('*'):
- folder = posixpath.dirname(path)
- try:
- entries = [n for n in self.get_node(folder).get_entries()]
- for node in sorted(entries, key=lambda n:
- embedded_numbers(n.path.lower())):
- if node.kind == Node.DIRECTORY:
- yield node
- except Exception: # no right (TODO: use a specific Exception)
- pass
- else:
- try:
- yield self.get_node(path)
- except Exception: # no right
- pass
-
- def get_quickjump_entries(self, rev):
- """Retrieve known branches, as (name, id) pairs.
-
- Purposedly ignores `rev` and always takes the last revision.
- """
- for n in self._get_tags_or_branches('branches'):
- yield 'branches', n.path, n.path, None
- for n in self._get_tags_or_branches('tags'):
- yield 'tags', n.path, n.created_path, n.created_rev
-
- def get_path_url(self, path, rev):
- """Retrieve the "native" URL from which this repository is reachable
- from Subversion clients.
- """
- url = self.params.get('url', '').rstrip('/')
- if url:
- if not path or path == '/':
- return url
- return url + '/' + path.lstrip('/')
-
- def get_changeset(self, rev):
- """Produce a `SubversionChangeset` from given revision
- specification"""
- rev = self.normalize_rev(rev)
- return SubversionChangeset(self, rev, self.scope, self.pool)
-
- def get_changeset_uid(self, rev):
- """Build a value identifying the `rev` in this repository."""
- return (self.uuid, rev)
-
- def get_node(self, path, rev=None):
- """Produce a `SubversionNode` from given path and optionally revision
- specifications. No revision given means use the latest.
- """
- path = path or ''
- if path and path[-1] == '/':
- path = path[:-1]
- rev = self.normalize_rev(rev) or self.youngest_rev
- return SubversionNode(path, rev, self, self.pool)
-
- def _get_node_revs(self, path, last=None, first=None):
- """Return the revisions affecting `path` between `first` and `last`
- revs. If `first` is not given, it goes down to the revision in which
- the branch was created.
- """
- node = self.get_node(path, last)
- revs = []
- for (p, r, chg) in node.get_history():
- if p != path or (first and r < first):
- break
- revs.append(r)
- return revs
-
- def _history(self, path, start, end, pool):
- """`path` is a unicode path in the scope.
-
- Generator yielding `(path, rev)` pairs, where `path` is an `unicode`
- object. Must start with `(path, created rev)`.
-
- (wraps ``fs.node_history``)
- """
- path_utf8 = _to_svn(pool(), self.scope, path)
- if start < end:
- start, end = end, start
- if (start, end) == (1, 0): # only happens for empty repos
- return
- root = fs.revision_root(self.fs_ptr, start, pool())
- # fs.node_history leaks when path doesn't exist (#6588)
- if fs.check_path(root, path_utf8, pool()) == core.svn_node_none:
- return
- tmp1 = Pool(pool)
- tmp2 = Pool(pool)
- history_ptr = fs.node_history(root, path_utf8, tmp1())
- cross_copies = 1
- while history_ptr:
- history_ptr = fs.history_prev(history_ptr, cross_copies, tmp2())
- tmp1.clear()
- tmp1, tmp2 = tmp2, tmp1
- if history_ptr:
- path_utf8, rev = fs.history_location(history_ptr, tmp2())
- tmp2.clear()
- if rev < end:
- break
- path = _from_svn(path_utf8)
- yield path, rev
- del tmp1
- del tmp2
-
- def _previous_rev(self, rev, path='', pool=None):
- if rev > 1: # don't use oldest here, as it's too expensive
- for _, prev in self._history(path, 1, rev-1, pool or self.pool):
- return prev
- return None
-
-
- def get_oldest_rev(self):
- """Gives an approximation of the oldest revision."""
- if self.oldest is None:
- self.oldest = 1
- # trying to figure out the oldest rev for scoped repository
- # is too expensive and uncovers a big memory leak (#5213)
- # if self.scope != '/':
- # self.oldest = self.next_rev(0, find_initial_rev=True)
- return self.oldest
-
- def get_youngest_rev(self):
- """Retrieve the latest revision in the repository.
-
- (wraps ``fs.youngest_rev``)
- """
- if not self.youngest:
- self.youngest = fs.youngest_rev(self.fs_ptr, self.pool())
- if self.scope != '/':
- for path, rev in self._history('', 1, self.youngest, self.pool):
- self.youngest = rev
- break
- return self.youngest
-
- def previous_rev(self, rev, path=''):
- """Return revision immediately preceeding `rev`, eventually below
- given `path` or globally.
- """
- # FIXME optimize for non-scoped
- rev = self.normalize_rev(rev)
- return self._previous_rev(rev, path)
-
- def next_rev(self, rev, path='', find_initial_rev=False):
- """Return revision immediately following `rev`, eventually below
- given `path` or globally.
- """
- rev = self.normalize_rev(rev)
- next = rev + 1
- youngest = self.youngest_rev
- subpool = Pool(self.pool)
- while next <= youngest:
- subpool.clear()
- for _, next in self._history(path, rev+1, next, subpool):
- return next
- else:
- if not find_initial_rev and \
- not self.has_node(path, next, subpool):
- return next # a 'delete' event is also interesting...
- next += 1
- return None
-
- def rev_older_than(self, rev1, rev2):
- """Check relative order between two revision specifications."""
- return self.normalize_rev(rev1) < self.normalize_rev(rev2)
-
- def get_path_history(self, path, rev=None, limit=None):
- """Retrieve creation and deletion events that happened on
- given `path`.
- """
- path = self.normalize_path(path)
- rev = self.normalize_rev(rev)
- expect_deletion = False
- subpool = Pool(self.pool)
- numrevs = 0
- while rev and (not limit or numrevs < limit):
- subpool.clear()
- if self.has_node(path, rev, subpool):
- if expect_deletion:
- # it was missing, now it's there again:
- # rev+1 must be a delete
- numrevs += 1
- yield path, rev+1, Changeset.DELETE
- newer = None # 'newer' is the previously seen history tuple
- older = None # 'older' is the currently examined history tuple
- for p, r in self._history(path, 1, rev, subpool):
- older = (_path_within_scope(self.scope, p), r,
- Changeset.ADD)
- rev = self._previous_rev(r, pool=subpool)
- if newer:
- numrevs += 1
- if older[0] == path:
- # still on the path: 'newer' was an edit
- yield newer[0], newer[1], Changeset.EDIT
- else:
- # the path changed: 'newer' was a copy
- rev = self._previous_rev(newer[1], pool=subpool)
- # restart before the copy op
- yield newer[0], newer[1], Changeset.COPY
- older = (older[0], older[1], 'unknown')
- break
- newer = older
- if older:
- # either a real ADD or the source of a COPY
- numrevs += 1
- yield older
- else:
- expect_deletion = True
- rev = self._previous_rev(rev, pool=subpool)
-
- def get_changes(self, old_path, old_rev, new_path, new_rev,
- ignore_ancestry=0):
- """Determine differences between two arbitrary pairs of paths
- and revisions.
-
- (wraps ``repos.svn_repos_dir_delta``)
- """
- old_node = new_node = None
- old_rev = self.normalize_rev(old_rev)
- new_rev = self.normalize_rev(new_rev)
- if self.has_node(old_path, old_rev):
- old_node = self.get_node(old_path, old_rev)
- else:
- raise NoSuchNode(old_path, old_rev, 'The Base for Diff is invalid')
- if self.has_node(new_path, new_rev):
- new_node = self.get_node(new_path, new_rev)
- else:
- raise NoSuchNode(new_path, new_rev,
- 'The Target for Diff is invalid')
- if new_node.kind != old_node.kind:
- raise TracError(_('Diff mismatch: Base is a %(oldnode)s '
- '(%(oldpath)s in revision %(oldrev)s) and '
- 'Target is a %(newnode)s (%(newpath)s in '
- 'revision %(newrev)s).', oldnode=old_node.kind,
- oldpath=old_path, oldrev=old_rev,
- newnode=new_node.kind, newpath=new_path,
- newrev=new_rev))
- subpool = Pool(self.pool)
- if new_node.isdir:
- editor = DiffChangeEditor()
- e_ptr, e_baton = delta.make_editor(editor, subpool())
- old_root = fs.revision_root(self.fs_ptr, old_rev, subpool())
- new_root = fs.revision_root(self.fs_ptr, new_rev, subpool())
- def authz_cb(root, path, pool):
- return 1
- text_deltas = 0 # as this is anyway re-done in Diff.py...
- entry_props = 0 # "... typically used only for working copy updates"
- repos.svn_repos_dir_delta(old_root,
- _to_svn(subpool(), self.scope, old_path),
- '', new_root,
- _to_svn(subpool(), self.scope, new_path),
- e_ptr, e_baton, authz_cb,
- text_deltas,
- 1, # directory
- entry_props,
- ignore_ancestry,
- subpool())
- for path, kind, change in editor.deltas:
- path = _from_svn(path)
- old_node = new_node = None
- if change != Changeset.ADD:
- old_node = self.get_node(posixpath.join(old_path, path),
- old_rev)
- if change != Changeset.DELETE:
- new_node = self.get_node(posixpath.join(new_path, path),
- new_rev)
- else:
- kind = _kindmap[fs.check_path(old_root,
- _to_svn(subpool(),
- self.scope,
- old_node.path),
- subpool())]
- yield (old_node, new_node, kind, change)
- else:
- old_root = fs.revision_root(self.fs_ptr, old_rev, subpool())
- new_root = fs.revision_root(self.fs_ptr, new_rev, subpool())
- if fs.contents_changed(old_root,
- _to_svn(subpool(), self.scope, old_path),
- new_root,
- _to_svn(subpool(), self.scope, new_path),
- subpool()):
- yield (old_node, new_node, Node.FILE, Changeset.EDIT)
-
-
-class SubversionNode(Node):
-
- def __init__(self, path, rev, repos, pool=None, parent_root=None):
- self.fs_ptr = repos.fs_ptr
- self.scope = repos.scope
- self.pool = Pool(pool)
- pool = self.pool()
- self._scoped_path_utf8 = _to_svn(pool, self.scope, path)
-
- if parent_root:
- self.root = parent_root
- else:
- self.root = fs.revision_root(self.fs_ptr, rev, pool)
- node_type = fs.check_path(self.root, self._scoped_path_utf8, pool)
- if not node_type in _kindmap:
- raise NoSuchNode(path, rev)
- cp_utf8 = fs.node_created_path(self.root, self._scoped_path_utf8, pool)
- cp = _from_svn(cp_utf8)
- cr = fs.node_created_rev(self.root, self._scoped_path_utf8, pool)
- # Note: `cp` differs from `path` if the last change was a copy,
- # In that case, `path` doesn't even exist at `cr`.
- # The only guarantees are:
- # * this node exists at (path,rev)
- # * the node existed at (created_path,created_rev)
- # Also, `cp` might well be out of the scope of the repository,
- # in this case, we _don't_ use the ''create'' information.
- if _is_path_within_scope(self.scope, cp):
- self.created_rev = cr
- self.created_path = _path_within_scope(self.scope, cp)
- else:
- self.created_rev, self.created_path = rev, path
- # TODO: check node id
- Node.__init__(self, repos, path, rev, _kindmap[node_type])
-
- def get_content(self):
- """Retrieve raw content as a "read()"able object."""
- if self.isdir:
- return None
- s = core.Stream(fs.file_contents(self.root, self._scoped_path_utf8,
- self.pool()))
- # The stream object needs to reference the pool to make sure the pool
- # is not destroyed before the former.
- s._pool = self.pool
- return s
-
- def get_entries(self):
- """Yield `SubversionNode` corresponding to entries in this directory.
-
- (wraps ``fs.dir_entries``)
- """
- if self.isfile:
- return
- pool = Pool(self.pool)
- entries = fs.dir_entries(self.root, self._scoped_path_utf8, pool())
- for item in entries.keys():
- path = posixpath.join(self.path, _from_svn(item))
- yield SubversionNode(path, self.rev, self.repos, self.pool,
- self.root)
-
- def get_history(self, limit=None):
- """Yield change events that happened on this path"""
- newer = None # 'newer' is the previously seen history tuple
- older = None # 'older' is the currently examined history tuple
- pool = Pool(self.pool)
- numrevs = 0
- for path, rev in self.repos._history(self.path, 1, self.rev, pool):
- path = _path_within_scope(self.scope, path)
- if rev > 0 and path:
- older = (path, rev, Changeset.ADD)
- if newer:
- if newer[0] == older[0]: # stay on same path
- change = Changeset.EDIT
- else:
- change = Changeset.COPY
- newer = (newer[0], newer[1], change)
- numrevs += 1
- yield newer
- newer = older
- if limit and numrevs >= limit:
- break
- if newer and (not limit or numrevs < limit):
- yield newer
-
- def get_annotations(self):
- """Return a list the last changed revision for each line.
- (wraps ``client.blame2``)
- """
- annotations = []
- if self.isfile:
- def blame_receiver(line_no, revision, author, date, line, pool):
- annotations.append(revision)
- try:
- rev = _svn_rev(self.rev)
- start = _svn_rev(0)
- file_url_utf8 = posixpath.join(self.repos.ra_url_utf8,
- self._scoped_path_utf8)
- self.repos.log.info('opening ra_local session to %r',
- file_url_utf8)
- from svn import client
- client.blame2(file_url_utf8, rev, start, rev, blame_receiver,
- client.create_context(), self.pool())
- except (core.SubversionException, AttributeError), e:
- # svn thinks file is a binary or blame not supported
- raise TracError(_('svn blame failed on %(path)s: %(error)s',
- path=self.path, error=to_unicode(e)))
- return annotations
-
-# def get_previous(self):
-# # FIXME: redo it with fs.node_history
-
- def get_properties(self):
- """Return `dict` of node properties at current revision.
-
- (wraps ``fs.node_proplist``)
- """
- props = fs.node_proplist(self.root, self._scoped_path_utf8, self.pool())
- for name, value in props.items():
- # Note that property values can be arbitrary binary values
- # so we can't assume they are UTF-8 strings...
- props[_from_svn(name)] = to_unicode(value)
- return props
-
- def get_content_length(self):
- """Retrieve byte size of a file.
-
- Return `None` for a folder. (wraps ``fs.file_length``)
- """
- if self.isdir:
- return None
- return fs.file_length(self.root, self._scoped_path_utf8, self.pool())
-
- def get_content_type(self):
- """Retrieve mime-type property of a file.
-
- Return `None` for a folder. (wraps ``fs.revision_prop``)
- """
- if self.isdir:
- return None
- return self._get_prop(core.SVN_PROP_MIME_TYPE)
-
- def get_last_modified(self):
- """Retrieve timestamp of last modification, in micro-seconds.
-
- (wraps ``fs.revision_prop``)
- """
- _date = fs.revision_prop(self.fs_ptr, self.created_rev,
- core.SVN_PROP_REVISION_DATE, self.pool())
- if not _date:
- return None
- return from_utimestamp(core.svn_time_from_cstring(_date, self.pool()))
-
- def _get_prop(self, name):
- return fs.node_prop(self.root, self._scoped_path_utf8, name,
- self.pool())
-
- def get_branch_origin(self):
- """Return the revision in which the node's path was created.
-
- (wraps ``fs.revision_root_revision(fs.closest_copy)``)
- """
- root_and_path = fs.closest_copy(self.root, self._scoped_path_utf8)
- if root_and_path:
- return fs.revision_root_revision(root_and_path[0])
-
- def get_copy_ancestry(self):
- """Retrieve the list of `(path,rev)` copy ancestors of this node.
- Most recent ancestor first. Each ancestor `(path, rev)` corresponds
- to the path and revision of the source at the time the copy or move
- operation was performed.
- """
- ancestors = []
- previous = (self._scoped_path_utf8, self.rev, self.root)
- while previous:
- (previous_path, previous_rev, previous_root) = previous
- previous = None
- root_path = fs.closest_copy(previous_root, previous_path)
- if root_path:
- (root, path) = root_path
- path = path.lstrip('/')
- rev = fs.revision_root_revision(root)
- relpath = None
- if path != previous_path:
- # `previous_path` is a subfolder of `path` and didn't
- # change since `path` was copied
- relpath = previous_path[len(path):].strip('/')
- copied_from = fs.copied_from(root, path)
- if copied_from:
- (rev, path) = copied_from
- path = path.lstrip('/')
- root = fs.revision_root(self.fs_ptr, rev, self.pool())
- if relpath:
- path += '/' + relpath
- ui_path = _path_within_scope(self.scope, _from_svn(path))
- if ui_path:
- ancestors.append((ui_path, rev))
- previous = (path, rev, root)
- return ancestors
-
-
-class SubversionChangeset(Changeset):
-
- def __init__(self, repos, rev, scope, pool=None):
- self.rev = rev
- self.scope = scope
- self.fs_ptr = repos.fs_ptr
- self.pool = Pool(pool)
- try:
- message = self._get_prop(core.SVN_PROP_REVISION_LOG)
- except core.SubversionException:
- raise NoSuchChangeset(rev)
- author = self._get_prop(core.SVN_PROP_REVISION_AUTHOR)
- # we _hope_ it's UTF-8, but can't be 100% sure (#4321)
- message = message and to_unicode(message, 'utf-8')
- author = author and to_unicode(author, 'utf-8')
- _date = self._get_prop(core.SVN_PROP_REVISION_DATE)
- if _date:
- ts = core.svn_time_from_cstring(_date, self.pool())
- date = from_utimestamp(ts)
- else:
- date = None
- Changeset.__init__(self, repos, rev, message, author, date)
-
- def get_properties(self):
- """Retrieve `dict` of Subversion properties for this revision
- (revprops)
- """
- props = fs.revision_proplist(self.fs_ptr, self.rev, self.pool())
- properties = {}
- for k, v in props.iteritems():
- if k not in (core.SVN_PROP_REVISION_LOG,
- core.SVN_PROP_REVISION_AUTHOR,
- core.SVN_PROP_REVISION_DATE):
- properties[k] = to_unicode(v)
- # Note: the above `to_unicode` has a small probability
- # to mess-up binary properties, like icons.
- return properties
-
- def get_changes(self):
- """Retrieve file changes for a given revision.
-
- (wraps ``repos.svn_repos_replay``)
- """
- pool = Pool(self.pool)
- tmp = Pool(pool)
- root = fs.revision_root(self.fs_ptr, self.rev, pool())
- editor = repos.RevisionChangeCollector(self.fs_ptr, self.rev, pool())
- e_ptr, e_baton = delta.make_editor(editor, pool())
- repos.svn_repos_replay(root, e_ptr, e_baton, pool())
-
- idx = 0
- copies, deletions = {}, {}
- changes = []
- revroots = {}
- for path_utf8, change in editor.changes.items():
- new_path = _from_svn(path_utf8)
-
- # Filtering on `path`
- if not _is_path_within_scope(self.scope, new_path):
- continue
-
- path_utf8 = change.path
- base_path_utf8 = change.base_path
- path = _from_svn(path_utf8)
- base_path = _from_svn(base_path_utf8)
- base_rev = change.base_rev
- change_action = getattr(change, 'action', None)
-
- # Ensure `base_path` is within the scope
- if not _is_path_within_scope(self.scope, base_path):
- base_path, base_rev = None, -1
-
- # Determine the action
- if not path and not new_path and self.scope == '/':
- action = Changeset.EDIT # root property change
- elif not path or (change_action is not None
- and change_action == repos.CHANGE_ACTION_DELETE):
- if new_path: # deletion
- action = Changeset.DELETE
- deletions[new_path.lstrip('/')] = idx
- else: # deletion outside of scope, ignore
- continue
- elif change.added or not base_path: # add or copy
- action = Changeset.ADD
- if base_path and base_rev:
- action = Changeset.COPY
- copies[base_path.lstrip('/')] = idx
- else:
- action = Changeset.EDIT
- # identify the most interesting base_path/base_rev
- # in terms of last changed information (see r2562)
- if revroots.has_key(base_rev):
- b_root = revroots[base_rev]
- else:
- b_root = fs.revision_root(self.fs_ptr, base_rev, pool())
- revroots[base_rev] = b_root
- tmp.clear()
- cbase_path_utf8 = fs.node_created_path(b_root, base_path_utf8,
- tmp())
- cbase_path = _from_svn(cbase_path_utf8)
- cbase_rev = fs.node_created_rev(b_root, base_path_utf8, tmp())
- # give up if the created path is outside the scope
- if _is_path_within_scope(self.scope, cbase_path):
- base_path, base_rev = cbase_path, cbase_rev
-
- kind = _kindmap[change.item_kind]
- path = _path_within_scope(self.scope, new_path or base_path)
- base_path = _path_within_scope(self.scope, base_path)
- changes.append([path, kind, action, base_path, base_rev])
- idx += 1
-
- moves = []
- # a MOVE is a COPY whose `base_path` corresponds to a `new_path`
- # which has been deleted
- for k, v in copies.items():
- if k in deletions:
- changes[v][2] = Changeset.MOVE
- moves.append(deletions[k])
- offset = 0
- moves.sort()
- for i in moves:
- del changes[i - offset]
- offset += 1
-
- changes.sort()
- for change in changes:
- yield tuple(change)
-
- def _get_prop(self, name):
- return fs.revision_prop(self.fs_ptr, self.rev, name, self.pool())
-
-
-#
-# Delta editor for diffs between arbitrary nodes
-#
-# Note 1: the 'copyfrom_path' and 'copyfrom_rev' information is not used
-# because 'repos.svn_repos_dir_delta' *doesn't* provide it.
-#
-# Note 2: the 'dir_baton' is the path of the parent directory
-#
-
-
-def DiffChangeEditor():
-
- class DiffChangeEditor(delta.Editor):
-
- def __init__(self):
- self.deltas = []
-
- # -- svn.delta.Editor callbacks
-
- def open_root(self, base_revision, dir_pool):
- return ('/', Changeset.EDIT)
-
- def add_directory(self, path, dir_baton, copyfrom_path, copyfrom_rev,
- dir_pool):
- self.deltas.append((path, Node.DIRECTORY, Changeset.ADD))
- return (path, Changeset.ADD)
-
- def open_directory(self, path, dir_baton, base_revision, dir_pool):
- return (path, dir_baton[1])
-
- def change_dir_prop(self, dir_baton, name, value, pool):
- path, change = dir_baton
- if change != Changeset.ADD:
- self.deltas.append((path, Node.DIRECTORY, change))
-
- def delete_entry(self, path, revision, dir_baton, pool):
- self.deltas.append((path, None, Changeset.DELETE))
-
- def add_file(self, path, dir_baton, copyfrom_path, copyfrom_revision,
- dir_pool):
- self.deltas.append((path, Node.FILE, Changeset.ADD))
-
- def open_file(self, path, dir_baton, dummy_rev, file_pool):
- self.deltas.append((path, Node.FILE, Changeset.EDIT))
- return DiffChangeEditor()
+from trac.util import import_namespace
+import_namespace(globals(), 'tracopt.versioncontrol.svn.svn_fs')
+# This module is a stub provided for backward compatibility. The svn_fs
+# module has been moved to tracopt.versioncontrol.svn. Please update your
+# code to use the new location.
Modified: incubator/bloodhound/vendor/trac/current/trac/versioncontrol/svn_prop.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/vendor/trac/current/trac/versioncontrol/svn_prop.py?rev=1398858&r1=1398857&r2=1398858&view=diff
==============================================================================
--- incubator/bloodhound/vendor/trac/current/trac/versioncontrol/svn_prop.py (original)
+++ incubator/bloodhound/vendor/trac/current/trac/versioncontrol/svn_prop.py Tue Oct 16 15:55:00 2012
@@ -1,8 +1,6 @@
# -*- coding: utf-8 -*-
#
-# Copyright (C) 2005-2009 Edgewall Software
-# Copyright (C) 2005 Christopher Lenz <cm...@gmx.de>
-# Copyright (C) 2005-2007 Christian Boos <cb...@neuf.fr>
+# Copyright (C) 2012 Edgewall Software
# All rights reserved.
#
# This software is licensed as described in the file COPYING, which
@@ -12,391 +10,10 @@
# This software consists of voluntary contributions made by many
# individuals. For the exact contribution history, see the revision
# history and logs, available at http://trac.edgewall.org/log/.
-#
-# Author: Christopher Lenz <cm...@gmx.de>
-# Christian Boos <cb...@neuf.fr>
-
-import posixpath
-
-from genshi.builder import tag
-
-from trac.config import ConfigSection
-from trac.core import *
-from trac.versioncontrol.api import NoSuchNode, RepositoryManager
-from trac.versioncontrol.svn_fs import _path_within_scope
-from trac.versioncontrol.web_ui.browser import IPropertyRenderer
-from trac.versioncontrol.web_ui.changeset import IPropertyDiffRenderer
-from trac.util import Ranges, to_ranges
-from trac.util.translation import _, tag_
-
-
-class SubversionPropertyRenderer(Component):
-
- implements(IPropertyRenderer)
-
- svn_externals_section = ConfigSection('svn:externals',
- """The TracBrowser for Subversion can interpret the `svn:externals`
- property of folders. By default, it only turns the URLs into links as
- Trac can't browse remote repositories.
-
- However, if you have another Trac instance (or an other repository
- browser like [http://www.viewvc.org/ ViewVC]) configured to browse the
- target repository, then you can instruct Trac which other repository
- browser to use for which external URL. This mapping is done in the
- `[svn:externals]` section of the TracIni.
-
- Example:
- {{{
- [svn:externals]
- 1 = svn://server/repos1 http://trac/proj1/browser/$path?rev=$rev
- 2 = svn://server/repos2 http://trac/proj2/browser/$path?rev=$rev
- 3 = http://theirserver.org/svn/eng-soft http://ourserver/viewvc/svn/$path/?pathrev=25914
- 4 = svn://anotherserver.com/tools_repository http://ourserver/tracs/tools/browser/$path?rev=$rev
- }}}
- With the above, the
- `svn://anotherserver.com/tools_repository/tags/1.1/tools` external will
- be mapped to `http://ourserver/tracs/tools/browser/tags/1.1/tools?rev=`
- (and `rev` will be set to the appropriate revision number if the
- external additionally specifies a revision, see the
- [http://svnbook.red-bean.com/en/1.4/svn.advanced.externals.html SVN Book on externals]
- for more details).
-
- Note that the number used as a key in the above section is purely used
- as a place holder, as the URLs themselves can't be used as a key due to
- various limitations in the configuration file parser.
-
- Finally, the relative URLs introduced in
- [http://subversion.apache.org/docs/release-notes/1.5.html#externals Subversion 1.5]
- are not yet supported.
-
- (''since 0.11'')""")
-
- def __init__(self):
- self._externals_map = {}
-
- # IPropertyRenderer methods
-
- def match_property(self, name, mode):
- if name in ('svn:externals', 'svn:needs-lock'):
- return 4
- return 2 if name in ('svn:mergeinfo', 'svnmerge-blocked',
- 'svnmerge-integrated') else 0
-
- def render_property(self, name, mode, context, props):
- if name == 'svn:externals':
- return self._render_externals(props[name])
- elif name == 'svn:needs-lock':
- return self._render_needslock(context)
- elif name == 'svn:mergeinfo' or name.startswith('svnmerge-'):
- return self._render_mergeinfo(name, mode, context, props)
-
- def _render_externals(self, prop):
- if not self._externals_map:
- for dummykey, value in self.svn_externals_section.options():
- value = value.split()
- if len(value) != 2:
- self.log.warn("svn:externals entry %s doesn't contain "
- "a space-separated key value pair, skipping.",
- dummykey)
- continue
- key, value = value
- self._externals_map[key] = value.replace('%', '%%') \
- .replace('$path', '%(path)s') \
- .replace('$rev', '%(rev)s')
- externals = []
- for external in prop.splitlines():
- elements = external.split()
- if not elements:
- continue
- localpath, rev, url = elements[0], '', elements[-1]
- if localpath.startswith('#'):
- externals.append((external, None, None, None, None))
- continue
- if len(elements) == 3:
- rev = elements[1]
- rev = rev.replace('-r', '')
- # retrieve a matching entry in the externals map
- prefix = []
- base_url = url
- while base_url:
- if base_url in self._externals_map or base_url == u'/':
- break
- base_url, pref = posixpath.split(base_url)
- prefix.append(pref)
- href = self._externals_map.get(base_url)
- revstr = ' at revision ' + rev if rev else ''
- if not href and (url.startswith('http://') or
- url.startswith('https://')):
- href = url.replace('%', '%%')
- if href:
- remotepath = ''
- if prefix:
- remotepath = posixpath.join(*reversed(prefix))
- externals.append((localpath, revstr, base_url, remotepath,
- href % {'path': remotepath, 'rev': rev}))
- else:
- externals.append((localpath, revstr, url, None, None))
- externals_data = []
- for localpath, rev, url, remotepath, href in externals:
- label = localpath
- if url is None:
- title = ''
- elif href:
- if url:
- url = ' in ' + url
- label += rev + url
- title = ''.join((remotepath, rev, url))
- else:
- title = _('No svn:externals configured in trac.ini')
- externals_data.append((label, href, title))
- return tag.ul([tag.li(tag.a(label, href=href, title=title))
- for label, href, title in externals_data])
-
- def _render_needslock(self, context):
- return tag.img(src=context.href.chrome('common/lock-locked.png'),
- alt="needs lock", title="needs lock")
-
- def _render_mergeinfo(self, name, mode, context, props):
- rows = []
- for row in props[name].splitlines():
- try:
- (path, revs) = row.rsplit(':', 1)
- rows.append([tag.td(path),
- tag.td(revs.replace(',', u',\u200b'))])
- except ValueError:
- rows.append(tag.td(row, colspan=2))
- return tag.table(tag.tbody([tag.tr(row) for row in rows]),
- class_='props')
-
-
-class SubversionMergePropertyRenderer(Component):
- implements(IPropertyRenderer)
-
- # IPropertyRenderer methods
-
- def match_property(self, name, mode):
- return 4 if name in ('svn:mergeinfo', 'svnmerge-blocked',
- 'svnmerge-integrated') else 0
-
- def render_property(self, name, mode, context, props):
- """Parse svn:mergeinfo and svnmerge-* properties, converting branch
- names to links and providing links to the revision log for merged
- and eligible revisions.
- """
- has_eligible = name in ('svnmerge-integrated', 'svn:mergeinfo')
- revs_label = _('blocked') if name.endswith('blocked') else _('merged')
- revs_cols = 2 if has_eligible else None
- reponame = context.resource.parent.id
- target_path = context.resource.id
- repos = RepositoryManager(self.env).get_repository(reponame)
- target_rev = context.resource.version
- if has_eligible:
- node = repos.get_node(target_path, target_rev)
- branch_starts = {}
- for path, rev in node.get_copy_ancestry():
- if path not in branch_starts:
- branch_starts[path] = rev + 1
- rows = []
- if name.startswith('svnmerge-'):
- sources = props[name].split()
- else:
- sources = props[name].splitlines()
- for line in sources:
- path, revs = line.split(':', 1)
- spath = _path_within_scope(repos.scope, path)
- if spath is None:
- continue
- revs = revs.strip()
- inheritable, non_inheritable = _partition_inheritable(revs)
- revs = ','.join(inheritable)
- deleted = False
- try:
- node = repos.get_node(spath, target_rev)
- resource = context.resource.parent.child('source', spath)
- if 'LOG_VIEW' in context.perm(resource):
- row = [_get_source_link(spath, context),
- _get_revs_link(revs_label, context, spath, revs)]
- if non_inheritable:
- non_inheritable = ','.join(non_inheritable)
- row.append(_get_revs_link(_('non-inheritable'), context,
- spath, non_inheritable,
- _('merged on the directory '
- 'itself but not below')))
- if has_eligible:
- first_rev = branch_starts.get(spath)
- if not first_rev:
- first_rev = node.get_branch_origin()
- eligible = set(xrange(first_rev or 1, target_rev + 1))
- eligible -= set(Ranges(revs))
- blocked = _get_blocked_revs(props, name, spath)
- if blocked:
- eligible -= set(Ranges(blocked))
- if eligible:
- nrevs = repos._get_node_revs(spath, max(eligible),
- min(eligible))
- eligible &= set(nrevs)
- eligible = to_ranges(eligible)
- row.append(_get_revs_link(_('eligible'), context,
- spath, eligible))
- rows.append((False, spath, [tag.td(each) for each in row]))
- continue
- except NoSuchNode:
- deleted = True
- revs = revs.replace(',', u',\u200b')
- rows.append((deleted, spath,
- [tag.td('/' + spath),
- tag.td(revs, colspan=revs_cols)]))
- if not rows:
- return None
- rows.sort()
- has_deleted = rows[-1][0] if rows else None
- return tag(has_deleted and tag.a(_('(toggle deleted branches)'),
- class_='trac-toggledeleted',
- href='#'),
- tag.table(tag.tbody(
- [tag.tr(row, class_='trac-deleted' if deleted else None)
- for deleted, spath, row in rows]), class_='props'))
-
-
-def _partition_inheritable(revs):
- """Non-inheritable revision ranges are marked with a trailing '*'."""
- inheritable, non_inheritable = [], []
- for r in revs.split(','):
- if r and r[-1] == '*':
- non_inheritable.append(r[:-1])
- else:
- inheritable.append(r)
- return inheritable, non_inheritable
-
-def _get_blocked_revs(props, name, path):
- """Return the revisions blocked from merging for the given property
- name and path.
- """
- if name == 'svnmerge-integrated':
- prop = props.get('svnmerge-blocked', '')
- else:
- return ""
- for line in prop.splitlines():
- try:
- p, revs = line.split(':', 1)
- if p.strip('/') == path:
- return revs
- except Exception:
- pass
- return ""
-
-def _get_source_link(spath, context):
- """Return a link to a merge source."""
- reponame = context.resource.parent.id
- return tag.a('/' + spath, title=_('View merge source'),
- href=context.href.browser(reponame or None, spath,
- rev=context.resource.version))
-
-def _get_revs_link(label, context, spath, revs, title=None):
- """Return a link to the revision log when more than one revision is
- given, to the revision itself for a single revision, or a `<span>`
- with "no revision" for none.
- """
- reponame = context.resource.parent.id
- if not revs:
- return tag.span(label, title=_('No revisions'))
- elif ',' in revs or '-' in revs:
- revs_href = context.href.log(reponame or None, spath, revs=revs)
- else:
- revs_href = context.href.changeset(revs, reponame or None, spath)
- revs = revs.replace(',', ', ')
- if title:
- title = _("%(title)s: %(revs)s", title=title, revs=revs)
- else:
- title = revs
- return tag.a(label, title=title, href=revs_href)
-
-
-class SubversionMergePropertyDiffRenderer(Component):
- implements(IPropertyDiffRenderer)
-
- # IPropertyDiffRenderer methods
- def match_property_diff(self, name):
- return 4 if name in ('svn:mergeinfo', 'svnmerge-blocked',
- 'svnmerge-integrated') else 0
+from trac.util import import_namespace
+import_namespace(globals(), 'tracopt.versioncontrol.svn.svn_prop')
- def render_property_diff(self, name, old_context, old_props,
- new_context, new_props, options):
- # Build 5 columns table showing modifications on merge sources
- # || source || added || removed || added (ni) || removed (ni) ||
- # || source || removed ||
- rm = RepositoryManager(self.env)
- repos = rm.get_repository(old_context.resource.parent.id)
- def parse_sources(props):
- sources = {}
- for line in props[name].splitlines():
- path, revs = line.split(':', 1)
- spath = _path_within_scope(repos.scope, path)
- if spath is not None:
- inheritable, non_inheritable = _partition_inheritable(revs)
- sources[spath] = (set(Ranges(inheritable)),
- set(Ranges(non_inheritable)))
- return sources
- old_sources = parse_sources(old_props)
- new_sources = parse_sources(new_props)
- # Go through new sources, detect modified ones or added ones
- blocked = name.endswith('blocked')
- added_label = [_("merged: "), _("blocked: ")][blocked]
- removed_label = [_("reverse-merged: "), _("un-blocked: ")][blocked]
- added_ni_label = _("marked as non-inheritable: ")
- removed_ni_label = _("unmarked as non-inheritable: ")
- def revs_link(revs, context):
- if revs:
- revs = to_ranges(revs)
- return _get_revs_link(revs.replace(',', u',\u200b'),
- context, spath, revs)
- modified_sources = []
- for spath, (new_revs, new_revs_ni) in new_sources.iteritems():
- if spath in old_sources:
- (old_revs, old_revs_ni), status = old_sources.pop(spath), None
- else:
- old_revs = old_revs_ni = set()
- status = _(' (added)')
- added = new_revs - old_revs
- removed = old_revs - new_revs
- added_ni = new_revs_ni - old_revs_ni
- removed_ni = old_revs_ni - new_revs_ni
- try:
- all_revs = set(repos._get_node_revs(spath))
- # TODO: also pass first_rev here, for getting smaller a set
- # (this is an optmization fix, result is already correct)
- added &= all_revs
- removed &= all_revs
- added_ni &= all_revs
- removed_ni &= all_revs
- except NoSuchNode:
- pass
- if added or removed:
- modified_sources.append((
- spath, [_get_source_link(spath, new_context), status],
- added and tag(added_label, revs_link(added, new_context)),
- removed and tag(removed_label,
- revs_link(removed, old_context)),
- added_ni and tag(added_ni_label,
- revs_link(added_ni, new_context)),
- removed_ni and tag(removed_ni_label,
- revs_link(removed_ni, old_context))
- ))
- # Go through remaining old sources, those were deleted
- removed_sources = []
- for spath, old_revs in old_sources.iteritems():
- removed_sources.append((spath,
- _get_source_link(spath, old_context)))
- if modified_sources or removed_sources:
- modified_sources.sort()
- removed_sources.sort()
- changes = tag.table(tag.tbody(
- [tag.tr(tag.td(c) for c in cols[1:])
- for cols in modified_sources],
- [tag.tr(tag.td(src), tag.td(_('removed'), colspan=4))
- for spath, src in removed_sources]), class_='props')
- else:
- changes = tag.em(_(' (with no actual effect on merging)'))
- return tag.li(tag_('Property %(prop)s changed', prop=tag.strong(name)),
- changes)
+# This module is a stub provided for backward compatibility. The svn_prop
+# module has been moved to tracopt.versioncontrol.svn. Please update your
+# code to use the new location.
Modified: incubator/bloodhound/vendor/trac/current/trac/versioncontrol/templates/admin_repositories.html
URL: http://svn.apache.org/viewvc/incubator/bloodhound/vendor/trac/current/trac/versioncontrol/templates/admin_repositories.html?rev=1398858&r1=1398857&r2=1398858&view=diff
==============================================================================
--- incubator/bloodhound/vendor/trac/current/trac/versioncontrol/templates/admin_repositories.html (original)
+++ incubator/bloodhound/vendor/trac/current/trac/versioncontrol/templates/admin_repositories.html Tue Oct 16 15:55:00 2012
@@ -10,12 +10,12 @@
<title>Repositories</title>
</head>
- <body>
+ <body py:with="sorted_repos = sorted(repositories.iteritems(), key=lambda item: item[0].lower())">
<h2>Manage Repositories</h2>
- <py:def function="type_field(editable, selected=None)">
+ <py:def function="type_field(editable, multiline=False, selected=None)">
<div class="field">
- <label>Type:<br/>
+ <label>Type:<br py:if="multiline"/>
<select size="1" id="trac-type" name="type" disabled="${not editable or None}">
<option py:for="type in types" value="$type" selected="${type == selected or None}">${type or _('(default)')}</option>
<option py:if="selected and selected not in types" selected="selected">$selected</option>
@@ -25,11 +25,11 @@
</div>
</py:def>
- <py:def function="alias_field(editable, selected=None)">
+ <py:def function="alias_field(editable, multiline=False, selected=None)">
<div class="field">
- <label>Repository:<br/>
+ <label>Repository:<br py:if="multiline"/>
<select size="1" id="trac-repository" name="alias" disabled="${not editable or None}">
- <option py:for="(reponame, info) in sorted(repositories.iteritems())" py:if="not info.alias"
+ <option py:for="(reponame, info) in sorted_repos" py:if="not info.alias"
value="$info.name" selected="${info.name == selected or None}">${info.name or _('(default)')}</option>
<option py:if="selected is not None and selected not in repositories" selected="selected">$selected</option>
</select>
@@ -51,10 +51,10 @@
</div>
<py:choose>
<py:when test="'alias' in info">
- ${alias_field(info.editable, info.alias)}
+ ${alias_field(info.editable, True, info.alias)}
</py:when>
<py:otherwise>
- ${type_field(info.editable, info.type)}
+ ${type_field(info.editable, True, info.type)}
<div class="field">
<label>Directory:<br/><input type="text" name="dir" size="48" value="$info.dir" readonly="$readonly"/></label>
</div>
@@ -69,7 +69,7 @@
</label>
</div>
<div class="field">
- <fieldset class="iefix">
+ <fieldset>
<label for="description" i18n:msg="">
Description (you may use <a tabindex="42" href="${href.wiki('WikiFormatting')}">WikiFormatting</a> here):
</label>
@@ -92,11 +92,11 @@ $info.description</textarea>
<fieldset>
<legend>Add Repository:</legend>
<div class="field">
- <label>Name:<br/><input type="text" name="name"/></label>
+ <label>Name: <input type="text" name="name"/></label>
</div>
${type_field(True)}
<div class="field">
- <label>Directory:<br/><input type="text" name="dir"/></label>
+ <label>Directory: <input type="text" name="dir"/></label>
</div>
<div class="buttons">
<input type="submit" name="add_repos" value="${_('Add')}"/>
@@ -109,7 +109,7 @@ $info.description</textarea>
<fieldset>
<legend>Add Alias:</legend>
<div class="field">
- <label>Name:<br/><input type="text" name="name"/></label>
+ <label>Name: <input type="text" name="name"/></label>
</div>
${alias_field(True)}
<div class="buttons">
@@ -126,7 +126,7 @@ $info.description</textarea>
</tr>
</thead>
<tbody>
- <tr py:for="(reponame, info) in sorted(repositories.iteritems())">
+ <tr py:for="(reponame, info) in sorted_repos">
<td class="sel"><input py:if="info.editable" type="checkbox" name="sel" value="$info.name"/></td>
<td class="name">
<a href="${panel_href(info.name or '(default)')}">${info.name or _('(default)')}</a>
Modified: incubator/bloodhound/vendor/trac/current/trac/versioncontrol/templates/browser.html
URL: http://svn.apache.org/viewvc/incubator/bloodhound/vendor/trac/current/trac/versioncontrol/templates/browser.html?rev=1398858&r1=1398857&r2=1398858&view=diff
==============================================================================
--- incubator/bloodhound/vendor/trac/current/trac/versioncontrol/templates/browser.html (original)
+++ incubator/bloodhound/vendor/trac/current/trac/versioncontrol/templates/browser.html Tue Oct 16 15:55:00 2012
@@ -207,11 +207,6 @@
<xi:include href="preview_file.html" py:with="preview = file.preview"/>
</div>
- <div id="help" i18n:msg="">
- <strong>Note:</strong> See <a href="${href.wiki('TracBrowser')}">TracBrowser</a>
- for help on using the repository browser.
- </div>
-
<div id="anydiff">
<form action="${href.diff()}" method="get">
<div class="buttons">
@@ -224,6 +219,11 @@
</form>
</div>
+ <div id="help" i18n:msg="">
+ <strong>Note:</strong> See <a href="${href.wiki('TracBrowser')}">TracBrowser</a>
+ for help on using the repository browser.
+ </div>
+
</div>
</body>
</html>
Modified: incubator/bloodhound/vendor/trac/current/trac/versioncontrol/tests/__init__.py
URL: http://svn.apache.org/viewvc/incubator/bloodhound/vendor/trac/current/trac/versioncontrol/tests/__init__.py?rev=1398858&r1=1398857&r2=1398858&view=diff
==============================================================================
--- incubator/bloodhound/vendor/trac/current/trac/versioncontrol/tests/__init__.py (original)
+++ incubator/bloodhound/vendor/trac/current/trac/versioncontrol/tests/__init__.py Tue Oct 16 15:55:00 2012
@@ -1,6 +1,6 @@
import unittest
-from trac.versioncontrol.tests import cache, diff, svn_authz, svn_fs, api
+from trac.versioncontrol.tests import cache, diff, svn_authz, api
from trac.versioncontrol.tests.functional import functionalSuite
def suite():
@@ -9,7 +9,6 @@ def suite():
suite.addTest(cache.suite())
suite.addTest(diff.suite())
suite.addTest(svn_authz.suite())
- suite.addTest(svn_fs.suite())
suite.addTest(api.suite())
return suite