# HG changeset patch # User Marcin Kuzminski # Date 1336249373 -7200 # Node ID 09fd90d91b6afdd823080d1929a67b8bdc4d49cd # Parent 69404d45f6c1cd5c9c6cba20cc3e99db8cd419df# Parent d919631e839028adb76209c17cd76c30ee174e12 merge with beta diff -r 69404d45f6c1 -r 09fd90d91b6a docs/changelog.rst --- a/docs/changelog.rst Thu May 03 01:05:08 2012 +0200 +++ b/docs/changelog.rst Sat May 05 22:22:53 2012 +0200 @@ -25,6 +25,7 @@ - #374 LDAP config is discarded when LDAP can't be activated - limited push/pull operations are now logged for git in the journal - bumped mercurial to 2.2.X series +- added support for displaying submodules in file-browser fixes +++++ @@ -34,6 +35,8 @@ - #418 cast to unicode fixes in notification objects - #426 fixed mention extracting regex - fixed remote-pulling for git remotes remopositories +- fixed #434: Error when accessing files or changesets of a git repository + with submodules 1.3.4 (**2012-03-28**) ---------------------- diff -r 69404d45f6c1 -r 09fd90d91b6a docs/conf.py --- a/docs/conf.py Thu May 03 01:05:08 2012 +0200 +++ b/docs/conf.py Sat May 05 22:22:53 2012 +0200 @@ -27,8 +27,8 @@ # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', - 'sphinx.ext.intersphinx', 'sphinx.ext.todo', +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', + 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.viewcode'] # Add any paths that contain templates here, relative to this directory. diff -r 69404d45f6c1 -r 09fd90d91b6a requires.txt --- a/requires.txt Thu May 03 01:05:08 2012 +0200 +++ b/requires.txt Sat May 05 22:22:53 2012 +0200 @@ -5,7 +5,7 @@ SQLAlchemy==0.7.6 Mako==0.7.0 pygments>=1.4 -whoosh>=2.3.0,<2.4 +whoosh>=2.4.0,<2.5 celery>=2.2.5,<2.3 babel python-dateutil>=1.5.0,<2.0.0 @@ -14,4 +14,4 @@ markdown==2.1.1 docutils==0.8.1 py-bcrypt -mercurial>=2.2,<2.3 \ No newline at end of file +mercurial>=2.2.1,<2.3 \ No newline at end of file diff -r 69404d45f6c1 -r 09fd90d91b6a rhodecode/__init__.py --- a/rhodecode/__init__.py Thu May 03 01:05:08 2012 +0200 +++ b/rhodecode/__init__.py Sat May 05 22:22:53 2012 +0200 @@ -54,7 +54,7 @@ "SQLAlchemy==0.7.6", "Mako==0.7.0", "pygments>=1.4", - "whoosh>=2.3.0,<2.4", + "whoosh>=2.4.0,<2.5", "celery>=2.2.5,<2.3", "babel", "python-dateutil>=1.5.0,<2.0.0", @@ -69,10 +69,10 @@ requirements.append("pysqlite") if __platform__ in PLATFORM_WIN: - requirements.append("mercurial>=2.2,<2.3") + requirements.append("mercurial>=2.2.1,<2.3") else: requirements.append("py-bcrypt") - requirements.append("mercurial>=2.2,<2.3") + requirements.append("mercurial>=2.2.1,<2.3") def get_version(): diff -r 69404d45f6c1 -r 09fd90d91b6a rhodecode/controllers/changelog.py --- a/rhodecode/controllers/changelog.py Thu May 03 01:05:08 2012 +0200 +++ b/rhodecode/controllers/changelog.py Sat May 05 22:22:53 2012 +0200 @@ -126,12 +126,7 @@ elif repo.alias == 'hg': dag = graphmod.dagwalker(repo._repo, revs) - try: - c.dag = graphmod.colored(dag) - except: - #HG 2.2+ - c.dag = graphmod.colored(dag, repo._repo) - + c.dag = graphmod.colored(dag, repo._repo) for (id, type, ctx, vtx, edges) in c.dag: if type != graphmod.CHANGESET: continue diff -r 69404d45f6c1 -r 09fd90d91b6a rhodecode/lib/diffs.py --- a/rhodecode/lib/diffs.py Thu May 03 01:05:08 2012 +0200 +++ b/rhodecode/lib/diffs.py Sat May 05 22:22:53 2012 +0200 @@ -33,8 +33,8 @@ from pylons.i18n.translation import _ from rhodecode.lib.vcs.exceptions import VCSError -from rhodecode.lib.vcs.nodes import FileNode - +from rhodecode.lib.vcs.nodes import FileNode, SubModuleNode +from rhodecode.lib.helpers import escape from rhodecode.lib.utils import EmptyChangeset @@ -79,9 +79,13 @@ 'diff menu to display this diff')) stats = (0, 0) size = 0 - if not diff: - diff = wrap_to_table(_('No changes detected')) + submodules = filter(lambda o: isinstance(o, SubModuleNode), + [filenode_new, filenode_old]) + if submodules: + diff = wrap_to_table(escape('Submodule %r' % submodules[0])) + else: + diff = wrap_to_table(_('No changes detected')) cs1 = filenode_old.changeset.raw_id cs2 = filenode_new.changeset.raw_id @@ -97,6 +101,10 @@ """ # make sure we pass in default context context = context or 3 + submodules = filter(lambda o: isinstance(o, SubModuleNode), + [filenode_new, filenode_old]) + if submodules: + return '' for filenode in (filenode_old, filenode_new): if not isinstance(filenode, FileNode): @@ -109,7 +117,6 @@ vcs_gitdiff = repo.get_diff(old_raw_id, new_raw_id, filenode_new.path, ignore_whitespace, context) - return vcs_gitdiff diff -r 69404d45f6c1 -r 09fd90d91b6a rhodecode/lib/hooks.py --- a/rhodecode/lib/hooks.py Thu May 03 01:05:08 2012 +0200 +++ b/rhodecode/lib/hooks.py Sat May 05 22:22:53 2012 +0200 @@ -111,7 +111,7 @@ Maps user last push action to new changeset id, from mercurial :param ui: - :param repo: + :param repo: repo object containing the `ui` object """ extras = dict(repo.ui.configitems('rhodecode_extras')) diff -r 69404d45f6c1 -r 09fd90d91b6a rhodecode/lib/middleware/simplegit.py --- a/rhodecode/lib/middleware/simplegit.py Thu May 03 01:05:08 2012 +0200 +++ b/rhodecode/lib/middleware/simplegit.py Sat May 05 22:22:53 2012 +0200 @@ -201,7 +201,7 @@ # invalidate cache on push if action == 'push': self._invalidate_cache(repo_name) - self._handle_githooks(action, baseui, environ) + self._handle_githooks(repo_name, action, baseui, environ) log.info('%s action on GIT repo "%s"' % (action, repo_name)) app = self.__make_app(repo_name, repo_path) @@ -264,7 +264,7 @@ op = getattr(self, '_git_stored_op', 'pull') return op - def _handle_githooks(self, action, baseui, environ): + def _handle_githooks(self, repo_name, action, baseui, environ): from rhodecode.lib.hooks import log_pull_action, log_push_action service = environ['QUERY_STRING'].split('=') if len(service) < 2: @@ -279,9 +279,9 @@ pull_hook = 'preoutgoing.pull_logger' _hooks = dict(baseui.configitems('hooks')) or {} if action == 'push' and _hooks.get(push_hook): - log_push_action(ui=baseui, repo=repo._repo) + log_push_action(ui=baseui, repo=_repo._repo) elif action == 'pull' and _hooks.get(pull_hook): - log_pull_action(ui=baseui, repo=repo._repo) + log_pull_action(ui=baseui, repo=_repo._repo) def __inject_extras(self, repo_path, baseui, extras={}): """ diff -r 69404d45f6c1 -r 09fd90d91b6a rhodecode/lib/vcs/backends/base.py --- a/rhodecode/lib/vcs/backends/base.py Thu May 03 01:05:08 2012 +0200 +++ b/rhodecode/lib/vcs/backends/base.py Sat May 05 22:22:53 2012 +0200 @@ -909,3 +909,48 @@ :raises ``CommitError``: if any error occurs while committing """ raise NotImplementedError + + +class EmptyChangeset(BaseChangeset): + """ + An dummy empty changeset. It's possible to pass hash when creating + an EmptyChangeset + """ + + def __init__(self, cs='0' * 40, repo=None, requested_revision=None, + alias=None): + self._empty_cs = cs + self.revision = -1 + self.message = '' + self.author = '' + self.date = '' + self.repository = repo + self.requested_revision = requested_revision + self.alias = alias + + @LazyProperty + def raw_id(self): + """ + Returns raw string identifying this changeset, useful for web + representation. + """ + + return self._empty_cs + + @LazyProperty + def branch(self): + from rhodecode.lib.vcs.backends import get_backend + return get_backend(self.alias).DEFAULT_BRANCH_NAME + + @LazyProperty + def short_id(self): + return self.raw_id[:12] + + def get_file_changeset(self, path): + return self + + def get_file_content(self, path): + return u'' + + def get_file_size(self, path): + return 0 diff -r 69404d45f6c1 -r 09fd90d91b6a rhodecode/lib/vcs/backends/git/changeset.py --- a/rhodecode/lib/vcs/backends/git/changeset.py Thu May 03 01:05:08 2012 +0200 +++ b/rhodecode/lib/vcs/backends/git/changeset.py Sat May 05 22:22:53 2012 +0200 @@ -10,7 +10,8 @@ from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError from rhodecode.lib.vcs.exceptions import ImproperArchiveTypeError from rhodecode.lib.vcs.backends.base import BaseChangeset -from rhodecode.lib.vcs.nodes import FileNode, DirNode, NodeKind, RootNode, RemovedFileNode +from rhodecode.lib.vcs.nodes import FileNode, DirNode, NodeKind, RootNode, \ + RemovedFileNode, SubModuleNode from rhodecode.lib.vcs.utils import safe_unicode from rhodecode.lib.vcs.utils import date_fromtimestamp from rhodecode.lib.vcs.utils.lazy import LazyProperty @@ -329,7 +330,13 @@ tree = self.repository._repo[id] dirnodes = [] filenodes = [] + als = self.repository.alias for name, stat, id in tree.iteritems(): + if objects.S_ISGITLINK(stat): + dirnodes.append(SubModuleNode(name, url=None, changeset=id, + alias=als)) + continue + obj = self.repository._repo.get_object(id) if path != '': obj_path = '/'.join((path, name)) @@ -357,24 +364,31 @@ path = self._fix_path(path) if not path in self.nodes: try: - id = self._get_id_for_path(path) + id_ = self._get_id_for_path(path) except ChangesetError: raise NodeDoesNotExistError("Cannot find one of parents' " "directories for a given path: %s" % path) - obj = self.repository._repo.get_object(id) - if isinstance(obj, objects.Tree): - if path == '': - node = RootNode(changeset=self) + + als = self.repository.alias + _GL = lambda m: m and objects.S_ISGITLINK(m) + if _GL(self._stat_modes.get(path)): + node = SubModuleNode(path, url=None, changeset=id_, alias=als) + else: + obj = self.repository._repo.get_object(id_) + + if isinstance(obj, objects.Tree): + if path == '': + node = RootNode(changeset=self) + else: + node = DirNode(path, changeset=self) + node._tree = obj + elif isinstance(obj, objects.Blob): + node = FileNode(path, changeset=self) + node._blob = obj else: - node = DirNode(path, changeset=self) - node._tree = obj - elif isinstance(obj, objects.Blob): - node = FileNode(path, changeset=self) - node._blob = obj - else: - raise NodeDoesNotExistError("There is no file nor directory " - "at the given path %r at revision %r" - % (path, self.short_id)) + raise NodeDoesNotExistError("There is no file nor directory " + "at the given path %r at revision %r" + % (path, self.short_id)) # cache node self.nodes[path] = node return self.nodes[path] @@ -416,7 +430,6 @@ line)) _path = splitted[1].strip() paths.add(_path) - return sorted(paths) @LazyProperty diff -r 69404d45f6c1 -r 09fd90d91b6a rhodecode/lib/vcs/backends/git/repository.py --- a/rhodecode/lib/vcs/backends/git/repository.py Thu May 03 01:05:08 2012 +0200 +++ b/rhodecode/lib/vcs/backends/git/repository.py Sat May 05 22:22:53 2012 +0200 @@ -52,7 +52,7 @@ if baseui is None: from mercurial.ui import ui baseui = ui() - # patch the instance of GitRepo with an "FAKE" ui object to add + # patch the instance of GitRepo with an "FAKE" ui object to add # compatibility layer with Mercurial setattr(self._repo, 'ui', baseui) @@ -411,7 +411,7 @@ yield self.get_changeset(rev) def get_diff(self, rev1, rev2, path=None, ignore_whitespace=False, - context=3): + context=3): """ Returns (git like) *diff*, as plain text. Shows changes introduced by ``rev2`` since ``rev1``. diff -r 69404d45f6c1 -r 09fd90d91b6a rhodecode/lib/vcs/backends/hg/changeset.py --- a/rhodecode/lib/vcs/backends/hg/changeset.py Thu May 03 01:05:08 2012 +0200 +++ b/rhodecode/lib/vcs/backends/hg/changeset.py Sat May 05 22:22:53 2012 +0200 @@ -5,8 +5,9 @@ from rhodecode.lib.vcs.conf import settings from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError, \ ChangesetError, ImproperArchiveTypeError, NodeDoesNotExistError, VCSError -from rhodecode.lib.vcs.nodes import AddedFileNodesGenerator, ChangedFileNodesGenerator, \ - DirNode, FileNode, NodeKind, RemovedFileNodesGenerator, RootNode +from rhodecode.lib.vcs.nodes import AddedFileNodesGenerator, \ + ChangedFileNodesGenerator, DirNode, FileNode, NodeKind, \ + RemovedFileNodesGenerator, RootNode, SubModuleNode from rhodecode.lib.vcs.utils import safe_str, safe_unicode, date_fromtimestamp from rhodecode.lib.vcs.utils.lazy import LazyProperty @@ -159,6 +160,13 @@ " %r" % (self.revision, path)) return self._ctx.filectx(path) + def _extract_submodules(self): + """ + returns a dictionary with submodule information from substate file + of hg repository + """ + return self._ctx.substate + def get_file_mode(self, path): """ Returns stat mode of the file at the given ``path``. @@ -271,17 +279,27 @@ raise ChangesetError("Directory does not exist for revision %r at " " %r" % (self.revision, path)) path = self._fix_path(path) + filenodes = [FileNode(f, changeset=self) for f in self._file_paths if os.path.dirname(f) == path] dirs = path == '' and '' or [d for d in self._dir_paths if d and posixpath.dirname(d) == path] dirnodes = [DirNode(d, changeset=self) for d in dirs if os.path.dirname(d) == path] + + als = self.repository.alias + for k, vals in self._extract_submodules().iteritems(): + #vals = url,rev,type + loc = vals[0] + cs = vals[1] + dirnodes.append(SubModuleNode(k, url=loc, changeset=cs, + alias=als)) nodes = dirnodes + filenodes # cache nodes for node in nodes: self.nodes[node.path] = node nodes.sort() + return nodes def get_node(self, path): diff -r 69404d45f6c1 -r 09fd90d91b6a rhodecode/lib/vcs/nodes.py --- a/rhodecode/lib/vcs/nodes.py Thu May 03 01:05:08 2012 +0200 +++ b/rhodecode/lib/vcs/nodes.py Sat May 05 22:22:53 2012 +0200 @@ -8,19 +8,22 @@ :created_on: Apr 8, 2010 :copyright: (c) 2010-2011 by Marcin Kuzminski, Lukasz Balcerzak. """ +import os import stat import posixpath import mimetypes +from pygments import lexers + from rhodecode.lib.vcs.utils.lazy import LazyProperty -from rhodecode.lib.vcs.utils import safe_unicode +from rhodecode.lib.vcs.utils import safe_unicode, safe_str from rhodecode.lib.vcs.exceptions import NodeError from rhodecode.lib.vcs.exceptions import RemovedFileNodeError - -from pygments import lexers +from rhodecode.lib.vcs.backends.base import EmptyChangeset class NodeKind: + SUBMODULE = -1 DIR = 1 FILE = 2 @@ -209,6 +212,13 @@ """ return self.kind == NodeKind.DIR and self.path == '' + def is_submodule(self): + """ + Returns ``True`` if node's kind is ``NodeKind.SUBMODULE``, ``False`` + otherwise. + """ + return self.kind == NodeKind.SUBMODULE + @LazyProperty def added(self): return self.state is NodeState.ADDED @@ -561,3 +571,41 @@ def __repr__(self): return '<%s>' % self.__class__.__name__ + + +class SubModuleNode(Node): + """ + represents a SubModule of Git or SubRepo of Mercurial + """ + is_binary = False + size = 0 + + def __init__(self, name, url=None, changeset=None, alias=None): + self.path = name + self.kind = NodeKind.SUBMODULE + self.alias = alias + # we have to use emptyChangeset here since this can point to svn/git/hg + # submodules we cannot get from repository + self.changeset = EmptyChangeset(str(changeset), alias=alias) + self.url = url or self._extract_submodule_url() + + def __repr__(self): + return '<%s %r @ %s>' % (self.__class__.__name__, self.path, + self.changeset.short_id) + + def _extract_submodule_url(self): + if self.alias == 'git': + #TODO: find a way to parse gits submodule file and extract the + # linking URL + return self.path + if self.alias == 'hg': + return self.path + + @LazyProperty + def name(self): + """ + Returns name of the node so if its path + then only last part is returned. + """ + org = safe_unicode(self.path.rstrip('/').split('/')[-1]) + return u'%s @ %s' % (org, self.changeset.short_id) diff -r 69404d45f6c1 -r 09fd90d91b6a rhodecode/public/css/style.css --- a/rhodecode/public/css/style.css Thu May 03 01:05:08 2012 +0200 +++ b/rhodecode/public/css/style.css Sat May 05 22:22:53 2012 +0200 @@ -2729,6 +2729,14 @@ text-align: left; } +table.code-browser .submodule-dir { + background: url("../images/icons/disconnect.png") no-repeat scroll 3px; + height: 16px; + padding-left: 20px; + text-align: left; +} + + .box .search { clear: both; overflow: hidden; diff -r 69404d45f6c1 -r 09fd90d91b6a rhodecode/templates/changeset/changeset.html --- a/rhodecode/templates/changeset/changeset.html Thu May 03 01:05:08 2012 +0200 +++ b/rhodecode/templates/changeset/changeset.html Sat May 05 22:22:53 2012 +0200 @@ -81,8 +81,11 @@ %if len(c.changeset.parents)>1: ${_('merge')} %endif - - ${h.link_to(c.changeset.branch,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))} + %if c.changeset.branch: + + ${h.link_to(c.changeset.branch,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))} + + %endif %for tag in c.changeset.tags: ${h.link_to(tag,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))} diff -r 69404d45f6c1 -r 09fd90d91b6a rhodecode/templates/files/files_browser.html --- a/rhodecode/templates/files/files_browser.html Thu May 03 01:05:08 2012 +0200 +++ b/rhodecode/templates/files/files_browser.html Sat May 05 22:22:53 2012 +0200 @@ -70,7 +70,11 @@ %for cnt,node in enumerate(c.file): - ${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")} + %if node.is_submodule(): + ${h.link_to(node.name,node.url or '#',class_="submodule-dir ypjax-link")} + %else: + ${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")} + %endif: %if node.is_file(): diff -r 69404d45f6c1 -r 09fd90d91b6a rhodecode/templates/index_base.html --- a/rhodecode/templates/index_base.html Thu May 03 01:05:08 2012 +0200 +++ b/rhodecode/templates/index_base.html Sat May 05 22:22:53 2012 +0200 @@ -119,7 +119,7 @@