diff --git a/rhodecode/controllers/api/api.py b/rhodecode/controllers/api/api.py
--- a/rhodecode/controllers/api/api.py
+++ b/rhodecode/controllers/api/api.py
@@ -544,6 +544,15 @@ class ApiController(JSONRPCController):
raise JSONRPCError('failed to create repository %s' % repo_name)
@HasPermissionAnyDecorator('hg.admin')
+ def fork_repo(self, apiuser, repoid):
+ repo = RepoModel().get_repo(repoid)
+ if repo is None:
+ raise JSONRPCError('unknown repository "%s"' % (repo or repoid))
+
+ RepoModel().create_fork(form_data, cur_user)
+
+
+ @HasPermissionAnyDecorator('hg.admin')
def delete_repo(self, apiuser, repo_name):
"""
Deletes a given repository
diff --git a/rhodecode/controllers/feed.py b/rhodecode/controllers/feed.py
--- a/rhodecode/controllers/feed.py
+++ b/rhodecode/controllers/feed.py
@@ -28,11 +28,12 @@ import logging
from pylons import url, response, tmpl_context as c
from pylons.i18n.translation import _
-from rhodecode.lib.utils2 import safe_unicode
+from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
+
+from rhodecode.lib import helpers as h
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
from rhodecode.lib.base import BaseRepoController
-
-from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
+from rhodecode.lib.diffs import DiffProcessor
log = logging.getLogger(__name__)
@@ -49,31 +50,36 @@ class FeedController(BaseRepoController)
self.title = self.title = _('%s %s feed') % (c.rhodecode_name, '%s')
self.language = 'en-us'
self.ttl = "5"
- self.feed_nr = 10
+ self.feed_nr = 20
def _get_title(self, cs):
- return "R%s:%s - %s" % (
- cs.revision, cs.short_id, cs.message
+ return "%s" % (
+ h.shorter(cs.message, 160)
)
def __changes(self, cs):
changes = []
- a = [safe_unicode(n.path) for n in cs.added]
- if a:
- changes.append('\nA ' + '\nA '.join(a))
-
- m = [safe_unicode(n.path) for n in cs.changed]
- if m:
- changes.append('\nM ' + '\nM '.join(m))
+ diffprocessor = DiffProcessor(cs.diff())
+ stats = diffprocessor.prepare(inline_diff=False)
+ for st in stats:
+ st.update({'added': st['stats'][0],
+ 'removed': st['stats'][1]})
+ changes.append('\n %(operation)s %(filename)s '
+ '(%(added)s lines added, %(removed)s lines removed)'
+ % st)
+ return changes
- d = [safe_unicode(n.path) for n in cs.removed]
- if d:
- changes.append('\nD ' + '\nD '.join(d))
-
- changes.append('')
-
- return ''.join(changes)
+ def __get_desc(self, cs):
+ desc_msg = []
+ desc_msg.append('%s %s %s:
' % (cs.author, _('commited on'),
+ cs.date))
+ desc_msg.append('
') + desc_msg.append(cs.message) + desc_msg.append('\n') + desc_msg.extend(self.__changes(cs)) + desc_msg.append('') + return desc_msg def atom(self, repo_name): """Produce an atom-1.0 feed via feedgenerator module""" @@ -87,15 +93,13 @@ class FeedController(BaseRepoController) ) for cs in reversed(list(c.rhodecode_repo[-self.feed_nr:])): - desc_msg = [] - desc_msg.append('%s - %s
' % (cs.author, cs.date)) - desc_msg.append(self.__changes(cs)) - feed.add_item(title=self._get_title(cs), link=url('changeset_home', repo_name=repo_name, revision=cs.raw_id, qualified=True), author_name=cs.author, - description=''.join(desc_msg)) + description=''.join(self.__get_desc(cs)), + pubdate=cs.date, + ) response.content_type = feed.mime_type return feed.writeString('utf-8') @@ -112,15 +116,12 @@ class FeedController(BaseRepoController) ) for cs in reversed(list(c.rhodecode_repo[-self.feed_nr:])): - desc_msg = [] - desc_msg.append('%s - %s' % (cs.author, cs.date)) - desc_msg.append(self.__changes(cs)) - feed.add_item(title=self._get_title(cs), link=url('changeset_home', repo_name=repo_name, revision=cs.raw_id, qualified=True), author_name=cs.author, - description=''.join(desc_msg), + description=''.join(self.__get_desc(cs)), + pubdate=cs.date, ) response.content_type = feed.mime_type diff --git a/rhodecode/lib/diffs.py b/rhodecode/lib/diffs.py --- a/rhodecode/lib/diffs.py +++ b/rhodecode/lib/diffs.py @@ -128,7 +128,7 @@ class DiffProcessor(object): """ _chunk_re = re.compile(r'@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@(.*)') - def __init__(self, diff, differ='diff', format='udiff'): + def __init__(self, diff, differ='diff', format='gitdiff'): """ :param diff: a text in diff format or generator :param format: format of diff passed, `udiff` or `gitdiff` @@ -171,7 +171,7 @@ class DiffProcessor(object): def _extract_rev(self, line1, line2): """ - Extract the filename and revision hint from a line. + Extract the operation (A/M/D), filename and revision hint from a line. """ try: @@ -189,11 +189,15 @@ class DiffProcessor(object): filename = (old_filename if old_filename != '/dev/null' else new_filename) - return filename, new_rev, old_rev + operation = 'D' if new_filename == '/dev/null' else None + if not operation: + operation = 'M' if old_filename != '/dev/null' else 'A' + + return operation, filename, new_rev, old_rev except (ValueError, IndexError): pass - return None, None, None + return None, None, None, None def _parse_gitdiff(self, diffiterator): def line_decoder(l): @@ -278,7 +282,7 @@ class DiffProcessor(object): do(line) do(next_) - def _parse_udiff(self): + def _parse_udiff(self, inline_diff=True): """ Parse the diff an return data for the template. """ @@ -293,13 +297,16 @@ class DiffProcessor(object): continue chunks = [] - filename, old_rev, new_rev = \ + stats = [0, 0] + operation, filename, old_rev, new_rev = \ self._extract_rev(line, lineiter.next()) files.append({ 'filename': filename, 'old_revision': old_rev, 'new_revision': new_rev, - 'chunks': chunks + 'chunks': chunks, + 'operation': operation, + 'stats': stats, }) line = lineiter.next() @@ -331,7 +338,6 @@ class DiffProcessor(object): }) line = lineiter.next() - while old_line < old_end or new_line < new_end: if line: command, line = line[0], line[1:] @@ -345,9 +351,11 @@ class DiffProcessor(object): elif command == '+': affects_new = True action = 'add' + stats[0] += 1 elif command == '-': affects_old = True action = 'del' + stats[1] += 1 else: affects_old = affects_new = True action = 'unmod' @@ -371,13 +379,16 @@ class DiffProcessor(object): }) line = lineiter.next() - except StopIteration: pass + sorter = lambda info: {'A': 0, 'M': 1, 'D': 2}.get(info['operation']) + if inline_diff is False: + return sorted(files, key=sorter) + # highlight inline changes - for _ in files: - for chunk in chunks: + for diff_data in files: + for chunk in diff_data['chunks']: lineiter = iter(chunk) try: while 1: @@ -391,14 +402,14 @@ class DiffProcessor(object): except StopIteration: pass - return files + return sorted(files, key=sorter) - def prepare(self): + def prepare(self, inline_diff=True): """ Prepare the passed udiff for HTML rendering. It'l return a list of dicts """ - return self._parse_udiff() + return self._parse_udiff(inline_diff=inline_diff) def _safe_id(self, idstring): """Make a string safe for including in an id attribute. @@ -432,7 +443,7 @@ class DiffProcessor(object): def as_html(self, table_class='code-difftable', line_class='line', new_lineno_class='lineno old', old_lineno_class='lineno new', - code_class='code', enable_comments=False): + code_class='code', enable_comments=False, diff_lines=None): """ Return udiff as html table with customized css classes """ @@ -448,7 +459,8 @@ class DiffProcessor(object): } else: return label - diff_lines = self.prepare() + if diff_lines is None: + diff_lines = self.prepare() _html_empty = True _html = [] _html.append('''