##// END OF EJS Templates
Hacking for git support,and new faster repo scan
marcink -
r631:05528ad9 beta
parent child Browse files
Show More
@@ -3,6 +3,12 b''
3 3 Changelog
4 4 =========
5 5
6 1.1.0 (**XXXX-XX-XX**)
7 ----------------------
8 - git support
9 - performance upgrade for cached repos list
10
11
6 12 1.0.0 (**2010-10-xx**)
7 13 ----------------------
8 14
@@ -24,7 +24,7 b' versioning implementation: http://semver'
24 24 @author: marcink
25 25 """
26 26
27 VERSION = (1, 0, 0, 'rc4')
27 VERSION = (1, 1, 0, 'beta')
28 28
29 29 __version__ = '.'.join((str(each) for each in VERSION[:4]))
30 30
@@ -53,7 +53,7 b' def load_environment(global_conf, app_co'
53 53 if test:
54 54 from rhodecode.lib.utils import create_test_env, create_test_index
55 55 create_test_env('/tmp', config)
56 create_test_index('/tmp/*', True)
56 create_test_index('/tmp', True)
57 57
58 58 #MULTIPLE DB configs
59 59 # Setup the SQLAlchemy database engine
@@ -28,4 +28,4 b' class Globals(object):'
28 28 @LazyProperty
29 29 def base_path(self):
30 30 if self.baseui:
31 return self.paths[0][1].replace('*', '')
31 return self.paths[0][1]
@@ -8,7 +8,11 b' from rhodecode.lib.smtp_mailer import Sm'
8 8 from rhodecode.lib.utils import OrderedDict
9 9 from time import mktime
10 10 from vcs.backends.hg import MercurialRepository
11 from vcs.backends.git import GitRepository
12 import os
11 13 import traceback
14 from vcs.backends import get_repo
15 from vcs.utils.helpers import get_scm
12 16
13 17 try:
14 18 import json
@@ -95,8 +99,9 b' def get_commits_stats(repo_name, ts_min_'
95 99
96 100 commits_by_day_author_aggregate = {}
97 101 commits_by_day_aggregate = {}
98 repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
99 repo = MercurialRepository(repos_path + repo_name)
102 repos_path = get_hg_ui_settings()['paths_root_path']
103 p = os.path.join(repos_path, repo_name)
104 repo = get_repo(get_scm(p)[0], p)
100 105
101 106 skip_date_limit = True
102 107 parse_limit = 250 #limit for single task changeset parsing optimal for
@@ -305,8 +310,10 b' def __get_codes_stats(repo_name):'
305 310 's', 'sh', 'tpl', 'txt', 'vim', 'wss', 'xhtml', 'xml', 'xsl', 'xslt', 'yaws']
306 311
307 312
308 repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
309 repo = MercurialRepository(repos_path + repo_name)
313 repos_path = get_hg_ui_settings()['paths_root_path']
314 p = os.path.join(repos_path, repo_name)
315 repo = get_repo(get_scm(p)[0], p)
316
310 317 tip = repo.get_changeset()
311 318
312 319 code_stats = {}
@@ -162,7 +162,7 b' class DbManage(object):'
162 162 paths = RhodeCodeUi()
163 163 paths.ui_section = 'paths'
164 164 paths.ui_key = '/'
165 paths.ui_value = os.path.join(path, '*')
165 paths.ui_value = path
166 166
167 167
168 168 hgsettings1 = RhodeCodeSettings()
@@ -323,7 +323,7 b' flash = _Flash()'
323 323 from mercurial import util
324 324 from mercurial.templatefilters import age as _age, person as _person
325 325
326 age = lambda x:_age(x)
326 age = lambda x:x
327 327 capitalize = lambda x: x.capitalize()
328 328 date = lambda x: util.datestr(x)
329 329 email = util.email
@@ -333,8 +333,8 b' hgdate = lambda x: "%d %d" % x'
333 333 isodate = lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2')
334 334 isodatesec = lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2')
335 335 localdate = lambda x: (x[0], util.makedate()[1])
336 rfc822date = lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2")
337 rfc822date_notz = lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S")
336 rfc822date = lambda x: x#util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2")
337 rfc822date_notz = lambda x: x#util.datestr(x, "%a, %d %b %Y %H:%M:%S")
338 338 rfc3339date = lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2")
339 339 time_ago = lambda x: util.datestr(_age(x), "%a, %d %b %Y %H:%M:%S %1%2")
340 340
@@ -1,4 +1,10 b''
1 import os
2 import sys
1 3 from os.path import dirname as dn, join as jn
4
5 #to get the rhodecode import
6 sys.path.append(dn(dn(dn(os.path.realpath(__file__)))))
7
2 8 from rhodecode.config.environment import load_environment
3 9 from rhodecode.model.hg import HgModel
4 10 from shutil import rmtree
@@ -11,13 +17,8 b' from whoosh.index import create_in, open'
11 17 from whoosh.formats import Characters
12 18 from whoosh.highlight import highlight, SimpleFragmenter, HtmlFormatter
13 19
14 import os
15 import sys
16 20 import traceback
17 21
18 #to get the rhodecode import
19 sys.path.append(dn(dn(dn(os.path.realpath(__file__)))))
20
21 22
22 23 #LOCATION WE KEEP THE INDEX
23 24 IDX_LOCATION = jn(dn(dn(dn(dn(os.path.abspath(__file__))))), 'data', 'index')
@@ -48,6 +49,59 b" IDX_NAME = 'HG_INDEX'"
48 49 FORMATTER = HtmlFormatter('span', between='\n<span class="break">...</span>\n')
49 50 FRAGMENTER = SimpleFragmenter(200)
50 51
52 from paste.script import command
53 import ConfigParser
54
55 class MakeIndex(command.Command):
56
57 max_args = 1
58 min_args = 1
59
60 usage = "CONFIG_FILE"
61 summary = "Creates index for full text search given configuration file"
62 group_name = "Whoosh indexing"
63
64 parser = command.Command.standard_parser(verbose=True)
65 # parser.add_option('--repo-location',
66 # action='store',
67 # dest='repo_location',
68 # help="Specifies repositories location to index",
69 # )
70 parser.add_option('-f',
71 action='store_true',
72 dest='full_index',
73 help="Specifies that index should be made full i.e"
74 " destroy old and build from scratch",
75 default=False)
76 def command(self):
77 config_name = self.args[0]
78
79 p = config_name.split('/')
80 if len(p) == 1:
81 root = '.'
82 else:
83 root = '/'.join(p[:-1])
84 print root
85 config = ConfigParser.ConfigParser({'here':root})
86 config.read(config_name)
87 print dict(config.items('app:main'))['index_dir']
88 index_location = dict(config.items('app:main'))['index_dir']
89 #return
90
91 #=======================================================================
92 # WHOOSH DAEMON
93 #=======================================================================
94 from rhodecode.lib.pidlock import LockHeld, DaemonLock
95 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
96 try:
97 l = DaemonLock()
98 WhooshIndexingDaemon(index_location=index_location)\
99 .run(full_index=self.options.full_index)
100 l.release()
101 except LockHeld:
102 sys.exit(1)
103
104
51 105 class ResultWrapper(object):
52 106 def __init__(self, search_type, searcher, matcher, highlight_items):
53 107 self.search_type = search_type
@@ -115,8 +169,8 b' class ResultWrapper(object):'
115 169 Smart function that implements chunking the content
116 170 but not overlap chunks so it doesn't highlight the same
117 171 close occurrences twice.
118 :param matcher:
119 :param size:
172 @param matcher:
173 @param size:
120 174 """
121 175 memory = [(0, 0)]
122 176 for span in self.matcher.spans():
@@ -32,12 +32,12 b' from os.path import join as jn'
32 32 project_path = dn(dn(dn(dn(os.path.realpath(__file__)))))
33 33 sys.path.append(project_path)
34 34
35 from rhodecode.lib.pidlock import LockHeld, DaemonLock
35
36 36 from rhodecode.model.hg import HgModel
37 37 from rhodecode.lib.helpers import safe_unicode
38 38 from whoosh.index import create_in, open_dir
39 39 from shutil import rmtree
40 from rhodecode.lib.indexers import INDEX_EXTENSIONS, IDX_LOCATION, SCHEMA, IDX_NAME
40 from rhodecode.lib.indexers import INDEX_EXTENSIONS, SCHEMA, IDX_NAME
41 41
42 42 from time import mktime
43 43 from vcs.exceptions import ChangesetError, RepositoryError
@@ -61,21 +61,33 b' ch.setFormatter(formatter)'
61 61 # add ch to logger
62 62 log.addHandler(ch)
63 63
64 def scan_paths(root_location):
65 return HgModel.repo_scan('/', root_location, None, True)
64 def get_repos_location():
65 return HgModel.get_repos_location()
66
66 67
67 68 class WhooshIndexingDaemon(object):
68 69 """
69 70 Deamon for atomic jobs
70 71 """
71 72
72 def __init__(self, indexname='HG_INDEX', repo_location=None):
73 def __init__(self, indexname='HG_INDEX', index_location=None,
74 repo_location=None):
73 75 self.indexname = indexname
76
77 self.index_location = index_location
78 if not index_location:
79 raise Exception('You have to provide index location')
80
74 81 self.repo_location = repo_location
75 self.repo_paths = scan_paths(self.repo_location)
82 if not repo_location:
83 raise Exception('You have to provide repositories location')
84
85
86
87 self.repo_paths = HgModel.repo_scan('/', self.repo_location, None, True)
76 88 self.initial = False
77 if not os.path.isdir(IDX_LOCATION):
78 os.mkdir(IDX_LOCATION)
89 if not os.path.isdir(self.index_location):
90 os.mkdir(self.index_location)
79 91 log.info('Cannot run incremental index since it does not'
80 92 ' yet exist running full build')
81 93 self.initial = True
@@ -87,9 +99,7 b' class WhooshIndexingDaemon(object):'
87 99 """
88 100 index_paths_ = set()
89 101 try:
90 tip = repo.get_changeset()
91
92 for topnode, dirs, files in tip.walk('/'):
102 for topnode, dirs, files in repo.walk('/', 'tip'):
93 103 for f in files:
94 104 index_paths_.add(jn(repo.path, f.path))
95 105 for dir in dirs:
@@ -130,14 +140,14 b' class WhooshIndexingDaemon(object):'
130 140
131 141
132 142 def build_index(self):
133 if os.path.exists(IDX_LOCATION):
143 if os.path.exists(self.index_location):
134 144 log.debug('removing previous index')
135 rmtree(IDX_LOCATION)
145 rmtree(self.index_location)
136 146
137 if not os.path.exists(IDX_LOCATION):
138 os.mkdir(IDX_LOCATION)
147 if not os.path.exists(self.index_location):
148 os.mkdir(self.index_location)
139 149
140 idx = create_in(IDX_LOCATION, SCHEMA, indexname=IDX_NAME)
150 idx = create_in(self.index_location, SCHEMA, indexname=IDX_NAME)
141 151 writer = idx.writer()
142 152
143 153 for cnt, repo in enumerate(self.repo_paths.values()):
@@ -154,7 +164,7 b' class WhooshIndexingDaemon(object):'
154 164 def update_index(self):
155 165 log.debug('STARTING INCREMENTAL INDEXING UPDATE')
156 166
157 idx = open_dir(IDX_LOCATION, indexname=self.indexname)
167 idx = open_dir(self.index_location, indexname=self.indexname)
158 168 # The set of all paths in the index
159 169 indexed_paths = set()
160 170 # The set of all paths we need to re-index
@@ -209,40 +219,3 b' class WhooshIndexingDaemon(object):'
209 219 self.build_index()
210 220 else:
211 221 self.update_index()
212
213 if __name__ == "__main__":
214 arg = sys.argv[1:]
215 if len(arg) != 2:
216 sys.stderr.write('Please specify indexing type [full|incremental]'
217 'and path to repositories as script args \n')
218 sys.exit()
219
220
221 if arg[0] == 'full':
222 full_index = True
223 elif arg[0] == 'incremental':
224 # False means looking just for changes
225 full_index = False
226 else:
227 sys.stdout.write('Please use [full|incremental]'
228 ' as script first arg \n')
229 sys.exit()
230
231 if not os.path.isdir(arg[1]):
232 sys.stderr.write('%s is not a valid path \n' % arg[1])
233 sys.exit()
234 else:
235 if arg[1].endswith('/'):
236 repo_location = arg[1] + '*'
237 else:
238 repo_location = arg[1] + '/*'
239
240 try:
241 l = DaemonLock()
242 WhooshIndexingDaemon(repo_location=repo_location)\
243 .run(full_index=full_index)
244 l.release()
245 reload(logging)
246 except LockHeld:
247 sys.exit(1)
248
@@ -16,24 +16,28 b''
16 16 # along with this program; if not, write to the Free Software
17 17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 18 # MA 02110-1301, USA.
19 from UserDict import DictMixin
20 from mercurial import ui, config, hg
21 from mercurial.error import RepoError
22 from rhodecode.model import meta
23 from rhodecode.model.caching_query import FromCache
24 from rhodecode.model.db import Repository, User, RhodeCodeUi, RhodeCodeSettings, \
25 UserLog
26 from rhodecode.model.repo import RepoModel
27 from rhodecode.model.user import UserModel
28 from vcs.backends.base import BaseChangeset
29 from vcs.backends.git import GitRepository
30 from vcs.backends.hg import MercurialRepository
31 from vcs.utils.lazy import LazyProperty
32 import datetime
33 import logging
34 import os
19 35
20 36 """
21 37 Created on April 18, 2010
22 38 Utilities for RhodeCode
23 39 @author: marcink
24 40 """
25 from rhodecode.model.caching_query import FromCache
26 from mercurial import ui, config, hg
27 from mercurial.error import RepoError
28 from rhodecode.model import meta
29 from rhodecode.model.user import UserModel
30 from rhodecode.model.repo import RepoModel
31 from rhodecode.model.db import Repository, User, RhodeCodeUi, RhodeCodeSettings, UserLog
32 from vcs.backends.base import BaseChangeset
33 from vcs.utils.lazy import LazyProperty
34 import logging
35 import datetime
36 import os
37 41
38 42 log = logging.getLogger(__name__)
39 43
@@ -96,14 +100,30 b' def action_logger(user, action, repo, ip'
96 100 sa.rollback()
97 101 log.error('could not log user action:%s', str(e))
98 102
99 def check_repo_dir(paths):
100 repos_path = paths[0][1].split('/')
101 if repos_path[-1] in ['*', '**']:
102 repos_path = repos_path[:-1]
103 if repos_path[0] != '/':
104 repos_path[0] = '/'
105 if not os.path.isdir(os.path.join(*repos_path)):
106 raise Exception('Not a valid repository in %s' % paths[0][1])
103 def get_repos(path, recursive=False, initial=False):
104 """
105 Scans given path for repos and return (name,(type,path)) tuple
106 :param prefix:
107 :param path:
108 :param recursive:
109 :param initial:
110 """
111 from vcs.utils.helpers import get_scm
112 from vcs.exceptions import VCSError
113 scm = get_scm(path)
114 if scm:
115 raise Exception('The given path %s should not be a repository got %s',
116 path, scm)
117
118 for dirpath in os.listdir(path):
119 try:
120 yield dirpath, get_scm(os.path.join(path, dirpath))
121 except VCSError:
122 pass
123
124 if __name__ == '__main__':
125 get_repos('', '/home/marcink/workspace-python')
126
107 127
108 128 def check_repo_fast(repo_name, base_path):
109 129 if os.path.isdir(os.path.join(base_path, repo_name)):return False
@@ -231,8 +251,6 b" def make_ui(read_from='file', path=None,"
231 251 for k, v in cfg.items(section):
232 252 baseui.setconfig(section, k, v)
233 253 log.debug('settings ui from file[%s]%s:%s', section, k, v)
234 if checkpaths:check_repo_dir(cfg.items('paths'))
235
236 254
237 255 elif read_from == 'db':
238 256 hg_ui = get_hg_ui_cached()
@@ -284,7 +302,7 b' class EmptyChangeset(BaseChangeset):'
284 302 @LazyProperty
285 303 def raw_id(self):
286 304 """
287 Returns raw string identifing this changeset, useful for web
305 Returns raw string identifying this changeset, useful for web
288 306 representation.
289 307 """
290 308 return '0' * 40
@@ -308,16 +326,21 b' def repo2db_mapper(initial_repo_list, re'
308 326 """
309 327
310 328 sa = meta.Session()
329 rm = RepoModel(sa)
311 330 user = sa.query(User).filter(User.admin == True).first()
312 331
313 rm = RepoModel()
332 for name, repo in initial_repo_list.items():
333 if not rm.get(name, cache=False):
334 log.info('repository %s not found creating default', name)
314 335
315 for name, repo in initial_repo_list.items():
316 if not RepoModel(sa).get(name, cache=False):
317 log.info('repository %s not found creating default', name)
336 if isinstance(repo, MercurialRepository):
337 repo_type = 'hg'
338 if isinstance(repo, GitRepository):
339 repo_type = 'git'
318 340
319 341 form_data = {
320 342 'repo_name':name,
343 'repo_type':repo_type,
321 344 'description':repo.description if repo.description != 'unknown' else \
322 345 'auto description for %s' % name,
323 346 'private':False
@@ -335,7 +358,6 b' def repo2db_mapper(initial_repo_list, re'
335 358
336 359 meta.Session.remove()
337 360
338 from UserDict import DictMixin
339 361
340 362 class OrderedDict(dict, DictMixin):
341 363
@@ -81,6 +81,7 b' class Repository(Base):'
81 81 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
82 82 repo_id = Column("repo_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
83 83 repo_name = Column("repo_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
84 repo_type = Column("repo_type", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
84 85 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=False, default=None)
85 86 private = Column("private", BOOLEAN(), nullable=True, unique=None, default=None)
86 87 description = Column("description", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
@@ -194,16 +194,12 b' class ValidSettings(formencode.validator'
194 194
195 195 class ValidPath(formencode.validators.FancyValidator):
196 196 def to_python(self, value, state):
197 isdir = os.path.isdir(value.replace('*', ''))
198 if (value.endswith('/*') or value.endswith('/**')) and isdir:
199 return value
200 elif not isdir:
197
198 if not os.path.isdir(value):
201 199 msg = _('This is not a valid path')
202 else:
203 msg = _('You need to specify * or ** at the end of path (ie. /tmp/*)')
204
205 200 raise formencode.Invalid(msg, value, state,
206 201 error_dict={'paths_root_path':msg})
202 return value
207 203
208 204 def UniqSystemEmail(old_data):
209 205 class _UniqSystemEmail(formencode.validators.FancyValidator):
@@ -24,7 +24,6 b' Model for RhodeCode'
24 24 """
25 25 from beaker.cache import cache_region
26 26 from mercurial import ui
27 from mercurial.hgweb.hgwebdir_mod import findrepos
28 27 from rhodecode.lib import helpers as h
29 28 from rhodecode.lib.utils import invalidate_cache
30 29 from rhodecode.lib.auth import HasRepoPermissionAny
@@ -33,12 +32,12 b' from rhodecode.model.db import Repositor'
33 32 from sqlalchemy.orm import joinedload
34 33 from vcs.exceptions import RepositoryError, VCSError
35 34 import logging
36 import os
37 35 import sys
38 36 log = logging.getLogger(__name__)
39 37
40 38 try:
41 39 from vcs.backends.hg import MercurialRepository
40 from vcs.backends.git import GitRepository
42 41 except ImportError:
43 42 sys.stderr.write('You have to import vcs module')
44 43 raise Exception('Unable to import vcs')
@@ -47,7 +46,7 b' def _get_repos_cached_initial(app_global'
47 46 """return cached dict with repos
48 47 """
49 48 g = app_globals
50 return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui, initial)
49 return HgModel().repo_scan(g.paths[0][1], g.baseui, initial)
51 50
52 51 @cache_region('long_term', 'cached_repo_list')
53 52 def _get_repos_cached():
@@ -55,7 +54,7 b' def _get_repos_cached():'
55 54 """
56 55 log.info('getting all repositories list')
57 56 from pylons import app_globals as g
58 return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui)
57 return HgModel().repo_scan(g.paths[0][1], g.baseui)
59 58
60 59 @cache_region('super_short_term', 'cached_repos_switcher_list')
61 60 def _get_repos_switcher_cached(cached_repo_list):
@@ -73,42 +72,34 b' def _full_changelog_cached(repo_name):'
73 72 return list(reversed(list(HgModel().get_repo(repo_name))))
74 73
75 74 class HgModel(object):
76 """Mercurial Model
75 """
76 Mercurial Model
77 77 """
78 78
79 def __init__(self):
80 pass
79 def __init__(self, sa=None):
80 if not sa:
81 self.sa = meta.Session()
82 else:
83 self.sa = sa
81 84
82 @staticmethod
83 def repo_scan(repos_prefix, repos_path, baseui, initial=False):
85 def repo_scan(self, repos_path, baseui, initial=False):
84 86 """
85 87 Listing of repositories in given path. This path should not be a
86 88 repository itself. Return a dictionary of repository objects
87 :param repos_path: path to directory it could take syntax with
88 * or ** for deep recursive displaying repositories
89 """
90 sa = meta.Session()
91 def check_repo_dir(path):
92 """Checks the repository
93 :param path:
89
90 :param repos_path: path to directory containing repositories
91 :param baseui
92 :param initial: initial scann
94 93 """
95 repos_path = path.split('/')
96 if repos_path[-1] in ['*', '**']:
97 repos_path = repos_path[:-1]
98 if repos_path[0] != '/':
99 repos_path[0] = '/'
100 if not os.path.isdir(os.path.join(*repos_path)):
101 raise RepositoryError('Not a valid repository in %s' % path)
102 if not repos_path.endswith('*'):
103 raise VCSError('You need to specify * or ** at the end of path '
104 'for recursive scanning')
94 log.info('scanning for repositories in %s', repos_path)
105 95
106 check_repo_dir(repos_path)
107 log.info('scanning for repositories in %s', repos_path)
108 repos = findrepos([(repos_prefix, repos_path)])
109 96 if not isinstance(baseui, ui.ui):
110 97 baseui = ui.ui()
111 98
99 from rhodecode.lib.utils import get_repos
100 repos = get_repos(repos_path)
101
102
112 103 repos_list = {}
113 104 for name, path in repos:
114 105 try:
@@ -117,15 +108,19 b' class HgModel(object):'
117 108 raise RepositoryError('Duplicate repository name %s found in'
118 109 ' %s' % (name, path))
119 110 else:
111 if path[0] == 'hg':
112 repos_list[name] = MercurialRepository(path[1], baseui=baseui)
113 repos_list[name].name = name
120 114
121 repos_list[name] = MercurialRepository(path, baseui=baseui)
115 if path[0] == 'git':
116 repos_list[name] = GitRepository(path[1])
122 117 repos_list[name].name = name
123 118
124 119 dbrepo = None
125 120 if not initial:
126 121 #for initial scann on application first run we don't
127 122 #have db repos yet.
128 dbrepo = sa.query(Repository)\
123 dbrepo = self.sa.query(Repository)\
129 124 .options(joinedload(Repository.fork))\
130 125 .filter(Repository.repo_name == name)\
131 126 .scalar()
@@ -137,16 +132,17 b' class HgModel(object):'
137 132 if dbrepo.user:
138 133 repos_list[name].contact = dbrepo.user.full_contact
139 134 else:
140 repos_list[name].contact = sa.query(User)\
135 repos_list[name].contact = self.sa.query(User)\
141 136 .filter(User.admin == True).first().full_contact
142 137 except OSError:
143 138 continue
144 meta.Session.remove()
139
145 140 return repos_list
146 141
147 142 def get_repos(self):
148 143 for name, repo in _get_repos_cached().items():
149 if repo._get_hidden():
144
145 if isinstance(repo, MercurialRepository) and repo._get_hidden():
150 146 #skip hidden web repository
151 147 continue
152 148
@@ -13,7 +13,7 b''
13 13 </tr>
14 14 %for cnt,cs in enumerate(c.repo_changesets):
15 15 <tr class="parity${cnt%2}">
16 <td>${h.age(cs._ctx.date())} - ${h.rfc822date_notz(cs._ctx.date())} </td>
16 <td>${h.age(cs.date)} - ${h.rfc822date_notz(cs.date)} </td>
17 17 <td title="${cs.author}">${h.person(cs.author)}</td>
18 18 <td>r${cs.revision}:${cs.short_id}</td>
19 19 <td>
General Comments 0
You need to be logged in to leave comments. Login now