##// END OF EJS Templates
always update changeset cache after invalidation signal
marcink -
r3150:80abc924 beta
parent child Browse files
Show More
@@ -1,618 +1,621
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.scm
3 rhodecode.model.scm
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Scm model for RhodeCode
6 Scm model for RhodeCode
7
7
8 :created_on: Apr 9, 2010
8 :created_on: Apr 9, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from __future__ import with_statement
25 from __future__ import with_statement
26 import os
26 import os
27 import re
27 import re
28 import time
28 import time
29 import traceback
29 import traceback
30 import logging
30 import logging
31 import cStringIO
31 import cStringIO
32 import pkg_resources
32 import pkg_resources
33 from os.path import dirname as dn, join as jn
33 from os.path import dirname as dn, join as jn
34
34
35 from sqlalchemy import func
35 from sqlalchemy import func
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37
37
38 import rhodecode
38 import rhodecode
39 from rhodecode.lib.vcs import get_backend
39 from rhodecode.lib.vcs import get_backend
40 from rhodecode.lib.vcs.exceptions import RepositoryError
40 from rhodecode.lib.vcs.exceptions import RepositoryError
41 from rhodecode.lib.vcs.utils.lazy import LazyProperty
41 from rhodecode.lib.vcs.utils.lazy import LazyProperty
42 from rhodecode.lib.vcs.nodes import FileNode
42 from rhodecode.lib.vcs.nodes import FileNode
43 from rhodecode.lib.vcs.backends.base import EmptyChangeset
43 from rhodecode.lib.vcs.backends.base import EmptyChangeset
44
44
45 from rhodecode import BACKENDS
45 from rhodecode import BACKENDS
46 from rhodecode.lib import helpers as h
46 from rhodecode.lib import helpers as h
47 from rhodecode.lib.utils2 import safe_str, safe_unicode
47 from rhodecode.lib.utils2 import safe_str, safe_unicode
48 from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny
48 from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny
49 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
49 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
50 action_logger, REMOVED_REPO_PAT
50 action_logger, REMOVED_REPO_PAT
51 from rhodecode.model import BaseModel
51 from rhodecode.model import BaseModel
52 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
52 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
53 UserFollowing, UserLog, User, RepoGroup, PullRequest
53 UserFollowing, UserLog, User, RepoGroup, PullRequest
54
54
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57
57
58 class UserTemp(object):
58 class UserTemp(object):
59 def __init__(self, user_id):
59 def __init__(self, user_id):
60 self.user_id = user_id
60 self.user_id = user_id
61
61
62 def __repr__(self):
62 def __repr__(self):
63 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
63 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
64
64
65
65
66 class RepoTemp(object):
66 class RepoTemp(object):
67 def __init__(self, repo_id):
67 def __init__(self, repo_id):
68 self.repo_id = repo_id
68 self.repo_id = repo_id
69
69
70 def __repr__(self):
70 def __repr__(self):
71 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
71 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
72
72
73
73
74 class CachedRepoList(object):
74 class CachedRepoList(object):
75 """
75 """
76 Cached repo list, uses in-memory cache after initialization, that is
76 Cached repo list, uses in-memory cache after initialization, that is
77 super fast
77 super fast
78 """
78 """
79
79
80 def __init__(self, db_repo_list, repos_path, order_by=None):
80 def __init__(self, db_repo_list, repos_path, order_by=None):
81 self.db_repo_list = db_repo_list
81 self.db_repo_list = db_repo_list
82 self.repos_path = repos_path
82 self.repos_path = repos_path
83 self.order_by = order_by
83 self.order_by = order_by
84 self.reversed = (order_by or '').startswith('-')
84 self.reversed = (order_by or '').startswith('-')
85
85
86 def __len__(self):
86 def __len__(self):
87 return len(self.db_repo_list)
87 return len(self.db_repo_list)
88
88
89 def __repr__(self):
89 def __repr__(self):
90 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
90 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
91
91
92 def __iter__(self):
92 def __iter__(self):
93 # pre-propagated cache_map to save executing select statements
93 # pre-propagated cache_map to save executing select statements
94 # for each repo
94 # for each repo
95 cache_map = CacheInvalidation.get_cache_map()
95 cache_map = CacheInvalidation.get_cache_map()
96
96
97 for dbr in self.db_repo_list:
97 for dbr in self.db_repo_list:
98 scmr = dbr.scm_instance_cached(cache_map)
98 scmr = dbr.scm_instance_cached(cache_map)
99 # check permission at this level
99 # check permission at this level
100 if not HasRepoPermissionAny(
100 if not HasRepoPermissionAny(
101 'repository.read', 'repository.write', 'repository.admin'
101 'repository.read', 'repository.write', 'repository.admin'
102 )(dbr.repo_name, 'get repo check'):
102 )(dbr.repo_name, 'get repo check'):
103 continue
103 continue
104
104
105 if scmr is None:
105 if scmr is None:
106 log.error(
106 log.error(
107 '%s this repository is present in database but it '
107 '%s this repository is present in database but it '
108 'cannot be created as an scm instance' % dbr.repo_name
108 'cannot be created as an scm instance' % dbr.repo_name
109 )
109 )
110 continue
110 continue
111
111
112 last_change = scmr.last_change
112 last_change = scmr.last_change
113 tip = h.get_changeset_safe(scmr, 'tip')
113 tip = h.get_changeset_safe(scmr, 'tip')
114
114
115 tmp_d = {}
115 tmp_d = {}
116 tmp_d['name'] = dbr.repo_name
116 tmp_d['name'] = dbr.repo_name
117 tmp_d['name_sort'] = tmp_d['name'].lower()
117 tmp_d['name_sort'] = tmp_d['name'].lower()
118 tmp_d['raw_name'] = tmp_d['name'].lower()
118 tmp_d['raw_name'] = tmp_d['name'].lower()
119 tmp_d['description'] = dbr.description
119 tmp_d['description'] = dbr.description
120 tmp_d['description_sort'] = tmp_d['description'].lower()
120 tmp_d['description_sort'] = tmp_d['description'].lower()
121 tmp_d['last_change'] = last_change
121 tmp_d['last_change'] = last_change
122 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
122 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
123 tmp_d['tip'] = tip.raw_id
123 tmp_d['tip'] = tip.raw_id
124 tmp_d['tip_sort'] = tip.revision
124 tmp_d['tip_sort'] = tip.revision
125 tmp_d['rev'] = tip.revision
125 tmp_d['rev'] = tip.revision
126 tmp_d['contact'] = dbr.user.full_contact
126 tmp_d['contact'] = dbr.user.full_contact
127 tmp_d['contact_sort'] = tmp_d['contact']
127 tmp_d['contact_sort'] = tmp_d['contact']
128 tmp_d['owner_sort'] = tmp_d['contact']
128 tmp_d['owner_sort'] = tmp_d['contact']
129 tmp_d['repo_archives'] = list(scmr._get_archives())
129 tmp_d['repo_archives'] = list(scmr._get_archives())
130 tmp_d['last_msg'] = tip.message
130 tmp_d['last_msg'] = tip.message
131 tmp_d['author'] = tip.author
131 tmp_d['author'] = tip.author
132 tmp_d['dbrepo'] = dbr.get_dict()
132 tmp_d['dbrepo'] = dbr.get_dict()
133 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
133 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
134 yield tmp_d
134 yield tmp_d
135
135
136
136
137 class SimpleCachedRepoList(CachedRepoList):
137 class SimpleCachedRepoList(CachedRepoList):
138 """
138 """
139 Lighter version of CachedRepoList without the scm initialisation
139 Lighter version of CachedRepoList without the scm initialisation
140 """
140 """
141
141
142 def __iter__(self):
142 def __iter__(self):
143 for dbr in self.db_repo_list:
143 for dbr in self.db_repo_list:
144 # check permission at this level
144 # check permission at this level
145 if not HasRepoPermissionAny(
145 if not HasRepoPermissionAny(
146 'repository.read', 'repository.write', 'repository.admin'
146 'repository.read', 'repository.write', 'repository.admin'
147 )(dbr.repo_name, 'get repo check'):
147 )(dbr.repo_name, 'get repo check'):
148 continue
148 continue
149
149
150 tmp_d = {}
150 tmp_d = {}
151 tmp_d['name'] = dbr.repo_name
151 tmp_d['name'] = dbr.repo_name
152 tmp_d['name_sort'] = tmp_d['name'].lower()
152 tmp_d['name_sort'] = tmp_d['name'].lower()
153 tmp_d['raw_name'] = tmp_d['name'].lower()
153 tmp_d['raw_name'] = tmp_d['name'].lower()
154 tmp_d['description'] = dbr.description
154 tmp_d['description'] = dbr.description
155 tmp_d['description_sort'] = tmp_d['description'].lower()
155 tmp_d['description_sort'] = tmp_d['description'].lower()
156 tmp_d['dbrepo'] = dbr.get_dict()
156 tmp_d['dbrepo'] = dbr.get_dict()
157 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
157 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
158 yield tmp_d
158 yield tmp_d
159
159
160
160
161 class GroupList(object):
161 class GroupList(object):
162
162
163 def __init__(self, db_repo_group_list):
163 def __init__(self, db_repo_group_list):
164 self.db_repo_group_list = db_repo_group_list
164 self.db_repo_group_list = db_repo_group_list
165
165
166 def __len__(self):
166 def __len__(self):
167 return len(self.db_repo_group_list)
167 return len(self.db_repo_group_list)
168
168
169 def __repr__(self):
169 def __repr__(self):
170 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
170 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
171
171
172 def __iter__(self):
172 def __iter__(self):
173 for dbgr in self.db_repo_group_list:
173 for dbgr in self.db_repo_group_list:
174 # check permission at this level
174 # check permission at this level
175 if not HasReposGroupPermissionAny(
175 if not HasReposGroupPermissionAny(
176 'group.read', 'group.write', 'group.admin'
176 'group.read', 'group.write', 'group.admin'
177 )(dbgr.group_name, 'get group repo check'):
177 )(dbgr.group_name, 'get group repo check'):
178 continue
178 continue
179
179
180 yield dbgr
180 yield dbgr
181
181
182
182
183 class ScmModel(BaseModel):
183 class ScmModel(BaseModel):
184 """
184 """
185 Generic Scm Model
185 Generic Scm Model
186 """
186 """
187
187
188 def __get_repo(self, instance):
188 def __get_repo(self, instance):
189 cls = Repository
189 cls = Repository
190 if isinstance(instance, cls):
190 if isinstance(instance, cls):
191 return instance
191 return instance
192 elif isinstance(instance, int) or safe_str(instance).isdigit():
192 elif isinstance(instance, int) or safe_str(instance).isdigit():
193 return cls.get(instance)
193 return cls.get(instance)
194 elif isinstance(instance, basestring):
194 elif isinstance(instance, basestring):
195 return cls.get_by_repo_name(instance)
195 return cls.get_by_repo_name(instance)
196 elif instance:
196 elif instance:
197 raise Exception('given object must be int, basestr or Instance'
197 raise Exception('given object must be int, basestr or Instance'
198 ' of %s got %s' % (type(cls), type(instance)))
198 ' of %s got %s' % (type(cls), type(instance)))
199
199
200 @LazyProperty
200 @LazyProperty
201 def repos_path(self):
201 def repos_path(self):
202 """
202 """
203 Get's the repositories root path from database
203 Get's the repositories root path from database
204 """
204 """
205
205
206 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
206 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
207
207
208 return q.ui_value
208 return q.ui_value
209
209
210 def repo_scan(self, repos_path=None):
210 def repo_scan(self, repos_path=None):
211 """
211 """
212 Listing of repositories in given path. This path should not be a
212 Listing of repositories in given path. This path should not be a
213 repository itself. Return a dictionary of repository objects
213 repository itself. Return a dictionary of repository objects
214
214
215 :param repos_path: path to directory containing repositories
215 :param repos_path: path to directory containing repositories
216 """
216 """
217
217
218 if repos_path is None:
218 if repos_path is None:
219 repos_path = self.repos_path
219 repos_path = self.repos_path
220
220
221 log.info('scanning for repositories in %s' % repos_path)
221 log.info('scanning for repositories in %s' % repos_path)
222
222
223 baseui = make_ui('db')
223 baseui = make_ui('db')
224 repos = {}
224 repos = {}
225
225
226 for name, path in get_filesystem_repos(repos_path, recursive=True):
226 for name, path in get_filesystem_repos(repos_path, recursive=True):
227 # skip removed repos
227 # skip removed repos
228 if REMOVED_REPO_PAT.match(name) or path[0] is None:
228 if REMOVED_REPO_PAT.match(name) or path[0] is None:
229 continue
229 continue
230
230
231 # name need to be decomposed and put back together using the /
231 # name need to be decomposed and put back together using the /
232 # since this is internal storage separator for rhodecode
232 # since this is internal storage separator for rhodecode
233 name = Repository.url_sep().join(name.split(os.sep))
233 name = Repository.url_sep().join(name.split(os.sep))
234
234
235 try:
235 try:
236 if name in repos:
236 if name in repos:
237 raise RepositoryError('Duplicate repository name %s '
237 raise RepositoryError('Duplicate repository name %s '
238 'found in %s' % (name, path))
238 'found in %s' % (name, path))
239 else:
239 else:
240
240
241 klass = get_backend(path[0])
241 klass = get_backend(path[0])
242
242
243 if path[0] == 'hg' and path[0] in BACKENDS.keys():
243 if path[0] == 'hg' and path[0] in BACKENDS.keys():
244 repos[name] = klass(safe_str(path[1]), baseui=baseui)
244 repos[name] = klass(safe_str(path[1]), baseui=baseui)
245
245
246 if path[0] == 'git' and path[0] in BACKENDS.keys():
246 if path[0] == 'git' and path[0] in BACKENDS.keys():
247 repos[name] = klass(path[1])
247 repos[name] = klass(path[1])
248 except OSError:
248 except OSError:
249 continue
249 continue
250
250
251 return repos
251 return repos
252
252
253 def get_repos(self, all_repos=None, sort_key=None, simple=False):
253 def get_repos(self, all_repos=None, sort_key=None, simple=False):
254 """
254 """
255 Get all repos from db and for each repo create it's
255 Get all repos from db and for each repo create it's
256 backend instance and fill that backed with information from database
256 backend instance and fill that backed with information from database
257
257
258 :param all_repos: list of repository names as strings
258 :param all_repos: list of repository names as strings
259 give specific repositories list, good for filtering
259 give specific repositories list, good for filtering
260
260
261 :param sort_key: initial sorting of repos
261 :param sort_key: initial sorting of repos
262 :param simple: use SimpleCachedList - one without the SCM info
262 :param simple: use SimpleCachedList - one without the SCM info
263 """
263 """
264 if all_repos is None:
264 if all_repos is None:
265 all_repos = self.sa.query(Repository)\
265 all_repos = self.sa.query(Repository)\
266 .filter(Repository.group_id == None)\
266 .filter(Repository.group_id == None)\
267 .order_by(func.lower(Repository.repo_name)).all()
267 .order_by(func.lower(Repository.repo_name)).all()
268 if simple:
268 if simple:
269 repo_iter = SimpleCachedRepoList(all_repos,
269 repo_iter = SimpleCachedRepoList(all_repos,
270 repos_path=self.repos_path,
270 repos_path=self.repos_path,
271 order_by=sort_key)
271 order_by=sort_key)
272 else:
272 else:
273 repo_iter = CachedRepoList(all_repos,
273 repo_iter = CachedRepoList(all_repos,
274 repos_path=self.repos_path,
274 repos_path=self.repos_path,
275 order_by=sort_key)
275 order_by=sort_key)
276
276
277 return repo_iter
277 return repo_iter
278
278
279 def get_repos_groups(self, all_groups=None):
279 def get_repos_groups(self, all_groups=None):
280 if all_groups is None:
280 if all_groups is None:
281 all_groups = RepoGroup.query()\
281 all_groups = RepoGroup.query()\
282 .filter(RepoGroup.group_parent_id == None).all()
282 .filter(RepoGroup.group_parent_id == None).all()
283 group_iter = GroupList(all_groups)
283 group_iter = GroupList(all_groups)
284
284
285 return group_iter
285 return group_iter
286
286
287 def mark_for_invalidation(self, repo_name):
287 def mark_for_invalidation(self, repo_name):
288 """
288 """
289 Puts cache invalidation task into db for
289 Puts cache invalidation task into db for
290 further global cache invalidation
290 further global cache invalidation
291
291
292 :param repo_name: this repo that should invalidation take place
292 :param repo_name: this repo that should invalidation take place
293 """
293 """
294 CacheInvalidation.set_invalidate(repo_name=repo_name)
294 CacheInvalidation.set_invalidate(repo_name=repo_name)
295 repo = Repository.get_by_repo_name(repo_name)
296 if repo:
297 repo.update_changeset_cache()
295
298
296 def toggle_following_repo(self, follow_repo_id, user_id):
299 def toggle_following_repo(self, follow_repo_id, user_id):
297
300
298 f = self.sa.query(UserFollowing)\
301 f = self.sa.query(UserFollowing)\
299 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
302 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
300 .filter(UserFollowing.user_id == user_id).scalar()
303 .filter(UserFollowing.user_id == user_id).scalar()
301
304
302 if f is not None:
305 if f is not None:
303 try:
306 try:
304 self.sa.delete(f)
307 self.sa.delete(f)
305 action_logger(UserTemp(user_id),
308 action_logger(UserTemp(user_id),
306 'stopped_following_repo',
309 'stopped_following_repo',
307 RepoTemp(follow_repo_id))
310 RepoTemp(follow_repo_id))
308 return
311 return
309 except:
312 except:
310 log.error(traceback.format_exc())
313 log.error(traceback.format_exc())
311 raise
314 raise
312
315
313 try:
316 try:
314 f = UserFollowing()
317 f = UserFollowing()
315 f.user_id = user_id
318 f.user_id = user_id
316 f.follows_repo_id = follow_repo_id
319 f.follows_repo_id = follow_repo_id
317 self.sa.add(f)
320 self.sa.add(f)
318
321
319 action_logger(UserTemp(user_id),
322 action_logger(UserTemp(user_id),
320 'started_following_repo',
323 'started_following_repo',
321 RepoTemp(follow_repo_id))
324 RepoTemp(follow_repo_id))
322 except:
325 except:
323 log.error(traceback.format_exc())
326 log.error(traceback.format_exc())
324 raise
327 raise
325
328
326 def toggle_following_user(self, follow_user_id, user_id):
329 def toggle_following_user(self, follow_user_id, user_id):
327 f = self.sa.query(UserFollowing)\
330 f = self.sa.query(UserFollowing)\
328 .filter(UserFollowing.follows_user_id == follow_user_id)\
331 .filter(UserFollowing.follows_user_id == follow_user_id)\
329 .filter(UserFollowing.user_id == user_id).scalar()
332 .filter(UserFollowing.user_id == user_id).scalar()
330
333
331 if f is not None:
334 if f is not None:
332 try:
335 try:
333 self.sa.delete(f)
336 self.sa.delete(f)
334 return
337 return
335 except:
338 except:
336 log.error(traceback.format_exc())
339 log.error(traceback.format_exc())
337 raise
340 raise
338
341
339 try:
342 try:
340 f = UserFollowing()
343 f = UserFollowing()
341 f.user_id = user_id
344 f.user_id = user_id
342 f.follows_user_id = follow_user_id
345 f.follows_user_id = follow_user_id
343 self.sa.add(f)
346 self.sa.add(f)
344 except:
347 except:
345 log.error(traceback.format_exc())
348 log.error(traceback.format_exc())
346 raise
349 raise
347
350
348 def is_following_repo(self, repo_name, user_id, cache=False):
351 def is_following_repo(self, repo_name, user_id, cache=False):
349 r = self.sa.query(Repository)\
352 r = self.sa.query(Repository)\
350 .filter(Repository.repo_name == repo_name).scalar()
353 .filter(Repository.repo_name == repo_name).scalar()
351
354
352 f = self.sa.query(UserFollowing)\
355 f = self.sa.query(UserFollowing)\
353 .filter(UserFollowing.follows_repository == r)\
356 .filter(UserFollowing.follows_repository == r)\
354 .filter(UserFollowing.user_id == user_id).scalar()
357 .filter(UserFollowing.user_id == user_id).scalar()
355
358
356 return f is not None
359 return f is not None
357
360
358 def is_following_user(self, username, user_id, cache=False):
361 def is_following_user(self, username, user_id, cache=False):
359 u = User.get_by_username(username)
362 u = User.get_by_username(username)
360
363
361 f = self.sa.query(UserFollowing)\
364 f = self.sa.query(UserFollowing)\
362 .filter(UserFollowing.follows_user == u)\
365 .filter(UserFollowing.follows_user == u)\
363 .filter(UserFollowing.user_id == user_id).scalar()
366 .filter(UserFollowing.user_id == user_id).scalar()
364
367
365 return f is not None
368 return f is not None
366
369
367 def get_followers(self, repo):
370 def get_followers(self, repo):
368 repo = self._get_repo(repo)
371 repo = self._get_repo(repo)
369
372
370 return self.sa.query(UserFollowing)\
373 return self.sa.query(UserFollowing)\
371 .filter(UserFollowing.follows_repository == repo).count()
374 .filter(UserFollowing.follows_repository == repo).count()
372
375
373 def get_forks(self, repo):
376 def get_forks(self, repo):
374 repo = self._get_repo(repo)
377 repo = self._get_repo(repo)
375 return self.sa.query(Repository)\
378 return self.sa.query(Repository)\
376 .filter(Repository.fork == repo).count()
379 .filter(Repository.fork == repo).count()
377
380
378 def get_pull_requests(self, repo):
381 def get_pull_requests(self, repo):
379 repo = self._get_repo(repo)
382 repo = self._get_repo(repo)
380 return self.sa.query(PullRequest)\
383 return self.sa.query(PullRequest)\
381 .filter(PullRequest.other_repo == repo).count()
384 .filter(PullRequest.other_repo == repo).count()
382
385
383 def mark_as_fork(self, repo, fork, user):
386 def mark_as_fork(self, repo, fork, user):
384 repo = self.__get_repo(repo)
387 repo = self.__get_repo(repo)
385 fork = self.__get_repo(fork)
388 fork = self.__get_repo(fork)
386 if fork and repo.repo_id == fork.repo_id:
389 if fork and repo.repo_id == fork.repo_id:
387 raise Exception("Cannot set repository as fork of itself")
390 raise Exception("Cannot set repository as fork of itself")
388 repo.fork = fork
391 repo.fork = fork
389 self.sa.add(repo)
392 self.sa.add(repo)
390 return repo
393 return repo
391
394
392 def pull_changes(self, repo, username):
395 def pull_changes(self, repo, username):
393 dbrepo = self.__get_repo(repo)
396 dbrepo = self.__get_repo(repo)
394 clone_uri = dbrepo.clone_uri
397 clone_uri = dbrepo.clone_uri
395 if not clone_uri:
398 if not clone_uri:
396 raise Exception("This repository doesn't have a clone uri")
399 raise Exception("This repository doesn't have a clone uri")
397
400
398 repo = dbrepo.scm_instance
401 repo = dbrepo.scm_instance
399 from rhodecode import CONFIG
402 from rhodecode import CONFIG
400 try:
403 try:
401 extras = {
404 extras = {
402 'ip': '',
405 'ip': '',
403 'username': username,
406 'username': username,
404 'action': 'push_remote',
407 'action': 'push_remote',
405 'repository': dbrepo.repo_name,
408 'repository': dbrepo.repo_name,
406 'scm': repo.alias,
409 'scm': repo.alias,
407 'config': CONFIG['__file__'],
410 'config': CONFIG['__file__'],
408 'make_lock': None,
411 'make_lock': None,
409 'locked_by': [None, None]
412 'locked_by': [None, None]
410 }
413 }
411
414
412 Repository.inject_ui(repo, extras=extras)
415 Repository.inject_ui(repo, extras=extras)
413
416
414 if repo.alias == 'git':
417 if repo.alias == 'git':
415 repo.fetch(clone_uri)
418 repo.fetch(clone_uri)
416 else:
419 else:
417 repo.pull(clone_uri)
420 repo.pull(clone_uri)
418 self.mark_for_invalidation(dbrepo.repo_name)
421 self.mark_for_invalidation(dbrepo.repo_name)
419 except:
422 except:
420 log.error(traceback.format_exc())
423 log.error(traceback.format_exc())
421 raise
424 raise
422
425
423 def commit_change(self, repo, repo_name, cs, user, author, message,
426 def commit_change(self, repo, repo_name, cs, user, author, message,
424 content, f_path):
427 content, f_path):
425 """
428 """
426 Commits changes
429 Commits changes
427
430
428 :param repo: SCM instance
431 :param repo: SCM instance
429
432
430 """
433 """
431
434
432 if repo.alias == 'hg':
435 if repo.alias == 'hg':
433 from rhodecode.lib.vcs.backends.hg import \
436 from rhodecode.lib.vcs.backends.hg import \
434 MercurialInMemoryChangeset as IMC
437 MercurialInMemoryChangeset as IMC
435 elif repo.alias == 'git':
438 elif repo.alias == 'git':
436 from rhodecode.lib.vcs.backends.git import \
439 from rhodecode.lib.vcs.backends.git import \
437 GitInMemoryChangeset as IMC
440 GitInMemoryChangeset as IMC
438
441
439 # decoding here will force that we have proper encoded values
442 # decoding here will force that we have proper encoded values
440 # in any other case this will throw exceptions and deny commit
443 # in any other case this will throw exceptions and deny commit
441 content = safe_str(content)
444 content = safe_str(content)
442 path = safe_str(f_path)
445 path = safe_str(f_path)
443 # message and author needs to be unicode
446 # message and author needs to be unicode
444 # proper backend should then translate that into required type
447 # proper backend should then translate that into required type
445 message = safe_unicode(message)
448 message = safe_unicode(message)
446 author = safe_unicode(author)
449 author = safe_unicode(author)
447 m = IMC(repo)
450 m = IMC(repo)
448 m.change(FileNode(path, content))
451 m.change(FileNode(path, content))
449 tip = m.commit(message=message,
452 tip = m.commit(message=message,
450 author=author,
453 author=author,
451 parents=[cs], branch=cs.branch)
454 parents=[cs], branch=cs.branch)
452
455
453 action = 'push_local:%s' % tip.raw_id
456 action = 'push_local:%s' % tip.raw_id
454 action_logger(user, action, repo_name)
457 action_logger(user, action, repo_name)
455 self.mark_for_invalidation(repo_name)
458 self.mark_for_invalidation(repo_name)
456 return tip
459 return tip
457
460
458 def create_node(self, repo, repo_name, cs, user, author, message, content,
461 def create_node(self, repo, repo_name, cs, user, author, message, content,
459 f_path):
462 f_path):
460 if repo.alias == 'hg':
463 if repo.alias == 'hg':
461 from rhodecode.lib.vcs.backends.hg import MercurialInMemoryChangeset as IMC
464 from rhodecode.lib.vcs.backends.hg import MercurialInMemoryChangeset as IMC
462 elif repo.alias == 'git':
465 elif repo.alias == 'git':
463 from rhodecode.lib.vcs.backends.git import GitInMemoryChangeset as IMC
466 from rhodecode.lib.vcs.backends.git import GitInMemoryChangeset as IMC
464 # decoding here will force that we have proper encoded values
467 # decoding here will force that we have proper encoded values
465 # in any other case this will throw exceptions and deny commit
468 # in any other case this will throw exceptions and deny commit
466
469
467 if isinstance(content, (basestring,)):
470 if isinstance(content, (basestring,)):
468 content = safe_str(content)
471 content = safe_str(content)
469 elif isinstance(content, (file, cStringIO.OutputType,)):
472 elif isinstance(content, (file, cStringIO.OutputType,)):
470 content = content.read()
473 content = content.read()
471 else:
474 else:
472 raise Exception('Content is of unrecognized type %s' % (
475 raise Exception('Content is of unrecognized type %s' % (
473 type(content)
476 type(content)
474 ))
477 ))
475
478
476 message = safe_unicode(message)
479 message = safe_unicode(message)
477 author = safe_unicode(author)
480 author = safe_unicode(author)
478 path = safe_str(f_path)
481 path = safe_str(f_path)
479 m = IMC(repo)
482 m = IMC(repo)
480
483
481 if isinstance(cs, EmptyChangeset):
484 if isinstance(cs, EmptyChangeset):
482 # EmptyChangeset means we we're editing empty repository
485 # EmptyChangeset means we we're editing empty repository
483 parents = None
486 parents = None
484 else:
487 else:
485 parents = [cs]
488 parents = [cs]
486
489
487 m.add(FileNode(path, content=content))
490 m.add(FileNode(path, content=content))
488 tip = m.commit(message=message,
491 tip = m.commit(message=message,
489 author=author,
492 author=author,
490 parents=parents, branch=cs.branch)
493 parents=parents, branch=cs.branch)
491
494
492 action = 'push_local:%s' % tip.raw_id
495 action = 'push_local:%s' % tip.raw_id
493 action_logger(user, action, repo_name)
496 action_logger(user, action, repo_name)
494 self.mark_for_invalidation(repo_name)
497 self.mark_for_invalidation(repo_name)
495 return tip
498 return tip
496
499
497 def get_nodes(self, repo_name, revision, root_path='/', flat=True):
500 def get_nodes(self, repo_name, revision, root_path='/', flat=True):
498 """
501 """
499 recursive walk in root dir and return a set of all path in that dir
502 recursive walk in root dir and return a set of all path in that dir
500 based on repository walk function
503 based on repository walk function
501
504
502 :param repo_name: name of repository
505 :param repo_name: name of repository
503 :param revision: revision for which to list nodes
506 :param revision: revision for which to list nodes
504 :param root_path: root path to list
507 :param root_path: root path to list
505 :param flat: return as a list, if False returns a dict with decription
508 :param flat: return as a list, if False returns a dict with decription
506
509
507 """
510 """
508 _files = list()
511 _files = list()
509 _dirs = list()
512 _dirs = list()
510 try:
513 try:
511 _repo = self.__get_repo(repo_name)
514 _repo = self.__get_repo(repo_name)
512 changeset = _repo.scm_instance.get_changeset(revision)
515 changeset = _repo.scm_instance.get_changeset(revision)
513 root_path = root_path.lstrip('/')
516 root_path = root_path.lstrip('/')
514 for topnode, dirs, files in changeset.walk(root_path):
517 for topnode, dirs, files in changeset.walk(root_path):
515 for f in files:
518 for f in files:
516 _files.append(f.path if flat else {"name": f.path,
519 _files.append(f.path if flat else {"name": f.path,
517 "type": "file"})
520 "type": "file"})
518 for d in dirs:
521 for d in dirs:
519 _dirs.append(d.path if flat else {"name": d.path,
522 _dirs.append(d.path if flat else {"name": d.path,
520 "type": "dir"})
523 "type": "dir"})
521 except RepositoryError:
524 except RepositoryError:
522 log.debug(traceback.format_exc())
525 log.debug(traceback.format_exc())
523 raise
526 raise
524
527
525 return _dirs, _files
528 return _dirs, _files
526
529
527 def get_unread_journal(self):
530 def get_unread_journal(self):
528 return self.sa.query(UserLog).count()
531 return self.sa.query(UserLog).count()
529
532
530 def get_repo_landing_revs(self, repo=None):
533 def get_repo_landing_revs(self, repo=None):
531 """
534 """
532 Generates select option with tags branches and bookmarks (for hg only)
535 Generates select option with tags branches and bookmarks (for hg only)
533 grouped by type
536 grouped by type
534
537
535 :param repo:
538 :param repo:
536 :type repo:
539 :type repo:
537 """
540 """
538
541
539 hist_l = []
542 hist_l = []
540 choices = []
543 choices = []
541 repo = self.__get_repo(repo)
544 repo = self.__get_repo(repo)
542 hist_l.append(['tip', _('latest tip')])
545 hist_l.append(['tip', _('latest tip')])
543 choices.append('tip')
546 choices.append('tip')
544 if not repo:
547 if not repo:
545 return choices, hist_l
548 return choices, hist_l
546
549
547 repo = repo.scm_instance
550 repo = repo.scm_instance
548
551
549 branches_group = ([(k, k) for k, v in
552 branches_group = ([(k, k) for k, v in
550 repo.branches.iteritems()], _("Branches"))
553 repo.branches.iteritems()], _("Branches"))
551 hist_l.append(branches_group)
554 hist_l.append(branches_group)
552 choices.extend([x[0] for x in branches_group[0]])
555 choices.extend([x[0] for x in branches_group[0]])
553
556
554 if repo.alias == 'hg':
557 if repo.alias == 'hg':
555 bookmarks_group = ([(k, k) for k, v in
558 bookmarks_group = ([(k, k) for k, v in
556 repo.bookmarks.iteritems()], _("Bookmarks"))
559 repo.bookmarks.iteritems()], _("Bookmarks"))
557 hist_l.append(bookmarks_group)
560 hist_l.append(bookmarks_group)
558 choices.extend([x[0] for x in bookmarks_group[0]])
561 choices.extend([x[0] for x in bookmarks_group[0]])
559
562
560 tags_group = ([(k, k) for k, v in
563 tags_group = ([(k, k) for k, v in
561 repo.tags.iteritems()], _("Tags"))
564 repo.tags.iteritems()], _("Tags"))
562 hist_l.append(tags_group)
565 hist_l.append(tags_group)
563 choices.extend([x[0] for x in tags_group[0]])
566 choices.extend([x[0] for x in tags_group[0]])
564
567
565 return choices, hist_l
568 return choices, hist_l
566
569
567 def install_git_hook(self, repo, force_create=False):
570 def install_git_hook(self, repo, force_create=False):
568 """
571 """
569 Creates a rhodecode hook inside a git repository
572 Creates a rhodecode hook inside a git repository
570
573
571 :param repo: Instance of VCS repo
574 :param repo: Instance of VCS repo
572 :param force_create: Create even if same name hook exists
575 :param force_create: Create even if same name hook exists
573 """
576 """
574
577
575 loc = jn(repo.path, 'hooks')
578 loc = jn(repo.path, 'hooks')
576 if not repo.bare:
579 if not repo.bare:
577 loc = jn(repo.path, '.git', 'hooks')
580 loc = jn(repo.path, '.git', 'hooks')
578 if not os.path.isdir(loc):
581 if not os.path.isdir(loc):
579 os.makedirs(loc)
582 os.makedirs(loc)
580
583
581 tmpl_post = pkg_resources.resource_string(
584 tmpl_post = pkg_resources.resource_string(
582 'rhodecode', jn('config', 'post_receive_tmpl.py')
585 'rhodecode', jn('config', 'post_receive_tmpl.py')
583 )
586 )
584 tmpl_pre = pkg_resources.resource_string(
587 tmpl_pre = pkg_resources.resource_string(
585 'rhodecode', jn('config', 'pre_receive_tmpl.py')
588 'rhodecode', jn('config', 'pre_receive_tmpl.py')
586 )
589 )
587
590
588 for h_type, tmpl in [('pre', tmpl_pre), ('post', tmpl_post)]:
591 for h_type, tmpl in [('pre', tmpl_pre), ('post', tmpl_post)]:
589 _hook_file = jn(loc, '%s-receive' % h_type)
592 _hook_file = jn(loc, '%s-receive' % h_type)
590 _rhodecode_hook = False
593 _rhodecode_hook = False
591 log.debug('Installing git hook in repo %s' % repo)
594 log.debug('Installing git hook in repo %s' % repo)
592 if os.path.exists(_hook_file):
595 if os.path.exists(_hook_file):
593 # let's take a look at this hook, maybe it's rhodecode ?
596 # let's take a look at this hook, maybe it's rhodecode ?
594 log.debug('hook exists, checking if it is from rhodecode')
597 log.debug('hook exists, checking if it is from rhodecode')
595 _HOOK_VER_PAT = re.compile(r'^RC_HOOK_VER')
598 _HOOK_VER_PAT = re.compile(r'^RC_HOOK_VER')
596 with open(_hook_file, 'rb') as f:
599 with open(_hook_file, 'rb') as f:
597 data = f.read()
600 data = f.read()
598 matches = re.compile(r'(?:%s)\s*=\s*(.*)'
601 matches = re.compile(r'(?:%s)\s*=\s*(.*)'
599 % 'RC_HOOK_VER').search(data)
602 % 'RC_HOOK_VER').search(data)
600 if matches:
603 if matches:
601 try:
604 try:
602 ver = matches.groups()[0]
605 ver = matches.groups()[0]
603 log.debug('got %s it is rhodecode' % (ver))
606 log.debug('got %s it is rhodecode' % (ver))
604 _rhodecode_hook = True
607 _rhodecode_hook = True
605 except:
608 except:
606 log.error(traceback.format_exc())
609 log.error(traceback.format_exc())
607 else:
610 else:
608 # there is no hook in this dir, so we want to create one
611 # there is no hook in this dir, so we want to create one
609 _rhodecode_hook = True
612 _rhodecode_hook = True
610
613
611 if _rhodecode_hook or force_create:
614 if _rhodecode_hook or force_create:
612 log.debug('writing %s hook file !' % h_type)
615 log.debug('writing %s hook file !' % h_type)
613 with open(_hook_file, 'wb') as f:
616 with open(_hook_file, 'wb') as f:
614 tmpl = tmpl.replace('_TMPL_', rhodecode.__version__)
617 tmpl = tmpl.replace('_TMPL_', rhodecode.__version__)
615 f.write(tmpl)
618 f.write(tmpl)
616 os.chmod(_hook_file, 0755)
619 os.chmod(_hook_file, 0755)
617 else:
620 else:
618 log.debug('skipping writing hook file')
621 log.debug('skipping writing hook file')
General Comments 0
You need to be logged in to leave comments. Login now