Changeset - a520d542697e
[Not reviewed]
beta
0 9 0
Marcin Kuzminski - 13 years ago 2012-11-28 01:27:21
marcin@python-works.com
Implemented file history page for showing detailed changelog for a given file
- fixed git detection of filenode history when executed on directory
- shortlog uses urlify_commit function now
9 files changed with 125 insertions and 14 deletions:
0 comments (0 inline, 0 general)
rhodecode/config/routing.py
Show inline comments
 
@@ -511,12 +511,16 @@ def make_map(config):
 
    rmap.connect('summary_home', '/{repo_name:.*?}/summary',
 
                controller='summary', conditions=dict(function=check_repo))
 

	
 
    rmap.connect('shortlog_home', '/{repo_name:.*?}/shortlog',
 
                controller='shortlog', conditions=dict(function=check_repo))
 

	
 
    rmap.connect('shortlog_file_home', '/{repo_name:.*?}/shortlog/{revision}/{f_path:.*}',
 
                controller='shortlog', f_path=None,
 
                conditions=dict(function=check_repo))
 

	
 
    rmap.connect('branches_home', '/{repo_name:.*?}/branches',
 
                controller='branches', conditions=dict(function=check_repo))
 

	
 
    rmap.connect('tags_home', '/{repo_name:.*?}/tags',
 
                controller='tags', conditions=dict(function=check_repo))
 

	
rhodecode/controllers/shortlog.py
Show inline comments
 
@@ -23,43 +23,81 @@
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 

	
 
import logging
 

	
 
from pylons import tmpl_context as c, request, url
 
from pylons.i18n.translation import _
 

	
 
from rhodecode.lib import helpers as h
 
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
 
from rhodecode.lib.base import BaseRepoController, render
 
from rhodecode.lib.helpers import RepoPage
 
from pylons.controllers.util import redirect
 
from rhodecode.lib.utils2 import safe_int
 
from rhodecode.lib.vcs.exceptions import NodeDoesNotExistError, ChangesetError,\
 
    RepositoryError
 

	
 
log = logging.getLogger(__name__)
 

	
 

	
 
class ShortlogController(BaseRepoController):
 

	
 
    @LoginRequired()
 
    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
 
                                   'repository.admin')
 
    def __before__(self):
 
        super(ShortlogController, self).__before__()
 

	
 
    def index(self, repo_name):
 
    def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
 
        """
 
        Safe way to get changeset if error occur it redirects to tip with
 
        proper message
 

	
 
        :param rev: revision to fetch
 
        :param repo_name: repo name to redirect after
 
        """
 

	
 
        try:
 
            return c.rhodecode_repo.get_changeset(rev)
 
        except RepositoryError, e:
 
            h.flash(str(e), category='warning')
 
            redirect(h.url('shortlog_home', repo_name=repo_name))
 

	
 
    def index(self, repo_name, revision=None, f_path=None):
 
        p = safe_int(request.params.get('page', 1), 1)
 
        size = safe_int(request.params.get('size', 20), 20)
 

	
 
        def url_generator(**kw):
 
            return url('shortlog_home', repo_name=repo_name, size=size, **kw)
 

	
 
        c.repo_changesets = RepoPage(c.rhodecode_repo, page=p,
 
        collection = c.rhodecode_repo
 
        c.file_history = f_path
 
        if f_path:
 
            f_path = f_path.lstrip('/')
 
            # get the history for the file !
 
            tip_cs = c.rhodecode_repo.get_changeset()
 
            try:
 
                collection = tip_cs.get_file_history(f_path)
 
            except (NodeDoesNotExistError, ChangesetError):
 
                #this node is not present at tip !
 
                try:
 
                    cs = self.__get_cs_or_redirect(revision, repo_name)
 
                    collection = cs.get_file_history(f_path)
 
                except RepositoryError, e:
 
                    h.flash(str(e), category='warning')
 
                    redirect(h.url('shortlog_home', repo_name=repo_name))
 
            collection = list(reversed(collection))
 

	
 
        c.repo_changesets = RepoPage(collection, page=p,
 
                                    items_per_page=size, url=url_generator)
 
        page_revisions = [x.raw_id for x in list(c.repo_changesets)]
 
        page_revisions = [x.raw_id for x in list(collection)]
 
        c.statuses = c.rhodecode_db_repo.statuses(page_revisions)
 

	
 
        if not c.repo_changesets:
 
            h.flash(_('There are no changesets yet'), category='warning')
 
            return redirect(url('summary_home', repo_name=repo_name))
 

	
 
        c.shortlog_data = render('shortlog/shortlog_data.html')
 
        if request.environ.get('HTTP_X_PARTIAL_XHR'):
 
            return c.shortlog_data
 
        r = render('shortlog/shortlog.html')
rhodecode/lib/vcs/backends/git/changeset.py
Show inline comments
 
@@ -159,12 +159,19 @@ class GitChangeset(BaseChangeset):
 
        obj = self.repository._repo[self._get_id_for_path(path)]
 
        if isinstance(obj, objects.Blob):
 
            return NodeKind.FILE
 
        elif isinstance(obj, objects.Tree):
 
            return NodeKind.DIR
 

	
 
    def _get_filectx(self, path):
 
        path = self._fix_path(path)
 
        if self._get_kind(path) != NodeKind.FILE:
 
            raise ChangesetError("File does not exist for revision %r at "
 
                " %r" % (self.raw_id, path))
 
        return path
 

	
 
    def _get_file_nodes(self):
 
        return chain(*(t[2] for t in self.walk()))
 

	
 
    @LazyProperty
 
    def parents(self):
 
        """
 
@@ -261,12 +268,13 @@ class GitChangeset(BaseChangeset):
 
        which file at given ``path`` has been modified.
 

	
 
        TODO: This function now uses os underlying 'git' and 'grep' commands
 
        which is generally not good. Should be replaced with algorithm
 
        iterating commits.
 
        """
 
        self._get_filectx(path)
 
        cmd = 'log --pretty="format: %%H" -s -p %s -- "%s"' % (
 
                  self.id, path
 
               )
 
        so, se = self.repository.run_git_command(cmd)
 
        ids = re.findall(r'[0-9a-fA-F]{40}', so)
 
        return [self.repository.get_changeset(id) for id in ids]
rhodecode/lib/vcs/backends/hg/changeset.py
Show inline comments
 
@@ -178,13 +178,13 @@ class MercurialChangeset(BaseChangeset):
 
                % (path))
 

	
 
    def _get_filectx(self, path):
 
        path = self._fix_path(path)
 
        if self._get_kind(path) != NodeKind.FILE:
 
            raise ChangesetError("File does not exist for revision %r at "
 
                " %r" % (self.revision, path))
 
                " %r" % (self.raw_id, path))
 
        return self._ctx.filectx(path)
 

	
 
    def _extract_submodules(self):
 
        """
 
        returns a dictionary with submodule information from substate file
 
        of hg repository
rhodecode/public/css/style.css
Show inline comments
 
@@ -2647,21 +2647,20 @@ h3.files_location {
 
	white-space: pre-wrap;
 
}
 
 
#graph_content .container .mid .message a:hover{
 
	text-decoration: none;
 
}
 
#content #graph_content .message .revision-link,
 
#changeset_content .container .message .revision-link
 
 
.revision-link
 
 {
 
	color:#3F6F9F;
 
    font-weight: bold !important;
 
}
 
 
#content #graph_content .message .issue-tracker-link,
 
#changeset_content .container .message .issue-tracker-link{
 
.issue-tracker-link{
 
    color:#3F6F9F;
 
    font-weight: bold !important;
 
}
 
 
.changeset-status-container{
 
    padding-right: 5px;
rhodecode/templates/files/files_history_box.html
Show inline comments
 
@@ -5,12 +5,13 @@
 
            <div style="float:left">
 
            ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
 
            ${h.hidden('diff2',c.file_changeset.raw_id)}
 
            ${h.select('diff1',c.file_changeset.raw_id,c.file_history)}
 
            ${h.submit('diff',_('diff to revision'),class_="ui-btn")}
 
            ${h.submit('show_rev',_('show at revision'),class_="ui-btn")}
 
            ${h.link_to(_('show full history'),h.url('shortlog_file_home',repo_name=c.repo_name, revision=c.file_changeset.raw_id, f_path=c.f_path),class_="ui-btn")}
 
            ${h.hidden('annotate', c.annotate)}
 
            ${h.end_form()}
 
            </div>
 
            <div class="file_author">
 
                <div class="item">${h.literal(ungettext(u'%s author',u'%s authors',len(c.authors)) % ('<b>%s</b>' % len(c.authors))) }</div>
 
                %for email, user in c.authors:
rhodecode/templates/shortlog/shortlog.html
Show inline comments
 
@@ -8,13 +8,19 @@
 

	
 
<%def name="breadcrumbs_links()">
 
    ${h.link_to(_(u'Home'),h.url('/'))}
 
    &raquo;
 
    ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
 
    &raquo;
 
    %if c.file_history:
 
        ${h.link_to(_('shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}
 
        &raquo;
 
        ${c.file_history}
 
    %else:
 
    ${_('shortlog')}
 
    %endif
 
</%def>
 

	
 
<%def name="page_nav()">
 
    ${self.menu('shortlog')}
 
</%def>
 
<%def name="main()">
rhodecode/templates/shortlog/shortlog_data.html
Show inline comments
 
@@ -27,15 +27,13 @@
 
              %endif
 
            </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>
 
        </td>
 
        <td>
 
            ${h.link_to(h.truncate(cs.message,50) or _('No commit message'),
 
            h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id),
 
            title=cs.message)}
 
            ${h.urlify_commit(h.truncate(cs.message,50),c.repo_name, h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}
 
        </td>
 
        <td><span class="tooltip" title="${h.tooltip(h.fmt_date(cs.date))}">
 
                      ${h.age(cs.date)}</span>
 
        </td>
 
		<td title="${cs.author}">${h.person(cs.author)}</td>
 
		<td>
rhodecode/tests/functional/test_shortlog.py
Show inline comments
 
from rhodecode.tests import *
 

	
 

	
 
class TestShortlogController(TestController):
 

	
 
    def test_index(self):
 
    def test_index_hg(self):
 
        self.log_user()
 
        response = self.app.get(url(controller='shortlog', action='index',
 
                                    repo_name=HG_REPO))
 
        # Test response...
 

	
 
    def test_index_git(self):
 
        self.log_user()
 
        response = self.app.get(url(controller='shortlog', action='index',
 
                                    repo_name=GIT_REPO))
 
        # Test response...
 

	
 
    def test_index_hg_with_filenode(self):
 
        self.log_user()
 
        response = self.app.get(url(controller='shortlog', action='index',
 
                                    revision='tip', f_path='/vcs/exceptions.py',
 
                                    repo_name=HG_REPO))
 
        #history commits messages
 
        response.mustcontain('Added exceptions module, this time for real')
 
        response.mustcontain('Added not implemented hg backend test case')
 
        response.mustcontain('Added BaseChangeset class')
 
        # Test response...
 

	
 
    def test_index_git_with_filenode(self):
 
        self.log_user()
 
        response = self.app.get(url(controller='shortlog', action='index',repo_name=HG_REPO))
 
        # Test response...
 
        response = self.app.get(url(controller='shortlog', action='index',
 
                                    revision='tip', f_path='/vcs/exceptions.py',
 
                                    repo_name=GIT_REPO))
 
        #history commits messages
 
        response.mustcontain('Added exceptions module, this time for real')
 
        response.mustcontain('Added not implemented hg backend test case')
 
        response.mustcontain('Added BaseChangeset class')
 

	
 
    def test_index_hg_with_filenode_that_is_dirnode(self):
 
        self.log_user()
 
        response = self.app.get(url(controller='shortlog', action='index',
 
                                    revision='tip', f_path='/tests',
 
                                    repo_name=HG_REPO))
 
        self.assertEqual(response.status, '302 Found')
 

	
 
    def test_index_git_with_filenode_that_is_dirnode(self):
 
        self.log_user()
 
        response = self.app.get(url(controller='shortlog', action='index',
 
                                    revision='tip', f_path='/tests',
 
                                    repo_name=GIT_REPO))
 
        self.assertEqual(response.status, '302 Found')
 

	
 
    def test_index_hg_with_filenode_not_existing(self):
 
        self.log_user()
 
        response = self.app.get(url(controller='shortlog', action='index',
 
                                    revision='tip', f_path='/wrong_path',
 
                                    repo_name=HG_REPO))
 
        self.assertEqual(response.status, '302 Found')
 

	
 
    def test_index_git_with_filenode_not_existing(self):
 
        self.log_user()
 
        response = self.app.get(url(controller='shortlog', action='index',
 
                                    revision='tip', f_path='/wrong_path',
 
                                    repo_name=GIT_REPO))
 
        self.assertEqual(response.status, '302 Found')
0 comments (0 inline, 0 general)