##// END OF EJS Templates
merge with beta
marcink -
r2238:09fd90d9 merge codereview
parent child Browse files
Show More
@@ -25,6 +25,7 b' news'
25 25 - #374 LDAP config is discarded when LDAP can't be activated
26 26 - limited push/pull operations are now logged for git in the journal
27 27 - bumped mercurial to 2.2.X series
28 - added support for displaying submodules in file-browser
28 29
29 30 fixes
30 31 +++++
@@ -34,6 +35,8 b' fixes'
34 35 - #418 cast to unicode fixes in notification objects
35 36 - #426 fixed mention extracting regex
36 37 - fixed remote-pulling for git remotes remopositories
38 - fixed #434: Error when accessing files or changesets of a git repository
39 with submodules
37 40
38 41 1.3.4 (**2012-03-28**)
39 42 ----------------------
@@ -27,8 +27,8 b" sys.path.insert(0, os.path.abspath('..')"
27 27
28 28 # Add any Sphinx extension module names here, as strings. They can be extensions
29 29 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
30 extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest',
31 'sphinx.ext.intersphinx', 'sphinx.ext.todo',
30 extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest',
31 'sphinx.ext.intersphinx', 'sphinx.ext.todo',
32 32 'sphinx.ext.viewcode']
33 33
34 34 # Add any paths that contain templates here, relative to this directory.
@@ -5,7 +5,7 b' formencode==1.2.4'
5 5 SQLAlchemy==0.7.6
6 6 Mako==0.7.0
7 7 pygments>=1.4
8 whoosh>=2.3.0,<2.4
8 whoosh>=2.4.0,<2.5
9 9 celery>=2.2.5,<2.3
10 10 babel
11 11 python-dateutil>=1.5.0,<2.0.0
@@ -14,4 +14,4 b' webob==1.0.8'
14 14 markdown==2.1.1
15 15 docutils==0.8.1
16 16 py-bcrypt
17 mercurial>=2.2,<2.3 No newline at end of file
17 mercurial>=2.2.1,<2.3 No newline at end of file
@@ -54,7 +54,7 b' requirements = ['
54 54 "SQLAlchemy==0.7.6",
55 55 "Mako==0.7.0",
56 56 "pygments>=1.4",
57 "whoosh>=2.3.0,<2.4",
57 "whoosh>=2.4.0,<2.5",
58 58 "celery>=2.2.5,<2.3",
59 59 "babel",
60 60 "python-dateutil>=1.5.0,<2.0.0",
@@ -69,10 +69,10 b' if __py_version__ < (2, 6):'
69 69 requirements.append("pysqlite")
70 70
71 71 if __platform__ in PLATFORM_WIN:
72 requirements.append("mercurial>=2.2,<2.3")
72 requirements.append("mercurial>=2.2.1,<2.3")
73 73 else:
74 74 requirements.append("py-bcrypt")
75 requirements.append("mercurial>=2.2,<2.3")
75 requirements.append("mercurial>=2.2.1,<2.3")
76 76
77 77
78 78 def get_version():
@@ -126,12 +126,7 b' class ChangelogController(BaseRepoContro'
126 126
127 127 elif repo.alias == 'hg':
128 128 dag = graphmod.dagwalker(repo._repo, revs)
129 try:
130 c.dag = graphmod.colored(dag)
131 except:
132 #HG 2.2+
133 c.dag = graphmod.colored(dag, repo._repo)
134
129 c.dag = graphmod.colored(dag, repo._repo)
135 130 for (id, type, ctx, vtx, edges) in c.dag:
136 131 if type != graphmod.CHANGESET:
137 132 continue
@@ -33,8 +33,8 b' from itertools import tee, imap'
33 33 from pylons.i18n.translation import _
34 34
35 35 from rhodecode.lib.vcs.exceptions import VCSError
36 from rhodecode.lib.vcs.nodes import FileNode
37
36 from rhodecode.lib.vcs.nodes import FileNode, SubModuleNode
37 from rhodecode.lib.helpers import escape
38 38 from rhodecode.lib.utils import EmptyChangeset
39 39
40 40
@@ -79,9 +79,13 b' def wrapped_diff(filenode_old, filenode_'
79 79 'diff menu to display this diff'))
80 80 stats = (0, 0)
81 81 size = 0
82
83 82 if not diff:
84 diff = wrap_to_table(_('No changes detected'))
83 submodules = filter(lambda o: isinstance(o, SubModuleNode),
84 [filenode_new, filenode_old])
85 if submodules:
86 diff = wrap_to_table(escape('Submodule %r' % submodules[0]))
87 else:
88 diff = wrap_to_table(_('No changes detected'))
85 89
86 90 cs1 = filenode_old.changeset.raw_id
87 91 cs2 = filenode_new.changeset.raw_id
@@ -97,6 +101,10 b' def get_gitdiff(filenode_old, filenode_n'
97 101 """
98 102 # make sure we pass in default context
99 103 context = context or 3
104 submodules = filter(lambda o: isinstance(o, SubModuleNode),
105 [filenode_new, filenode_old])
106 if submodules:
107 return ''
100 108
101 109 for filenode in (filenode_old, filenode_new):
102 110 if not isinstance(filenode, FileNode):
@@ -109,7 +117,6 b' def get_gitdiff(filenode_old, filenode_n'
109 117
110 118 vcs_gitdiff = repo.get_diff(old_raw_id, new_raw_id, filenode_new.path,
111 119 ignore_whitespace, context)
112
113 120 return vcs_gitdiff
114 121
115 122
@@ -111,7 +111,7 b' def log_push_action(ui, repo, **kwargs):'
111 111 Maps user last push action to new changeset id, from mercurial
112 112
113 113 :param ui:
114 :param repo:
114 :param repo: repo object containing the `ui` object
115 115 """
116 116
117 117 extras = dict(repo.ui.configitems('rhodecode_extras'))
@@ -201,7 +201,7 b' class SimpleGit(BaseVCSController):'
201 201 # invalidate cache on push
202 202 if action == 'push':
203 203 self._invalidate_cache(repo_name)
204 self._handle_githooks(action, baseui, environ)
204 self._handle_githooks(repo_name, action, baseui, environ)
205 205
206 206 log.info('%s action on GIT repo "%s"' % (action, repo_name))
207 207 app = self.__make_app(repo_name, repo_path)
@@ -264,7 +264,7 b' class SimpleGit(BaseVCSController):'
264 264 op = getattr(self, '_git_stored_op', 'pull')
265 265 return op
266 266
267 def _handle_githooks(self, action, baseui, environ):
267 def _handle_githooks(self, repo_name, action, baseui, environ):
268 268 from rhodecode.lib.hooks import log_pull_action, log_push_action
269 269 service = environ['QUERY_STRING'].split('=')
270 270 if len(service) < 2:
@@ -279,9 +279,9 b' class SimpleGit(BaseVCSController):'
279 279 pull_hook = 'preoutgoing.pull_logger'
280 280 _hooks = dict(baseui.configitems('hooks')) or {}
281 281 if action == 'push' and _hooks.get(push_hook):
282 log_push_action(ui=baseui, repo=repo._repo)
282 log_push_action(ui=baseui, repo=_repo._repo)
283 283 elif action == 'pull' and _hooks.get(pull_hook):
284 log_pull_action(ui=baseui, repo=repo._repo)
284 log_pull_action(ui=baseui, repo=_repo._repo)
285 285
286 286 def __inject_extras(self, repo_path, baseui, extras={}):
287 287 """
@@ -909,3 +909,48 b' class BaseInMemoryChangeset(object):'
909 909 :raises ``CommitError``: if any error occurs while committing
910 910 """
911 911 raise NotImplementedError
912
913
914 class EmptyChangeset(BaseChangeset):
915 """
916 An dummy empty changeset. It's possible to pass hash when creating
917 an EmptyChangeset
918 """
919
920 def __init__(self, cs='0' * 40, repo=None, requested_revision=None,
921 alias=None):
922 self._empty_cs = cs
923 self.revision = -1
924 self.message = ''
925 self.author = ''
926 self.date = ''
927 self.repository = repo
928 self.requested_revision = requested_revision
929 self.alias = alias
930
931 @LazyProperty
932 def raw_id(self):
933 """
934 Returns raw string identifying this changeset, useful for web
935 representation.
936 """
937
938 return self._empty_cs
939
940 @LazyProperty
941 def branch(self):
942 from rhodecode.lib.vcs.backends import get_backend
943 return get_backend(self.alias).DEFAULT_BRANCH_NAME
944
945 @LazyProperty
946 def short_id(self):
947 return self.raw_id[:12]
948
949 def get_file_changeset(self, path):
950 return self
951
952 def get_file_content(self, path):
953 return u''
954
955 def get_file_size(self, path):
956 return 0
@@ -10,7 +10,8 b' from rhodecode.lib.vcs.exceptions import'
10 10 from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError
11 11 from rhodecode.lib.vcs.exceptions import ImproperArchiveTypeError
12 12 from rhodecode.lib.vcs.backends.base import BaseChangeset
13 from rhodecode.lib.vcs.nodes import FileNode, DirNode, NodeKind, RootNode, RemovedFileNode
13 from rhodecode.lib.vcs.nodes import FileNode, DirNode, NodeKind, RootNode, \
14 RemovedFileNode, SubModuleNode
14 15 from rhodecode.lib.vcs.utils import safe_unicode
15 16 from rhodecode.lib.vcs.utils import date_fromtimestamp
16 17 from rhodecode.lib.vcs.utils.lazy import LazyProperty
@@ -329,7 +330,13 b' class GitChangeset(BaseChangeset):'
329 330 tree = self.repository._repo[id]
330 331 dirnodes = []
331 332 filenodes = []
333 als = self.repository.alias
332 334 for name, stat, id in tree.iteritems():
335 if objects.S_ISGITLINK(stat):
336 dirnodes.append(SubModuleNode(name, url=None, changeset=id,
337 alias=als))
338 continue
339
333 340 obj = self.repository._repo.get_object(id)
334 341 if path != '':
335 342 obj_path = '/'.join((path, name))
@@ -357,24 +364,31 b' class GitChangeset(BaseChangeset):'
357 364 path = self._fix_path(path)
358 365 if not path in self.nodes:
359 366 try:
360 id = self._get_id_for_path(path)
367 id_ = self._get_id_for_path(path)
361 368 except ChangesetError:
362 369 raise NodeDoesNotExistError("Cannot find one of parents' "
363 370 "directories for a given path: %s" % path)
364 obj = self.repository._repo.get_object(id)
365 if isinstance(obj, objects.Tree):
366 if path == '':
367 node = RootNode(changeset=self)
371
372 als = self.repository.alias
373 _GL = lambda m: m and objects.S_ISGITLINK(m)
374 if _GL(self._stat_modes.get(path)):
375 node = SubModuleNode(path, url=None, changeset=id_, alias=als)
376 else:
377 obj = self.repository._repo.get_object(id_)
378
379 if isinstance(obj, objects.Tree):
380 if path == '':
381 node = RootNode(changeset=self)
382 else:
383 node = DirNode(path, changeset=self)
384 node._tree = obj
385 elif isinstance(obj, objects.Blob):
386 node = FileNode(path, changeset=self)
387 node._blob = obj
368 388 else:
369 node = DirNode(path, changeset=self)
370 node._tree = obj
371 elif isinstance(obj, objects.Blob):
372 node = FileNode(path, changeset=self)
373 node._blob = obj
374 else:
375 raise NodeDoesNotExistError("There is no file nor directory "
376 "at the given path %r at revision %r"
377 % (path, self.short_id))
389 raise NodeDoesNotExistError("There is no file nor directory "
390 "at the given path %r at revision %r"
391 % (path, self.short_id))
378 392 # cache node
379 393 self.nodes[path] = node
380 394 return self.nodes[path]
@@ -416,7 +430,6 b' class GitChangeset(BaseChangeset):'
416 430 line))
417 431 _path = splitted[1].strip()
418 432 paths.add(_path)
419
420 433 return sorted(paths)
421 434
422 435 @LazyProperty
@@ -52,7 +52,7 b' class GitRepository(BaseRepository):'
52 52 if baseui is None:
53 53 from mercurial.ui import ui
54 54 baseui = ui()
55 # patch the instance of GitRepo with an "FAKE" ui object to add
55 # patch the instance of GitRepo with an "FAKE" ui object to add
56 56 # compatibility layer with Mercurial
57 57 setattr(self._repo, 'ui', baseui)
58 58
@@ -411,7 +411,7 b' class GitRepository(BaseRepository):'
411 411 yield self.get_changeset(rev)
412 412
413 413 def get_diff(self, rev1, rev2, path=None, ignore_whitespace=False,
414 context=3):
414 context=3):
415 415 """
416 416 Returns (git like) *diff*, as plain text. Shows changes introduced by
417 417 ``rev2`` since ``rev1``.
@@ -5,8 +5,9 b' from rhodecode.lib.vcs.backends.base imp'
5 5 from rhodecode.lib.vcs.conf import settings
6 6 from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError, \
7 7 ChangesetError, ImproperArchiveTypeError, NodeDoesNotExistError, VCSError
8 from rhodecode.lib.vcs.nodes import AddedFileNodesGenerator, ChangedFileNodesGenerator, \
9 DirNode, FileNode, NodeKind, RemovedFileNodesGenerator, RootNode
8 from rhodecode.lib.vcs.nodes import AddedFileNodesGenerator, \
9 ChangedFileNodesGenerator, DirNode, FileNode, NodeKind, \
10 RemovedFileNodesGenerator, RootNode, SubModuleNode
10 11
11 12 from rhodecode.lib.vcs.utils import safe_str, safe_unicode, date_fromtimestamp
12 13 from rhodecode.lib.vcs.utils.lazy import LazyProperty
@@ -159,6 +160,13 b' class MercurialChangeset(BaseChangeset):'
159 160 " %r" % (self.revision, path))
160 161 return self._ctx.filectx(path)
161 162
163 def _extract_submodules(self):
164 """
165 returns a dictionary with submodule information from substate file
166 of hg repository
167 """
168 return self._ctx.substate
169
162 170 def get_file_mode(self, path):
163 171 """
164 172 Returns stat mode of the file at the given ``path``.
@@ -271,17 +279,27 b' class MercurialChangeset(BaseChangeset):'
271 279 raise ChangesetError("Directory does not exist for revision %r at "
272 280 " %r" % (self.revision, path))
273 281 path = self._fix_path(path)
282
274 283 filenodes = [FileNode(f, changeset=self) for f in self._file_paths
275 284 if os.path.dirname(f) == path]
276 285 dirs = path == '' and '' or [d for d in self._dir_paths
277 286 if d and posixpath.dirname(d) == path]
278 287 dirnodes = [DirNode(d, changeset=self) for d in dirs
279 288 if os.path.dirname(d) == path]
289
290 als = self.repository.alias
291 for k, vals in self._extract_submodules().iteritems():
292 #vals = url,rev,type
293 loc = vals[0]
294 cs = vals[1]
295 dirnodes.append(SubModuleNode(k, url=loc, changeset=cs,
296 alias=als))
280 297 nodes = dirnodes + filenodes
281 298 # cache nodes
282 299 for node in nodes:
283 300 self.nodes[node.path] = node
284 301 nodes.sort()
302
285 303 return nodes
286 304
287 305 def get_node(self, path):
@@ -8,19 +8,22 b''
8 8 :created_on: Apr 8, 2010
9 9 :copyright: (c) 2010-2011 by Marcin Kuzminski, Lukasz Balcerzak.
10 10 """
11 import os
11 12 import stat
12 13 import posixpath
13 14 import mimetypes
14 15
16 from pygments import lexers
17
15 18 from rhodecode.lib.vcs.utils.lazy import LazyProperty
16 from rhodecode.lib.vcs.utils import safe_unicode
19 from rhodecode.lib.vcs.utils import safe_unicode, safe_str
17 20 from rhodecode.lib.vcs.exceptions import NodeError
18 21 from rhodecode.lib.vcs.exceptions import RemovedFileNodeError
19
20 from pygments import lexers
22 from rhodecode.lib.vcs.backends.base import EmptyChangeset
21 23
22 24
23 25 class NodeKind:
26 SUBMODULE = -1
24 27 DIR = 1
25 28 FILE = 2
26 29
@@ -209,6 +212,13 b' class Node(object):'
209 212 """
210 213 return self.kind == NodeKind.DIR and self.path == ''
211 214
215 def is_submodule(self):
216 """
217 Returns ``True`` if node's kind is ``NodeKind.SUBMODULE``, ``False``
218 otherwise.
219 """
220 return self.kind == NodeKind.SUBMODULE
221
212 222 @LazyProperty
213 223 def added(self):
214 224 return self.state is NodeState.ADDED
@@ -561,3 +571,41 b' class RootNode(DirNode):'
561 571
562 572 def __repr__(self):
563 573 return '<%s>' % self.__class__.__name__
574
575
576 class SubModuleNode(Node):
577 """
578 represents a SubModule of Git or SubRepo of Mercurial
579 """
580 is_binary = False
581 size = 0
582
583 def __init__(self, name, url=None, changeset=None, alias=None):
584 self.path = name
585 self.kind = NodeKind.SUBMODULE
586 self.alias = alias
587 # we have to use emptyChangeset here since this can point to svn/git/hg
588 # submodules we cannot get from repository
589 self.changeset = EmptyChangeset(str(changeset), alias=alias)
590 self.url = url or self._extract_submodule_url()
591
592 def __repr__(self):
593 return '<%s %r @ %s>' % (self.__class__.__name__, self.path,
594 self.changeset.short_id)
595
596 def _extract_submodule_url(self):
597 if self.alias == 'git':
598 #TODO: find a way to parse gits submodule file and extract the
599 # linking URL
600 return self.path
601 if self.alias == 'hg':
602 return self.path
603
604 @LazyProperty
605 def name(self):
606 """
607 Returns name of the node so if its path
608 then only last part is returned.
609 """
610 org = safe_unicode(self.path.rstrip('/').split('/')[-1])
611 return u'%s @ %s' % (org, self.changeset.short_id)
@@ -2729,6 +2729,14 b' table.code-browser .browser-dir {'
2729 2729 text-align: left;
2730 2730 }
2731 2731
2732 table.code-browser .submodule-dir {
2733 background: url("../images/icons/disconnect.png") no-repeat scroll 3px;
2734 height: 16px;
2735 padding-left: 20px;
2736 text-align: left;
2737 }
2738
2739
2732 2740 .box .search {
2733 2741 clear: both;
2734 2742 overflow: hidden;
@@ -81,8 +81,11 b''
81 81 %if len(c.changeset.parents)>1:
82 82 <span class="merge">${_('merge')}</span>
83 83 %endif
84 <span class="branchtag" title="${'%s %s' % (_('branch'),c.changeset.branch)}">
85 ${h.link_to(c.changeset.branch,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}</span>
84 %if c.changeset.branch:
85 <span class="branchtag" title="${'%s %s' % (_('branch'),c.changeset.branch)}">
86 ${h.link_to(c.changeset.branch,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}
87 </span>
88 %endif
86 89 %for tag in c.changeset.tags:
87 90 <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}">
88 91 ${h.link_to(tag,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}</span>
@@ -70,7 +70,11 b''
70 70 %for cnt,node in enumerate(c.file):
71 71 <tr class="parity${cnt%2}">
72 72 <td>
73 ${h.link_to(node.name,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=h.safe_unicode(node.path)),class_=file_class(node)+" ypjax-link")}
73 %if node.is_submodule():
74 ${h.link_to(node.name,node.url or '#',class_="submodule-dir ypjax-link")}
75 %else:
76 ${h.link_to(node.name, h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=h.safe_unicode(node.path)),class_=file_class(node)+" ypjax-link")}
77 %endif:
74 78 </td>
75 79 <td>
76 80 %if node.is_file():
@@ -119,7 +119,7 b''
119 119 </div>
120 120 </div>
121 121 <script>
122 YUD.get('repo_count').innerHTML = ${cnt};
122 YUD.get('repo_count').innerHTML = ${cnt+1};
123 123 var func = function(node){
124 124 return node.parentNode.parentNode.parentNode.parentNode;
125 125 }
@@ -15,7 +15,7 b''
15 15 <div><pre><a href="${h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id)}">r${cs.revision}:${h.short_id(cs.raw_id)}</a></pre></div>
16 16 </td>
17 17 <td>
18 ${h.link_to(h.truncate(cs.message,50),
18 ${h.link_to(h.truncate(cs.message,50) or _('No commit message'),
19 19 h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id),
20 20 title=cs.message)}
21 21 </td>
@@ -25,9 +25,11 b''
25 25 <td title="${cs.author}">${h.person(cs.author)}</td>
26 26 <td>
27 27 <span class="logtags">
28 %if cs.branch:
28 29 <span class="branchtag">
29 30 ${cs.branch}
30 31 </span>
32 %endif
31 33 </span>
32 34 </td>
33 35 <td>
General Comments 0
You need to be logged in to leave comments. Login now