Show More
@@ -1,13 +1,13 b'' | |||||
1 | """ |
|
1 | """ | |
2 | Hg app, a web based mercurial repository managment based on pylons |
|
2 | Hg app, a web based mercurial repository managment based on pylons | |
3 | """ |
|
3 | """ | |
4 |
|
4 | |||
5 |
VERSION = (0, 7, |
|
5 | VERSION = (0, 7, 2, 'beta') | |
6 |
|
6 | |||
7 | __version__ = '.'.join((str(each) for each in VERSION[:4])) |
|
7 | __version__ = '.'.join((str(each) for each in VERSION[:4])) | |
8 |
|
8 | |||
9 | def get_version(): |
|
9 | def get_version(): | |
10 | """ |
|
10 | """ | |
11 | Returns shorter version (digit parts only) as string. |
|
11 | Returns shorter version (digit parts only) as string. | |
12 | """ |
|
12 | """ | |
13 | return '.'.join((str(each) for each in VERSION[:3])) |
|
13 | return '.'.join((str(each) for each in VERSION[:3])) |
@@ -1,130 +1,157 b'' | |||||
1 | import logging |
|
1 | import tempfile | |
2 |
|
2 | from pylons import request, response, session, tmpl_context as c, url, config, \ | ||
3 | from pylons import request, response, session, tmpl_context as c, url, config, app_globals as g |
|
3 | app_globals as g | |
4 | from pylons.controllers.util import abort, redirect |
|
4 | from pylons.controllers.util import abort, redirect | |
5 |
|
5 | from pylons_app.lib.auth import LoginRequired | ||
6 | from pylons_app.lib.base import BaseController, render |
|
6 | from pylons_app.lib.base import BaseController, render | |
7 | from pylons_app.lib.utils import get_repo_slug |
|
7 | from pylons_app.lib.utils import get_repo_slug | |
8 | from pylons_app.model.hg_model import HgModel |
|
8 | from pylons_app.model.hg_model import HgModel | |
|
9 | from vcs.exceptions import RepositoryError, ChangesetError | |||
9 | from vcs.utils import diffs as differ |
|
10 | from vcs.utils import diffs as differ | |
10 | from vcs.exceptions import RepositoryError, ChangesetError |
|
11 | import logging | |
11 | from pylons_app.lib.auth import LoginRequired |
|
12 | from mercurial import archival | |
|
13 | ||||
12 |
|
14 | |||
13 | log = logging.getLogger(__name__) |
|
15 | log = logging.getLogger(__name__) | |
14 |
|
16 | |||
15 | class FilesController(BaseController): |
|
17 | class FilesController(BaseController): | |
16 |
|
18 | |||
17 | @LoginRequired() |
|
19 | @LoginRequired() | |
18 | def __before__(self): |
|
20 | def __before__(self): | |
19 | super(FilesController, self).__before__() |
|
21 | super(FilesController, self).__before__() | |
20 |
|
22 | |||
21 | def index(self, repo_name, revision, f_path): |
|
23 | def index(self, repo_name, revision, f_path): | |
22 | hg_model = HgModel() |
|
24 | hg_model = HgModel() | |
23 | c.repo = repo = hg_model.get_repo(c.repo_name) |
|
25 | c.repo = repo = hg_model.get_repo(c.repo_name) | |
24 | revision = request.POST.get('at_rev', None) or revision |
|
26 | revision = request.POST.get('at_rev', None) or revision | |
25 |
|
27 | |||
26 | def get_next_rev(cur): |
|
28 | def get_next_rev(cur): | |
27 | max_rev = len(c.repo.revisions) - 1 |
|
29 | max_rev = len(c.repo.revisions) - 1 | |
28 | r = cur + 1 |
|
30 | r = cur + 1 | |
29 | if r > max_rev: |
|
31 | if r > max_rev: | |
30 | r = max_rev |
|
32 | r = max_rev | |
31 | return r |
|
33 | return r | |
32 |
|
34 | |||
33 | def get_prev_rev(cur): |
|
35 | def get_prev_rev(cur): | |
34 | r = cur - 1 |
|
36 | r = cur - 1 | |
35 | return r |
|
37 | return r | |
36 |
|
38 | |||
37 | c.f_path = f_path |
|
39 | c.f_path = f_path | |
38 |
|
40 | |||
39 |
|
41 | |||
40 | try: |
|
42 | try: | |
41 | cur_rev = repo.get_changeset(revision).revision |
|
43 | cur_rev = repo.get_changeset(revision).revision | |
42 | prev_rev = repo.get_changeset(get_prev_rev(cur_rev)).raw_id |
|
44 | prev_rev = repo.get_changeset(get_prev_rev(cur_rev)).raw_id | |
43 | next_rev = repo.get_changeset(get_next_rev(cur_rev)).raw_id |
|
45 | next_rev = repo.get_changeset(get_next_rev(cur_rev)).raw_id | |
44 |
|
46 | |||
45 | c.url_prev = url('files_home', repo_name=c.repo_name, |
|
47 | c.url_prev = url('files_home', repo_name=c.repo_name, | |
46 | revision=prev_rev, f_path=f_path) |
|
48 | revision=prev_rev, f_path=f_path) | |
47 | c.url_next = url('files_home', repo_name=c.repo_name, |
|
49 | c.url_next = url('files_home', repo_name=c.repo_name, | |
48 | revision=next_rev, f_path=f_path) |
|
50 | revision=next_rev, f_path=f_path) | |
49 |
|
51 | |||
50 | c.changeset = repo.get_changeset(revision) |
|
52 | c.changeset = repo.get_changeset(revision) | |
51 | try: |
|
53 | try: | |
52 | c.file_msg = c.changeset.get_file_message(f_path) |
|
54 | c.file_msg = c.changeset.get_file_message(f_path) | |
53 | except: |
|
55 | except: | |
54 | c.file_msg = None |
|
56 | c.file_msg = None | |
55 |
|
57 | |||
56 | c.cur_rev = c.changeset.raw_id |
|
58 | c.cur_rev = c.changeset.raw_id | |
57 | c.rev_nr = c.changeset.revision |
|
59 | c.rev_nr = c.changeset.revision | |
58 | c.files_list = c.changeset.get_node(f_path) |
|
60 | c.files_list = c.changeset.get_node(f_path) | |
59 | c.file_history = self._get_history(repo, c.files_list, f_path) |
|
61 | c.file_history = self._get_history(repo, c.files_list, f_path) | |
60 |
|
62 | |||
61 | except (RepositoryError, ChangesetError): |
|
63 | except (RepositoryError, ChangesetError): | |
62 | c.files_list = None |
|
64 | c.files_list = None | |
63 |
|
65 | |||
64 | return render('files/files.html') |
|
66 | return render('files/files.html') | |
65 |
|
67 | |||
66 | def rawfile(self, repo_name, revision, f_path): |
|
68 | def rawfile(self, repo_name, revision, f_path): | |
67 | hg_model = HgModel() |
|
69 | hg_model = HgModel() | |
68 | c.repo = hg_model.get_repo(c.repo_name) |
|
70 | c.repo = hg_model.get_repo(c.repo_name) | |
69 | file_node = c.repo.get_changeset(revision).get_node(f_path) |
|
71 | file_node = c.repo.get_changeset(revision).get_node(f_path) | |
70 | response.content_type = file_node.mimetype |
|
72 | response.content_type = file_node.mimetype | |
71 | response.content_disposition = 'attachment; filename=%s' \ |
|
73 | response.content_disposition = 'attachment; filename=%s' \ | |
72 | % f_path.split('/')[-1] |
|
74 | % f_path.split('/')[-1] | |
73 | return file_node.content |
|
75 | return file_node.content | |
74 |
|
76 | |||
75 | def annotate(self, repo_name, revision, f_path): |
|
77 | def annotate(self, repo_name, revision, f_path): | |
76 | hg_model = HgModel() |
|
78 | hg_model = HgModel() | |
77 | c.repo = hg_model.get_repo(c.repo_name) |
|
79 | c.repo = hg_model.get_repo(c.repo_name) | |
78 | cs = c.repo.get_changeset(revision) |
|
80 | cs = c.repo.get_changeset(revision) | |
79 | c.file = cs.get_node(f_path) |
|
81 | c.file = cs.get_node(f_path) | |
80 | c.file_msg = cs.get_file_message(f_path) |
|
82 | c.file_msg = cs.get_file_message(f_path) | |
81 | c.cur_rev = cs.raw_id |
|
83 | c.cur_rev = cs.raw_id | |
82 | c.f_path = f_path |
|
84 | c.f_path = f_path | |
83 | c.annotate = cs.get_file_annotate(f_path) |
|
85 | c.annotate = cs.get_file_annotate(f_path) | |
84 | return render('files/files_annotate.html') |
|
86 | return render('files/files_annotate.html') | |
85 |
|
87 | |||
86 | def archivefile(self, repo_name, revision, fileformat): |
|
88 | def archivefile(self, repo_name, revision, fileformat): | |
87 | return '%s %s %s' % (repo_name, revision, fileformat) |
|
89 | archive_specs = { | |
|
90 | '.tar.bz2': ('application/x-tar', 'tbz2'), | |||
|
91 | '.tar.gz': ('application/x-tar', 'tgz'), | |||
|
92 | '.zip': ('application/zip', 'zip'), | |||
|
93 | } | |||
|
94 | if not archive_specs.has_key(fileformat): | |||
|
95 | return 'Unknown archive type %s' % fileformat | |||
|
96 | ||||
|
97 | def read_in_chunks(file_object, chunk_size=1024 * 40): | |||
|
98 | """Lazy function (generator) to read a file piece by piece. | |||
|
99 | Default chunk size: 40k.""" | |||
|
100 | while True: | |||
|
101 | data = file_object.read(chunk_size) | |||
|
102 | if not data: | |||
|
103 | break | |||
|
104 | yield data | |||
|
105 | ||||
|
106 | archive = tempfile.TemporaryFile() | |||
|
107 | repo = HgModel().get_repo(repo_name).repo | |||
|
108 | fname = '%s-%s%s' % (repo_name, revision, fileformat) | |||
|
109 | archival.archive(repo, archive, revision, archive_specs[fileformat][1], | |||
|
110 | prefix='%s-%s' % (repo_name, revision)) | |||
|
111 | response.content_type = archive_specs[fileformat][0] | |||
|
112 | response.content_disposition = 'attachment; filename=%s' % fname | |||
|
113 | archive.seek(0) | |||
|
114 | return read_in_chunks(archive) | |||
88 |
|
115 | |||
89 | def diff(self, repo_name, f_path): |
|
116 | def diff(self, repo_name, f_path): | |
90 | hg_model = HgModel() |
|
117 | hg_model = HgModel() | |
91 | diff1 = request.GET.get('diff1') |
|
118 | diff1 = request.GET.get('diff1') | |
92 | diff2 = request.GET.get('diff2') |
|
119 | diff2 = request.GET.get('diff2') | |
93 | c.action = action = request.GET.get('diff') |
|
120 | c.action = action = request.GET.get('diff') | |
94 | c.no_changes = diff1 == diff2 |
|
121 | c.no_changes = diff1 == diff2 | |
95 | c.f_path = f_path |
|
122 | c.f_path = f_path | |
96 | c.repo = hg_model.get_repo(c.repo_name) |
|
123 | c.repo = hg_model.get_repo(c.repo_name) | |
97 | c.changeset_1 = c.repo.get_changeset(diff1) |
|
124 | c.changeset_1 = c.repo.get_changeset(diff1) | |
98 | c.changeset_2 = c.repo.get_changeset(diff2) |
|
125 | c.changeset_2 = c.repo.get_changeset(diff2) | |
99 |
|
126 | |||
100 | c.diff1 = 'r%s:%s' % (c.changeset_1.revision, c.changeset_1._short) |
|
127 | c.diff1 = 'r%s:%s' % (c.changeset_1.revision, c.changeset_1._short) | |
101 | c.diff2 = 'r%s:%s' % (c.changeset_2.revision, c.changeset_2._short) |
|
128 | c.diff2 = 'r%s:%s' % (c.changeset_2.revision, c.changeset_2._short) | |
102 | f_udiff = differ.get_udiff(c.changeset_1.get_node(f_path), |
|
129 | f_udiff = differ.get_udiff(c.changeset_1.get_node(f_path), | |
103 | c.changeset_2.get_node(f_path)) |
|
130 | c.changeset_2.get_node(f_path)) | |
104 |
|
131 | |||
105 | diff = differ.DiffProcessor(f_udiff) |
|
132 | diff = differ.DiffProcessor(f_udiff) | |
106 |
|
133 | |||
107 | if action == 'download': |
|
134 | if action == 'download': | |
108 | diff_name = '%s_vs_%s.diff' % (diff1, diff2) |
|
135 | diff_name = '%s_vs_%s.diff' % (diff1, diff2) | |
109 | response.content_type = 'text/plain' |
|
136 | response.content_type = 'text/plain' | |
110 | response.content_disposition = 'attachment; filename=%s' \ |
|
137 | response.content_disposition = 'attachment; filename=%s' \ | |
111 | % diff_name |
|
138 | % diff_name | |
112 | return diff.raw_diff() |
|
139 | return diff.raw_diff() | |
113 |
|
140 | |||
114 | elif action == 'raw': |
|
141 | elif action == 'raw': | |
115 | c.cur_diff = '<pre class="raw">%s</pre>' % diff.raw_diff() |
|
142 | c.cur_diff = '<pre class="raw">%s</pre>' % diff.raw_diff() | |
116 | elif action == 'diff': |
|
143 | elif action == 'diff': | |
117 | c.cur_diff = diff.as_html() |
|
144 | c.cur_diff = diff.as_html() | |
118 |
|
145 | |||
119 | return render('files/file_diff.html') |
|
146 | return render('files/file_diff.html') | |
120 |
|
147 | |||
121 | def _get_history(self, repo, node, f_path): |
|
148 | def _get_history(self, repo, node, f_path): | |
122 | from vcs.nodes import NodeKind |
|
149 | from vcs.nodes import NodeKind | |
123 | if not node.kind is NodeKind.FILE: |
|
150 | if not node.kind is NodeKind.FILE: | |
124 | return [] |
|
151 | return [] | |
125 | changesets = node.history |
|
152 | changesets = node.history | |
126 | hist_l = [] |
|
153 | hist_l = [] | |
127 | for chs in changesets: |
|
154 | for chs in changesets: | |
128 | n_desc = 'r%s:%s' % (chs.revision, chs._short) |
|
155 | n_desc = 'r%s:%s' % (chs.revision, chs._short) | |
129 | hist_l.append((chs._short, n_desc,)) |
|
156 | hist_l.append((chs._short, n_desc,)) | |
130 | return hist_l |
|
157 | return hist_l |
General Comments 0
You need to be logged in to leave comments.
Login now