@@ -46,6 +46,7 @@ from rhodecode.lib import diffs
from rhodecode.model.db import ChangesetComment
from rhodecode.model.comment import ChangesetCommentsModel
from rhodecode.model.meta import Session
from rhodecode.lib.diffs import wrapped_diff
log = logging.getLogger(__name__)
@@ -145,15 +146,6 @@ def _context_url(fileid=None):
return h.link_to(lbl, h.url.current(**params))
def wrap_to_table(str_):
return '''<table class="code-difftable">
<tr class="line no-comment">
<td class="lineno new"></td>
<td class="code no-comment"><pre>%s</pre></td>
</tr>
</table>''' % str_
class ChangesetController(BaseRepoController):
@LoginRequired()
@@ -192,10 +184,11 @@ class ChangesetController(BaseRepoContro
return redirect(url('home'))
c.changes = OrderedDict()
c.sum_added = 0
c.sum_removed = 0
c.lines_added = 0
c.lines_deleted = 0
c.lines_added = 0 # count of lines added
c.lines_deleted = 0 # count of lines removes
cumulative_diff = 0
c.cut_off = False # defines if cut off limit is reached
c.comments = []
@@ -220,37 +213,19 @@ class ChangesetController(BaseRepoContro
# ADDED FILES
#==================================================================
for node in changeset.added:
filenode_old = FileNode(node.path, '', EmptyChangeset())
if filenode_old.is_binary or node.is_binary:
diff = wrap_to_table(_('binary file'))
st = (0, 0)
else:
# in this case node.size is good parameter since those are
# added nodes and their size defines how many changes were
# made
c.sum_added += node.size
fid = h.FID(revision, node.path)
line_context_lcl = get_line_ctx(fid, request.GET)
ignore_whitespace_lcl = get_ignore_ws(fid, request.GET)
if c.sum_added < self.cut_off_limit:
f_gitdiff = diffs.get_gitdiff(filenode_old, node,
ignore_whitespace=ignore_whitespace_lcl,
context=line_context_lcl)
d = diffs.DiffProcessor(f_gitdiff, format='gitdiff')
st = d.stat()
diff = d.as_html(enable_comments=enable_comments)
diff = wrap_to_table(_('Changeset is to big and '
'was cut off, see raw '
'changeset instead'))
c.cut_off = True
break
cs1 = None
cs2 = node.last_changeset.raw_id
ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
lim = self.cut_off_limit
if cumulative_diff > self.cut_off_limit:
lim = -1
size, cs1, cs2, diff, st = wrapped_diff(filenode_old=None,
filenode_new=node,
cut_off_limit=lim,
ignore_whitespace=ign_whitespace_lcl,
line_context=line_context_lcl,
enable_comments=enable_comments)
cumulative_diff += size
c.lines_added += st[0]
c.lines_deleted += st[1]
c.changes[changeset.raw_id].append(('added', node, diff,
@@ -259,60 +234,37 @@ class ChangesetController(BaseRepoContro
# CHANGED FILES
if not c.cut_off:
for node in changeset.changed:
try:
filenode_old = changeset_parent.get_node(node.path)
except ChangesetError:
log.warning('Unable to fetch parent node for diff')
filenode_old = FileNode(node.path, '',
EmptyChangeset())
if c.sum_removed < self.cut_off_limit:
ignore_whitespace_lcl = get_ignore_ws(fid, request.GET,)
d = diffs.DiffProcessor(f_gitdiff,
format='gitdiff')
if (st[0] + st[1]) * 256 > self.cut_off_limit:
diff = wrap_to_table(_('Diff is to big '
'and was cut off, see '
'raw diff instead'))
if diff:
c.sum_removed += len(diff)
cs1 = filenode_old.last_changeset.raw_id
c.changes[changeset.raw_id].append(('changed', node, diff,
cs1, cs2, st))
size, cs1, cs2, diff, st = wrapped_diff(filenode_old=filenode_old,
# REMOVED FILES
for node in changeset.removed:
c.changes[changeset.raw_id].append(('removed', node, None,
None, None, (0, 0)))
# count inline comments
for path, lines in c.inline_comments:
@@ -27,19 +27,18 @@ import os
import logging
import traceback
from os.path import join as jn
from pylons import request, response, session, tmpl_context as c, url
from pylons import request, response, tmpl_context as c, url
from pylons.i18n.translation import _
from pylons.controllers.util import redirect
from pylons.decorators import jsonify
from vcs.conf import settings
from vcs.exceptions import RepositoryError, ChangesetDoesNotExistError, \
EmptyRepositoryError, ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError
from vcs.nodes import FileNode, NodeKind
EmptyRepositoryError, ImproperArchiveTypeError, VCSError, \
NodeAlreadyExistsError
from vcs.nodes import FileNode
from rhodecode.lib.compat import OrderedDict
from rhodecode.lib import convert_line_endings, detect_mode, safe_str
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
from rhodecode.lib.base import BaseRepoController, render
@@ -47,6 +46,9 @@ from rhodecode.lib.utils import EmptyCha
from rhodecode.lib import diffs
import rhodecode.lib.helpers as h
from rhodecode.model.repo import RepoModel
from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\
_context_url, get_line_ctx, get_ignore_ws
@@ -105,7 +107,6 @@ class FilesController(BaseRepoController
return file_node
def __get_paths(self, changeset, starting_path):
"""recursive walk in root dir and return a set of all path in that dir
based on repository walk function
@@ -128,7 +129,7 @@ class FilesController(BaseRepoController
@HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
'repository.admin')
def index(self, repo_name, revision, f_path):
#reditect to given revision from form if given
# redirect to given revision from form if given
post_revision = request.POST.get('at_rev', None)
if post_revision:
cs = self.__get_cs_or_redirect(post_revision, repo_name)
@@ -141,7 +142,7 @@ class FilesController(BaseRepoController
cur_rev = c.changeset.revision
#prev link
# prev link
prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
c.url_prev = url('files_home', repo_name=c.repo_name,
@@ -151,7 +152,7 @@ class FilesController(BaseRepoController
except (ChangesetDoesNotExistError, VCSError):
c.url_prev = '#'
#next link
# next link
next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
c.url_next = url('files_home', repo_name=c.repo_name,
@@ -161,7 +162,7 @@ class FilesController(BaseRepoController
c.url_next = '#'
#files or dirs
# files or dirs
c.file = c.changeset.get_node(f_path)
@@ -407,13 +408,17 @@ class FilesController(BaseRepoController
def diff(self, repo_name, f_path):
ignore_whitespace = request.GET.get('ignorews') == '1'
line_context = request.GET.get('context', 3)
diff1 = request.GET.get('diff1')
diff2 = request.GET.get('diff2')
diff1 = request.GET.get('diff1', '')
diff2 = request.GET.get('diff2', '')
c.action = request.GET.get('diff')
c.no_changes = diff1 == diff2
c.f_path = f_path
c.big_diff = False
c.anchor_url = anchor_url
c.ignorews_url = _ignorews_url
c.context_url = _context_url
c.changes[diff2] = []
if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
@@ -429,14 +434,14 @@ class FilesController(BaseRepoController
c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
node2 = FileNode('.', '', changeset=c.changeset_2)
except RepositoryError:
return redirect(url('files_home',
repo_name=c.repo_name, f_path=f_path))
return redirect(url('files_home', repo_name=c.repo_name,
f_path=f_path))
if c.action == 'download':
_diff = diffs.get_gitdiff(node1, node2,
ignore_whitespace=ignore_whitespace,
context=line_context)
diff = diffs.DiffProcessor(_diff,format='gitdiff')
diff = diffs.DiffProcessor(_diff, format='gitdiff')
diff_name = '%s_vs_%s.diff' % (diff1, diff2)
response.content_type = 'text/plain'
@@ -448,42 +453,25 @@ class FilesController(BaseRepoController
return diff.raw_diff()
elif c.action == 'diff':
if node1.is_binary or node2.is_binary:
c.cur_diff = _('Binary file')
elif node1.size > self.cut_off_limit or \
node2.size > self.cut_off_limit:
c.cur_diff = ''
c.big_diff = True
c.cur_diff = diff.as_html()
fid = h.FID(diff2, node2.path)
#default option
lim = request.GET.get('fulldiff') or self.cut_off_limit
_, cs1, cs2, diff, st = wrapped_diff(filenode_old=node1,
filenode_new=node2,
enable_comments=False)
c.changes = [('', node2, diff, cs1, cs2, st,)]
if not c.cur_diff and not c.big_diff:
c.no_changes = True
return render('files/file_diff.html')
def _get_node_history(self, cs, f_path):
@@ -501,12 +489,10 @@ class FilesController(BaseRepoController
hist_l.append(changesets_group)
for name, chs in c.rhodecode_repo.branches.items():
#chs = chs.split(':')[-1]
branches_group[0].append((chs, name),)
hist_l.append(branches_group)
for name, chs in c.rhodecode_repo.tags.items():
tags_group[0].append((chs, name),)
hist_l.append(tags_group)
@@ -27,12 +27,66 @@
import re
import difflib
import markupsafe
from itertools import tee, imap
from vcs.exceptions import VCSError
from rhodecode.lib.utils import EmptyChangeset
def wrapped_diff(filenode_old, filenode_new, cut_off_limit=None,
ignore_whitespace=True, line_context=3,
enable_comments=False):
"""
returns a wrapped diff into a table, checks for cut_off_limit and presents
proper message
if filenode_old is None:
filenode_old = FileNode(filenode_new.path, '', EmptyChangeset())
if filenode_old.is_binary or filenode_new.is_binary:
stats = (0, 0)
size = 0
elif cut_off_limit != -1 and (cut_off_limit is None or
(filenode_old.size < cut_off_limit and filenode_new.size < cut_off_limit)):
f_gitdiff = get_gitdiff(filenode_old, filenode_new,
diff_processor = DiffProcessor(f_gitdiff, format='gitdiff')
diff = diff_processor.as_html(enable_comments=enable_comments)
stats = diff_processor.stat()
size = len(diff or '')
diff = wrap_to_table(_('Changeset was to big and was cut off, use '
'diff menu to display this diff'))
if not diff:
diff = wrap_to_table(_('No changes detected'))
cs2 = filenode_new.last_changeset.raw_id
return size, cs1, cs2, diff, stats
def get_gitdiff(filenode_old, filenode_new, ignore_whitespace=True, context=3):
@@ -263,8 +317,8 @@ class DiffProcessor(object):
lines.append({
'old_lineno': '...',
'new_lineno': '...',
'action': 'context',
'line': line,
})
skipfirst = False
@@ -371,34 +425,40 @@ class DiffProcessor(object):
if condition:
return '''<a href="%(url)s">%(label)s</a>''' % {'url': url,
'label': label}
return '''<a href="%(url)s">%(label)s</a>''' % {
'url': url,
'label': label
}
return label
diff_lines = self.prepare()
_html_empty = True
_html = []
_html.append('''<table class="%(table_class)s">\n''' \
% {'table_class': table_class})
_html.append('''<table class="%(table_class)s">\n''' % {
'table_class': table_class
for diff in diff_lines:
for line in diff['chunks']:
_html_empty = False
for change in line:
_html.append('''<tr class="%(line_class)s %(action)s">\n''' \
% {'line_class': line_class,
'action': change['action']})
_html.append('''<tr class="%(lc)s %(action)s">\n''' % {
'lc': line_class,
'action': change['action']
anchor_old_id = ''
anchor_new_id = ''
anchor_old = "%(filename)s_o%(oldline_no)s" % \
{'filename': self._safe_id(diff['filename']),
'oldline_no': change['old_lineno']}
anchor_new = "%(filename)s_n%(oldline_no)s" % \
'oldline_no': change['new_lineno']}
cond_old = change['old_lineno'] != '...' and \
change['old_lineno']
cond_new = change['new_lineno'] != '...' and \
change['new_lineno']
anchor_old = "%(filename)s_o%(oldline_no)s" % {
'filename': self._safe_id(diff['filename']),
'oldline_no': change['old_lineno']
anchor_new = "%(filename)s_n%(oldline_no)s" % {
'oldline_no': change['new_lineno']
cond_old = (change['old_lineno'] != '...' and
change['old_lineno'])
cond_new = (change['new_lineno'] != '...' and
change['new_lineno'])
if cond_old:
anchor_old_id = 'id="%s"' % anchor_old
if cond_new:
@@ -406,37 +466,41 @@ class DiffProcessor(object):
###########################################################
# OLD LINE NUMBER
_html.append('''\t<td %(a_id)s class="%(old_lineno_cls)s">''' \
% {'a_id': anchor_old_id,
'old_lineno_cls': old_lineno_class})
_html.append('''\t<td %(a_id)s class="%(olc)s">''' % {
'a_id': anchor_old_id,
'olc': old_lineno_class
_html.append('''%(link)s''' \
% {'link':
_link_to_if(cond_old, change['old_lineno'], '#%s' \
% anchor_old)})
_html.append('''%(link)s''' % {
'link': _link_to_if(True, change['old_lineno'],
'#%s' % anchor_old)
_html.append('''</td>\n''')
# NEW LINE NUMBER
_html.append('''\t<td %(a_id)s class="%(new_lineno_cls)s">''' \
% {'a_id': anchor_new_id,
'new_lineno_cls': new_lineno_class})
_html.append('''\t<td %(a_id)s class="%(nlc)s">''' % {
'a_id': anchor_new_id,
'nlc': new_lineno_class
_link_to_if(cond_new, change['new_lineno'], '#%s' \
% anchor_new)})
'link': _link_to_if(True, change['new_lineno'],
'#%s' % anchor_new)
# CODE
comments = '' if enable_comments else 'no-comment'
_html.append('''\t<td class="%(code_class)s %(in-comments)s">''' \
% {'code_class': code_class,
'in-comments': comments})
_html.append('''\n\t\t<pre>%(code)s</pre>\n''' \
% {'code': change['line']})
_html.append('''\t<td class="%(cc)s %(inc)s">''' % {
'cc': code_class,
'inc': comments
_html.append('''\n\t\t<pre>%(code)s</pre>\n''' % {
'code': change['line']
_html.append('''\t</td>''')
_html.append('''\n</tr>\n''')
_html.append('''</table>''')
@@ -40,6 +40,7 @@ from rhodecode.lib.utils import repo_nam
from rhodecode.lib import str2bool, safe_unicode, safe_str, get_changeset_safe
from rhodecode.lib.markup_renderer import MarkupRenderer
def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
Reset button
@@ -52,11 +53,11 @@ def _reset(name, value=None, id=NotGiven
reset = _reset
safeid = _make_safe_id_component
def FID(raw_id,path):
def FID(raw_id, path):
Creates a uniqe ID for filenode based on it's path and revision
:param raw_id:
:param path:
@@ -116,7 +117,7 @@ class _FilesBreadCrumbs(object):
paths_l = paths.split('/')
for cnt, p in enumerate(paths_l):
if p != '':
url_l.append(link_to(p,
url('files_home',
repo_name=repo_name,
revision=rev,
@@ -738,14 +739,14 @@ def urlify_text(text):
def rst(source):
return literal('<div class="rst-block">%s</div>' %
MarkupRenderer.rst(source))
def rst_w_mentions(source):
Wrapped rst renderer with @mention highlighting
:param source:
MarkupRenderer.rst_with_mentions(source))
@@ -4,7 +4,7 @@
~~~~~~~~~~~~~~~~~~~~~~~
comments model for RhodeCode
:created_on: Nov 11, 2011
:author: marcink
:copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
@@ -55,7 +55,7 @@ class ChangesetCommentsModel(BaseModel):
line_no=None):
Creates new comment for changeset
:param text:
:param repo_id:
:param user_id:
@@ -84,7 +84,7 @@ class ChangesetCommentsModel(BaseModel):
if line_no:
line = _('on line %s') % line_no
subj = h.link_to('Re commit: %(commit_desc)s %(line)s' % \
{'commit_desc':desc, 'line':line},
{'commit_desc': desc, 'line': line},
h.url('changeset_home', repo_name=repo.repo_name,
revision=revision,
anchor='comment-%s' % comment.comment_id,
@@ -114,7 +114,7 @@ class ChangesetCommentsModel(BaseModel):
def delete(self, comment):
Deletes given comment
:param comment_id:
comment = self.__get_changeset_comment(comment)
@@ -122,7 +122,6 @@ class ChangesetCommentsModel(BaseModel):
return comment
def get_comments(self, repo_id, revision):
return ChangesetComment.query()\
.filter(ChangesetComment.repo_id == repo_id)\
@@ -137,7 +136,7 @@ class ChangesetCommentsModel(BaseModel):
.filter(ChangesetComment.line_no != None)\
.filter(ChangesetComment.f_path != None).all()
paths = defaultdict(lambda:defaultdict(list))
paths = defaultdict(lambda: defaultdict(list))
for co in comments:
paths[co.f_path][co.line_no].append(co)
@@ -19,7 +19,7 @@
<img class="diff-menu-activate" style="margin-bottom:-6px;cursor: pointer" alt="diff-menu" src="${h.url('/images/icons/script_gear.png')}" />
<div class="diff-menu" style="display:none">
<ul>
<li>${h.link_to(_('diff'),h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='diff'))}</li>
<li>${h.link_to(_('diff'),h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='diff',fulldiff=1))}</li>
<li>${h.link_to(_('raw diff'),h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='raw'))}</li>
<li>${h.link_to(_('download diff'),h.url('files_diff_home',repo_name=c.repo_name,f_path=h.safe_unicode(filenode.path),diff2=cs2,diff1=cs1,diff='download'))}</li>
<li>${c.ignorews_url(h.FID(filenode.changeset.raw_id,filenode.path))}</li>
@@ -37,11 +37,7 @@
</div>
<div class="code-body">
<div class="full_f_path" path="${h.safe_unicode(filenode.path)}"></div>
%if diff:
${diff|n}
%else:
${_('No changes in this file')}
%endif
@@ -21,33 +21,29 @@
<div class="title">
${self.breadcrumbs()}
<div class="table">
<div id="body" class="diffblock">
<div class="code-header">
<div class="changeset_header">
<span class="changeset_file">${h.link_to(c.f_path,h.url('files_home',repo_name=c.repo_name,
revision=c.changeset_2.raw_id,f_path=c.f_path))}</span>
» <span>${h.link_to(_('diff'),
h.url.current(diff2=c.changeset_2.raw_id,diff1=c.changeset_1.raw_id,diff='diff'))}</span>
» <span>${h.link_to(_('raw diff'),
h.url.current(diff2=c.changeset_2.raw_id,diff1=c.changeset_1.raw_id,diff='raw'))}</span>
» <span>${h.link_to(_('download diff'),
h.url.current(diff2=c.changeset_2.raw_id,diff1=c.changeset_1.raw_id,diff='download'))}</span>
%if c.no_changes:
${_('No changes')}
%elif c.big_diff:
${_('Diff is to big to display')} ${h.link_to(_('raw diff'),
h.url.current(diff2=c.changeset_2.raw_id,diff1=c.changeset_1.raw_id,diff='raw'))}
${c.cur_diff|n}
<div>
## diff block
<%namespace name="diff_block" file="/changeset/diff_block.html"/>
${diff_block.diff_block(c.changes)}
<script>
YUE.onDOMReady(function(){
YUE.on(YUQ('.diff-menu-activate'),'click',function(e){
var act = e.currentTarget.nextElementSibling;
if(YUD.hasClass(act,'active')){
YUD.removeClass(act,'active');
YUD.setStyle(act,'display','none');
}else{
YUD.addClass(act,'active');
YUD.setStyle(act,'display','');
});
</script>
</%def>
\ No newline at end of file
@@ -59,7 +59,7 @@
<div class="commit">${_('Editing file')}: ${c.file.path}</div>
<pre id="editor_pre"></pre>
<textarea id="editor" name="content" style="display:none">${c.file.content|n}</textarea>
<textarea id="editor" name="content" style="display:none">${h.escape(c.file.content)|n}</textarea>
<div style="padding: 10px;color:#666666">${_('commit message')}</div>
<textarea id="commit" name="message" style="height: 60px;width: 99%;margin-left:4px"></textarea>
@@ -11,7 +11,6 @@
</dd>
</dl>
<div id="body" class="codeblock">
Status change: