.. _changelog:
Changelog
=========
1.1.0 (**2010-XX-XX**)
1.2.0 (**2010-12-18**)
----------------------
:status: in-progress
:branch: beta
news
++++
- implemented #91 added nicer looking archive urls
fixes
1.1.0 (**2010-12-18**)
- rewrite of internals for vcs >=0.1.10
- uses mercurial 1.7 with dotencode disabled for maintaining compatibility
with older clients
- anonymous access, authentication via ldap
- performance upgrade for cached repos list - each repository has it's own
cache that's invalidated when needed.
- performance upgrades on repositories with large amount of commits (20K+)
- main page quick filter for filtering repositories
- user dashboards with ability to follow chosen repositories actions
- sends email to admin on new user registration
- added cache/statistics reset options into repository settings
- more detailed action logger (based on hooks) with pushed changesets lists
and options to disable those hooks from admin panel
- introduced new enhanced changelog for merges that shows more accurate results
- new improved and faster code stats (based on pygments lexers mapping tables,
showing up to 10 trending sources for each repository. Additionally stats
can be disabled in repository settings.
- gui optimizations, fixed application width to 1024px
- added cut off (for large files/changesets) limit into config files
- whoosh, celeryd, upgrade moved to paster command
- other than sqlite database backends can be used
+++++
@@ -96,26 +108,25 @@ fixes
- templating and css corrections, fixed repo switcher on chrome, updated titles
- admin menu accessible from options menu on repository view
- permissions cached queries
1.0.0rc4 (**2010-10-12**)
--------------------------
- fixed python2.5 missing simplejson imports (thanks to Jens Bäckman)
- removed cache_manager settings from sqlalchemy meta
- added sqlalchemy cache settings to ini files
- validated password length and added second try of failure on paster setup-app
- fixed setup database destroy prompt even when there was no db
1.0.0rc3 (**2010-10-11**)
-------------------------
- fixed i18n during installation.
1.0.0rc2 (**2010-10-11**)
- Disabled dirsize in file browser, it's causing nasty bug when dir renames
occure. After vcs is fixed it'll be put back again.
- templating/css rewrites, optimized css.
\ No newline at end of file
@@ -168,45 +168,45 @@ def make_map(config):
controller='summary', conditions=dict(function=check_repo))
map.connect('shortlog_home', '/{repo_name:.*}/shortlog',
controller='shortlog', conditions=dict(function=check_repo))
map.connect('branches_home', '/{repo_name:.*}/branches',
controller='branches', conditions=dict(function=check_repo))
map.connect('tags_home', '/{repo_name:.*}/tags',
controller='tags', conditions=dict(function=check_repo))
map.connect('changelog_home', '/{repo_name:.*}/changelog',
controller='changelog', conditions=dict(function=check_repo))
map.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
controller='files', revision='tip', f_path='',
conditions=dict(function=check_repo))
map.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
controller='files', action='diff', revision='tip', f_path='',
map.connect('files_rawfile_home', '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
controller='files', action='rawfile', revision='tip', f_path='',
map.connect('files_raw_home', '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
controller='files', action='raw', revision='tip', f_path='',
map.connect('files_annotate_home', '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
controller='files', action='annotate', revision='tip', f_path='',
map.connect('files_archive_home', '/{repo_name:.*}/archive/{revision}/{fileformat}',
controller='files', action='archivefile', revision='tip',
map.connect('files_archive_home', '/{repo_name:.*}/archive/{fname}',
controller='files', action='archivefile',
map.connect('repo_settings_delete', '/{repo_name:.*}/settings',
controller='settings', action="delete",
conditions=dict(method=["DELETE"], function=check_repo))
map.connect('repo_settings_update', '/{repo_name:.*}/settings',
controller='settings', action="update",
conditions=dict(method=["PUT"], function=check_repo))
map.connect('repo_settings_home', '/{repo_name:.*}/settings',
controller='settings', action='index',
map.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
controller='settings', action='fork_create',
conditions=dict(function=check_repo, method=["POST"]))
map.connect('repo_fork_home', '/{repo_name:.*}/fork',
controller='settings', action='fork',
return map
@@ -18,49 +18,49 @@
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
import tempfile
import logging
import rhodecode.lib.helpers as h
from mercurial import archival
from pylons import request, response, session, tmpl_context as c, url
from pylons.i18n.translation import _
from pylons.controllers.util import redirect
from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
from rhodecode.lib.base import BaseController, render
from rhodecode.lib.utils import EmptyChangeset
from rhodecode.model.scm import ScmModel
from vcs.exceptions import RepositoryError, ChangesetError
from vcs.exceptions import RepositoryError, ChangesetError, ChangesetDoesNotExistError
from vcs.nodes import FileNode
from vcs.utils import diffs as differ
log = logging.getLogger(__name__)
class FilesController(BaseController):
@LoginRequired()
@HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
'repository.admin')
def __before__(self):
super(FilesController, self).__before__()
c.cut_off_limit = self.cut_off_limit
def index(self, repo_name, revision, f_path):
hg_model = ScmModel()
c.repo = hg_model.get_repo(c.repo_name)
revision = request.POST.get('at_rev', None) or revision
def get_next_rev(cur):
max_rev = len(c.repo.revisions) - 1
r = cur + 1
if r > max_rev:
r = max_rev
@@ -112,74 +112,84 @@ class FilesController(BaseController):
file_node = c.repo.get_changeset(revision).get_node(f_path)
response.content_type = 'text/plain'
return file_node.content
def annotate(self, repo_name, revision, f_path):
try:
c.cs = c.repo.get_changeset(revision)
c.file = c.cs.get_node(f_path)
except RepositoryError, e:
h.flash(str(e), category='warning')
redirect(h.url('files_home', repo_name=repo_name, revision=revision))
c.file_history = self._get_history(c.repo, c.file, f_path)
c.f_path = f_path
return render('files/files_annotate.html')
def archivefile(self, repo_name, revision, fileformat):
def archivefile(self, repo_name, fname):
info = fname.split('.')
revision, fileformat = info[0], '.' + '.'.join(info[1:])
archive_specs = {
'.tar.bz2': ('application/x-tar', 'tbz2'),
'.tar.gz': ('application/x-tar', 'tgz'),
'.zip': ('application/zip', 'zip'),
}
if not archive_specs.has_key(fileformat):
return 'Unknown archive type %s' % fileformat
return _('Unknown archive type %s') % fileformat
repo = ScmModel().get_repo(repo_name)
repo.get_changeset(revision)
except ChangesetDoesNotExistError:
return _('Unknown revision %s') % revision
archive = tempfile.TemporaryFile()
localrepo = repo.repo
fname = '%s-%s%s' % (repo_name, revision, fileformat)
archival.archive(localrepo, archive, revision, archive_specs[fileformat][1],
prefix='%s-%s' % (repo_name, revision))
response.content_type = archive_specs[fileformat][0]
response.content_disposition = 'attachment; filename=%s' % fname
archive.seek(0)
def read_in_chunks(file_object, chunk_size=1024 * 40):
"""Lazy function (generator) to read a file piece by piece.
Default chunk size: 40k."""
while True:
data = file_object.read(chunk_size)
if not data:
break
yield data
repo = ScmModel().get_repo(repo_name).repo
archival.archive(repo, archive, revision, archive_specs[fileformat][1],
return read_in_chunks(archive)
def diff(self, repo_name, f_path):
diff1 = request.GET.get('diff1')
diff2 = request.GET.get('diff2')
c.action = request.GET.get('diff')
c.no_changes = diff1 == diff2
if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
c.changeset_1 = c.repo.get_changeset(diff1)
node1 = c.changeset_1.get_node(f_path)
else:
c.changeset_1 = EmptyChangeset()
node1 = FileNode('.', '', changeset=c.changeset_1)
if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
c.changeset_2 = c.repo.get_changeset(diff2)
node2 = c.changeset_2.get_node(f_path)
c.changeset_2 = EmptyChangeset()
@@ -202,49 +202,49 @@
l = YUD.getElementsByClassName('stats_hidden')
for (e in l){
YUD.setStyle(l[e],'display','');
};
YUD.setStyle(YUD.get('code_stats_show_more'),
'display','none');
})
</script>
</div>
<div class="field">
<div class="label">
<label>${_('Download')}:</label>
<div class="input-short">
%for cnt,archive in enumerate(c.repo_info._get_archives()):
%if cnt >=1:
|
%endif
${h.link_to(c.repo_info.name+'.'+archive['type'],
h.url('files_archive_home',repo_name=c.repo_info.name,
revision='tip',fileformat=archive['extension']),class_="archive_icon")}
fname='tip'+archive['extension']),class_="archive_icon")}
%endfor
<label>${_('Feeds')}:</label>
${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.repo_info.name),class_='rss_icon')}
${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.repo_info.name),class_='atom_icon')}
<div class="box box-right" style="min-height:455px">
<!-- box / title -->
<div class="title">
<h5>${_('Commit activity by day / author')}</h5>
<div class="table">
import sys
py_version = sys.version_info
from rhodecode import get_version
requirements = [
"Pylons==1.0.0",
"SQLAlchemy==0.6.5",
"SQLAlchemy>=0.6.5",
"Mako==0.3.6",
"vcs==0.1.10",
"pygments==1.3.1",
"mercurial==1.7.2",
"whoosh==1.3.4",
"celery==2.1.4",
"vcs=>0.1.10",
"pygments>=1.3.1",
"mercurial>=1.7.2",
"whoosh>=1.3.4",
"celery>=2.1.4",
"py-bcrypt",
"babel",
]
classifiers = ['Development Status :: 4 - Beta',
'Environment :: Web Environment',
'Framework :: Pylons',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python', ]
if sys.version_info < (2, 6):
requirements.append("simplejson")
requirements.append("pysqlite")
#additional files from project that goes somewhere in the filesystem
#relative to sys.prefix
data_files = []
#additional files that goes into package itself
package_data = {'rhodecode': ['i18n/*/LC_MESSAGES/*.mo', ], }
description = ('Mercurial repository browser/management with '
Status change: