changeset 2232:49dc09e9f076 beta

Implements subrepos view inside filebrowser - fixes issue #434: Error when accessing files or changesets of a git repository with submodules
author Marcin Kuzminski <marcin@python-works.com>
date Thu, 03 May 2012 23:15:47 +0200
parents 2cfaf199a5a7
children 07fce1930417
files rhodecode/lib/vcs/backends/git/changeset.py rhodecode/lib/vcs/backends/hg/changeset.py rhodecode/lib/vcs/nodes.py rhodecode/public/css/style.css rhodecode/templates/files/files_browser.html
diffstat 5 files changed, 81 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/rhodecode/lib/vcs/backends/git/changeset.py	Thu May 03 21:17:30 2012 +0200
+++ b/rhodecode/lib/vcs/backends/git/changeset.py	Thu May 03 23:15:47 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))
--- a/rhodecode/lib/vcs/backends/hg/changeset.py	Thu May 03 21:17:30 2012 +0200
+++ b/rhodecode/lib/vcs/backends/hg/changeset.py	Thu May 03 23:15:47 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):
--- a/rhodecode/lib/vcs/nodes.py	Thu May 03 21:17:30 2012 +0200
+++ b/rhodecode/lib/vcs/nodes.py	Thu May 03 23:15:47 2012 +0200
@@ -8,19 +8,21 @@
     :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
-
 
 class NodeKind:
+    SUBMODULE = -1
     DIR = 1
     FILE = 2
 
@@ -209,6 +211,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 +570,31 @@
 
     def __repr__(self):
         return '<%s>' % self.__class__.__name__
+
+
+class SubModuleNode(Node):
+    """
+    represents a SubModule of Git or SubRepo of Mercurial
+    """
+    def __init__(self, name, url=None, changeset=None, alias=None):
+        self.path = name
+        self.kind = NodeKind.SUBMODULE
+        self.alias = alias
+        # changeset MUST be STR !! since it can point to non-valid SCM
+        self.changeset = str(changeset)
+        self.url = url or self._extract_submodule_url()
+
+    def _extract_submodule_url(self):
+        if self.alias == 'git':
+            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[:12])
--- a/rhodecode/public/css/style.css	Thu May 03 21:17:30 2012 +0200
+++ b/rhodecode/public/css/style.css	Thu May 03 23:15:47 2012 +0200
@@ -2718,6 +2718,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;
--- a/rhodecode/templates/files/files_browser.html	Thu May 03 21:17:30 2012 +0200
+++ b/rhodecode/templates/files/files_browser.html	Thu May 03 23:15:47 2012 +0200
@@ -70,7 +70,11 @@
 		    %for cnt,node in enumerate(c.file):
 				<tr class="parity${cnt%2}">
 		             <td>
-                        ${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:
 		             </td>
 		             <td>
 		             %if node.is_file():