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('''