import re
from itertools import chain
from dulwich import objects
from subprocess import Popen, PIPE
from rhodecode.lib.vcs.conf import settings
from rhodecode.lib.vcs.exceptions import RepositoryError
from rhodecode.lib.vcs.exceptions import ChangesetError
from rhodecode.lib.vcs.exceptions import NodeDoesNotExistError
from rhodecode.lib.vcs.exceptions import VCSError
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
class GitChangeset(BaseChangeset):
"""
Represents state of the repository at single revision.
def __init__(self, repository, revision):
self._stat_modes = {}
@@ -320,25 +321,31 @@ class GitChangeset(BaseChangeset):
# Make sure all descriptors would be read
popen.communicate()
def get_nodes(self, path):
if self._get_kind(path) != NodeKind.DIR:
raise ChangesetError("Directory does not exist for revision %r at "
" %r" % (self.revision, path))
path = self._fix_path(path)
id = self._get_id_for_path(path)
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))
else:
obj_path = name
if obj_path not in self._stat_modes:
self._stat_modes[obj_path] = stat
if isinstance(obj, objects.Tree):
dirnodes.append(DirNode(obj_path, changeset=self))
elif isinstance(obj, objects.Blob):
filenodes.append(FileNode(obj_path, changeset=self, mode=stat))
import os
import posixpath
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.paths import get_dirs_for_path
from ...utils.hgcompat import archival, hex
class MercurialChangeset(BaseChangeset):
Represents state of the repository at the single revision.
@@ -150,24 +151,31 @@ class MercurialChangeset(BaseChangeset):
return NodeKind.DIR
raise ChangesetError("Node does not exist at the given path %r"
% (path))
def _get_filectx(self, path):
if self._get_kind(path) != NodeKind.FILE:
raise ChangesetError("File does not exist for revision %r at "
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``.
fctx = self._get_filectx(path)
if 'x' in fctx.flags():
return 0100755
return 0100644
def get_file_content(self, path):
@@ -262,35 +270,45 @@ class MercurialChangeset(BaseChangeset):
Returns combined ``DirNode`` and ``FileNode`` objects list representing
state of changeset at the given ``path``. If node at the given ``path``
is not instance of ``DirNode``, ChangesetError would be raised.
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]
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,
nodes = dirnodes + filenodes
# cache nodes
for node in nodes:
self.nodes[node.path] = node
nodes.sort()
return nodes
def get_node(self, path):
Returns ``Node`` object from the given ``path``. If there is no node at
the given ``path``, ``ChangesetError`` would be raised.
if not path in self.nodes:
if path in self._file_paths:
# -*- coding: utf-8 -*-
vcs.nodes
~~~~~~~~~
Module holding everything related to vcs nodes.
:created_on: Apr 8, 2010
:copyright: (c) 2010-2011 by Marcin Kuzminski, Lukasz Balcerzak.
import stat
import mimetypes
from pygments import lexers
from rhodecode.lib.vcs.utils import safe_unicode, safe_str
from rhodecode.lib.vcs.exceptions import NodeError
from rhodecode.lib.vcs.exceptions import RemovedFileNodeError
class NodeKind:
SUBMODULE = -1
DIR = 1
FILE = 2
class NodeState:
ADDED = u'added'
CHANGED = u'changed'
NOT_CHANGED = u'not changed'
REMOVED = u'removed'
class NodeGeneratorBase(object):
@@ -200,24 +202,31 @@ class Node(object):
Returns ``True`` if node's kind is ``NodeKind.DIR``, ``False``
otherwise.
return self.kind == NodeKind.DIR
def is_root(self):
Returns ``True`` if node is a root node and ``False`` otherwise.
return self.kind == NodeKind.DIR and self.path == ''
def is_submodule(self):
Returns ``True`` if node's kind is ``NodeKind.SUBMODULE``, ``False``
return self.kind == NodeKind.SUBMODULE
@LazyProperty
def added(self):
return self.state is NodeState.ADDED
def changed(self):
return self.state is NodeState.CHANGED
def not_changed(self):
return self.state is NodeState.NOT_CHANGED
@@ -552,12 +561,40 @@ class DirNode(Node):
class RootNode(DirNode):
DirNode being the root node of the repository.
def __init__(self, nodes=(), changeset=None):
super(RootNode, self).__init__(path='', nodes=nodes,
changeset=changeset)
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':
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])
@@ -2709,24 +2709,32 @@ table.code-browser .browser-file {
}
.diffblock .diff-menu ul li a:hover{
text-decoration: none;
background-color: #EEEEEE;
table.code-browser .browser-dir {
background: url("../images/icons/folder_16.png") no-repeat scroll 3px;
height: 16px;
padding-left: 20px;
text-align: left;
table.code-browser .submodule-dir {
background: url("../images/icons/disconnect.png") no-repeat scroll 3px;
.box .search {
clear: both;
overflow: hidden;
margin: 0;
padding: 0 20px 10px;
.box .search div.search_path {
background: none repeat scroll 0 0 #EEE;
border: 1px solid #CCC;
color: blue;
margin-bottom: 10px;
@@ -61,25 +61,29 @@
</td>
<td></td>
</tr>
%endif
%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:
%if node.is_file():
${h.format_byte_size(node.size,binary=True)}
${node.mimetype}
Status change: