##// END OF EJS Templates
changelog: fix and optimize loading of chunks for file history....
marcink -
r2130:69819530 default
parent child Browse files
Show More
@@ -1,447 +1,450 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 from rhodecode.apps._base import add_route_with_slash
20 from rhodecode.apps._base import add_route_with_slash
21
21
22
22
23 def includeme(config):
23 def includeme(config):
24
24
25 # repo creating checks, special cases that aren't repo routes
25 # repo creating checks, special cases that aren't repo routes
26 config.add_route(
26 config.add_route(
27 name='repo_creating',
27 name='repo_creating',
28 pattern='/{repo_name:.*?[^/]}/repo_creating')
28 pattern='/{repo_name:.*?[^/]}/repo_creating')
29
29
30 config.add_route(
30 config.add_route(
31 name='repo_creating_check',
31 name='repo_creating_check',
32 pattern='/{repo_name:.*?[^/]}/repo_creating_check')
32 pattern='/{repo_name:.*?[^/]}/repo_creating_check')
33
33
34 # Summary
34 # Summary
35 # NOTE(marcink): one additional route is defined in very bottom, catch
35 # NOTE(marcink): one additional route is defined in very bottom, catch
36 # all pattern
36 # all pattern
37 config.add_route(
37 config.add_route(
38 name='repo_summary_explicit',
38 name='repo_summary_explicit',
39 pattern='/{repo_name:.*?[^/]}/summary', repo_route=True)
39 pattern='/{repo_name:.*?[^/]}/summary', repo_route=True)
40 config.add_route(
40 config.add_route(
41 name='repo_summary_commits',
41 name='repo_summary_commits',
42 pattern='/{repo_name:.*?[^/]}/summary-commits', repo_route=True)
42 pattern='/{repo_name:.*?[^/]}/summary-commits', repo_route=True)
43
43
44 # Commits
44 # Commits
45 config.add_route(
45 config.add_route(
46 name='repo_commit',
46 name='repo_commit',
47 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}', repo_route=True)
47 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}', repo_route=True)
48
48
49 config.add_route(
49 config.add_route(
50 name='repo_commit_children',
50 name='repo_commit_children',
51 pattern='/{repo_name:.*?[^/]}/changeset_children/{commit_id}', repo_route=True)
51 pattern='/{repo_name:.*?[^/]}/changeset_children/{commit_id}', repo_route=True)
52
52
53 config.add_route(
53 config.add_route(
54 name='repo_commit_parents',
54 name='repo_commit_parents',
55 pattern='/{repo_name:.*?[^/]}/changeset_parents/{commit_id}', repo_route=True)
55 pattern='/{repo_name:.*?[^/]}/changeset_parents/{commit_id}', repo_route=True)
56
56
57 config.add_route(
57 config.add_route(
58 name='repo_commit_raw',
58 name='repo_commit_raw',
59 pattern='/{repo_name:.*?[^/]}/changeset-diff/{commit_id}', repo_route=True)
59 pattern='/{repo_name:.*?[^/]}/changeset-diff/{commit_id}', repo_route=True)
60
60
61 config.add_route(
61 config.add_route(
62 name='repo_commit_patch',
62 name='repo_commit_patch',
63 pattern='/{repo_name:.*?[^/]}/changeset-patch/{commit_id}', repo_route=True)
63 pattern='/{repo_name:.*?[^/]}/changeset-patch/{commit_id}', repo_route=True)
64
64
65 config.add_route(
65 config.add_route(
66 name='repo_commit_download',
66 name='repo_commit_download',
67 pattern='/{repo_name:.*?[^/]}/changeset-download/{commit_id}', repo_route=True)
67 pattern='/{repo_name:.*?[^/]}/changeset-download/{commit_id}', repo_route=True)
68
68
69 config.add_route(
69 config.add_route(
70 name='repo_commit_data',
70 name='repo_commit_data',
71 pattern='/{repo_name:.*?[^/]}/changeset-data/{commit_id}', repo_route=True)
71 pattern='/{repo_name:.*?[^/]}/changeset-data/{commit_id}', repo_route=True)
72
72
73 config.add_route(
73 config.add_route(
74 name='repo_commit_comment_create',
74 name='repo_commit_comment_create',
75 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/create', repo_route=True)
75 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/create', repo_route=True)
76
76
77 config.add_route(
77 config.add_route(
78 name='repo_commit_comment_preview',
78 name='repo_commit_comment_preview',
79 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/preview', repo_route=True)
79 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/preview', repo_route=True)
80
80
81 config.add_route(
81 config.add_route(
82 name='repo_commit_comment_delete',
82 name='repo_commit_comment_delete',
83 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/{comment_id}/delete', repo_route=True)
83 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/{comment_id}/delete', repo_route=True)
84
84
85 # still working url for backward compat.
85 # still working url for backward compat.
86 config.add_route(
86 config.add_route(
87 name='repo_commit_raw_deprecated',
87 name='repo_commit_raw_deprecated',
88 pattern='/{repo_name:.*?[^/]}/raw-changeset/{commit_id}', repo_route=True)
88 pattern='/{repo_name:.*?[^/]}/raw-changeset/{commit_id}', repo_route=True)
89
89
90 # Files
90 # Files
91 config.add_route(
91 config.add_route(
92 name='repo_archivefile',
92 name='repo_archivefile',
93 pattern='/{repo_name:.*?[^/]}/archive/{fname}', repo_route=True)
93 pattern='/{repo_name:.*?[^/]}/archive/{fname}', repo_route=True)
94
94
95 config.add_route(
95 config.add_route(
96 name='repo_files_diff',
96 name='repo_files_diff',
97 pattern='/{repo_name:.*?[^/]}/diff/{f_path:.*}', repo_route=True)
97 pattern='/{repo_name:.*?[^/]}/diff/{f_path:.*}', repo_route=True)
98 config.add_route( # legacy route to make old links work
98 config.add_route( # legacy route to make old links work
99 name='repo_files_diff_2way_redirect',
99 name='repo_files_diff_2way_redirect',
100 pattern='/{repo_name:.*?[^/]}/diff-2way/{f_path:.*}', repo_route=True)
100 pattern='/{repo_name:.*?[^/]}/diff-2way/{f_path:.*}', repo_route=True)
101
101
102 config.add_route(
102 config.add_route(
103 name='repo_files',
103 name='repo_files',
104 pattern='/{repo_name:.*?[^/]}/files/{commit_id}/{f_path:.*}', repo_route=True)
104 pattern='/{repo_name:.*?[^/]}/files/{commit_id}/{f_path:.*}', repo_route=True)
105 config.add_route(
105 config.add_route(
106 name='repo_files:default_path',
106 name='repo_files:default_path',
107 pattern='/{repo_name:.*?[^/]}/files/{commit_id}/', repo_route=True)
107 pattern='/{repo_name:.*?[^/]}/files/{commit_id}/', repo_route=True)
108 config.add_route(
108 config.add_route(
109 name='repo_files:default_commit',
109 name='repo_files:default_commit',
110 pattern='/{repo_name:.*?[^/]}/files', repo_route=True)
110 pattern='/{repo_name:.*?[^/]}/files', repo_route=True)
111
111
112 config.add_route(
112 config.add_route(
113 name='repo_files:rendered',
113 name='repo_files:rendered',
114 pattern='/{repo_name:.*?[^/]}/render/{commit_id}/{f_path:.*}', repo_route=True)
114 pattern='/{repo_name:.*?[^/]}/render/{commit_id}/{f_path:.*}', repo_route=True)
115
115
116 config.add_route(
116 config.add_route(
117 name='repo_files:annotated',
117 name='repo_files:annotated',
118 pattern='/{repo_name:.*?[^/]}/annotate/{commit_id}/{f_path:.*}', repo_route=True)
118 pattern='/{repo_name:.*?[^/]}/annotate/{commit_id}/{f_path:.*}', repo_route=True)
119 config.add_route(
119 config.add_route(
120 name='repo_files:annotated_previous',
120 name='repo_files:annotated_previous',
121 pattern='/{repo_name:.*?[^/]}/annotate-previous/{commit_id}/{f_path:.*}', repo_route=True)
121 pattern='/{repo_name:.*?[^/]}/annotate-previous/{commit_id}/{f_path:.*}', repo_route=True)
122
122
123 config.add_route(
123 config.add_route(
124 name='repo_nodetree_full',
124 name='repo_nodetree_full',
125 pattern='/{repo_name:.*?[^/]}/nodetree_full/{commit_id}/{f_path:.*}', repo_route=True)
125 pattern='/{repo_name:.*?[^/]}/nodetree_full/{commit_id}/{f_path:.*}', repo_route=True)
126 config.add_route(
126 config.add_route(
127 name='repo_nodetree_full:default_path',
127 name='repo_nodetree_full:default_path',
128 pattern='/{repo_name:.*?[^/]}/nodetree_full/{commit_id}/', repo_route=True)
128 pattern='/{repo_name:.*?[^/]}/nodetree_full/{commit_id}/', repo_route=True)
129
129
130 config.add_route(
130 config.add_route(
131 name='repo_files_nodelist',
131 name='repo_files_nodelist',
132 pattern='/{repo_name:.*?[^/]}/nodelist/{commit_id}/{f_path:.*}', repo_route=True)
132 pattern='/{repo_name:.*?[^/]}/nodelist/{commit_id}/{f_path:.*}', repo_route=True)
133
133
134 config.add_route(
134 config.add_route(
135 name='repo_file_raw',
135 name='repo_file_raw',
136 pattern='/{repo_name:.*?[^/]}/raw/{commit_id}/{f_path:.*}', repo_route=True)
136 pattern='/{repo_name:.*?[^/]}/raw/{commit_id}/{f_path:.*}', repo_route=True)
137
137
138 config.add_route(
138 config.add_route(
139 name='repo_file_download',
139 name='repo_file_download',
140 pattern='/{repo_name:.*?[^/]}/download/{commit_id}/{f_path:.*}', repo_route=True)
140 pattern='/{repo_name:.*?[^/]}/download/{commit_id}/{f_path:.*}', repo_route=True)
141 config.add_route( # backward compat to keep old links working
141 config.add_route( # backward compat to keep old links working
142 name='repo_file_download:legacy',
142 name='repo_file_download:legacy',
143 pattern='/{repo_name:.*?[^/]}/rawfile/{commit_id}/{f_path:.*}',
143 pattern='/{repo_name:.*?[^/]}/rawfile/{commit_id}/{f_path:.*}',
144 repo_route=True)
144 repo_route=True)
145
145
146 config.add_route(
146 config.add_route(
147 name='repo_file_history',
147 name='repo_file_history',
148 pattern='/{repo_name:.*?[^/]}/history/{commit_id}/{f_path:.*}', repo_route=True)
148 pattern='/{repo_name:.*?[^/]}/history/{commit_id}/{f_path:.*}', repo_route=True)
149
149
150 config.add_route(
150 config.add_route(
151 name='repo_file_authors',
151 name='repo_file_authors',
152 pattern='/{repo_name:.*?[^/]}/authors/{commit_id}/{f_path:.*}', repo_route=True)
152 pattern='/{repo_name:.*?[^/]}/authors/{commit_id}/{f_path:.*}', repo_route=True)
153
153
154 config.add_route(
154 config.add_route(
155 name='repo_files_remove_file',
155 name='repo_files_remove_file',
156 pattern='/{repo_name:.*?[^/]}/remove_file/{commit_id}/{f_path:.*}',
156 pattern='/{repo_name:.*?[^/]}/remove_file/{commit_id}/{f_path:.*}',
157 repo_route=True)
157 repo_route=True)
158 config.add_route(
158 config.add_route(
159 name='repo_files_delete_file',
159 name='repo_files_delete_file',
160 pattern='/{repo_name:.*?[^/]}/delete_file/{commit_id}/{f_path:.*}',
160 pattern='/{repo_name:.*?[^/]}/delete_file/{commit_id}/{f_path:.*}',
161 repo_route=True)
161 repo_route=True)
162 config.add_route(
162 config.add_route(
163 name='repo_files_edit_file',
163 name='repo_files_edit_file',
164 pattern='/{repo_name:.*?[^/]}/edit_file/{commit_id}/{f_path:.*}',
164 pattern='/{repo_name:.*?[^/]}/edit_file/{commit_id}/{f_path:.*}',
165 repo_route=True)
165 repo_route=True)
166 config.add_route(
166 config.add_route(
167 name='repo_files_update_file',
167 name='repo_files_update_file',
168 pattern='/{repo_name:.*?[^/]}/update_file/{commit_id}/{f_path:.*}',
168 pattern='/{repo_name:.*?[^/]}/update_file/{commit_id}/{f_path:.*}',
169 repo_route=True)
169 repo_route=True)
170 config.add_route(
170 config.add_route(
171 name='repo_files_add_file',
171 name='repo_files_add_file',
172 pattern='/{repo_name:.*?[^/]}/add_file/{commit_id}/{f_path:.*}',
172 pattern='/{repo_name:.*?[^/]}/add_file/{commit_id}/{f_path:.*}',
173 repo_route=True)
173 repo_route=True)
174 config.add_route(
174 config.add_route(
175 name='repo_files_create_file',
175 name='repo_files_create_file',
176 pattern='/{repo_name:.*?[^/]}/create_file/{commit_id}/{f_path:.*}',
176 pattern='/{repo_name:.*?[^/]}/create_file/{commit_id}/{f_path:.*}',
177 repo_route=True)
177 repo_route=True)
178
178
179 # Refs data
179 # Refs data
180 config.add_route(
180 config.add_route(
181 name='repo_refs_data',
181 name='repo_refs_data',
182 pattern='/{repo_name:.*?[^/]}/refs-data', repo_route=True)
182 pattern='/{repo_name:.*?[^/]}/refs-data', repo_route=True)
183
183
184 config.add_route(
184 config.add_route(
185 name='repo_refs_changelog_data',
185 name='repo_refs_changelog_data',
186 pattern='/{repo_name:.*?[^/]}/refs-data-changelog', repo_route=True)
186 pattern='/{repo_name:.*?[^/]}/refs-data-changelog', repo_route=True)
187
187
188 config.add_route(
188 config.add_route(
189 name='repo_stats',
189 name='repo_stats',
190 pattern='/{repo_name:.*?[^/]}/repo_stats/{commit_id}', repo_route=True)
190 pattern='/{repo_name:.*?[^/]}/repo_stats/{commit_id}', repo_route=True)
191
191
192 # Changelog
192 # Changelog
193 config.add_route(
193 config.add_route(
194 name='repo_changelog',
194 name='repo_changelog',
195 pattern='/{repo_name:.*?[^/]}/changelog', repo_route=True)
195 pattern='/{repo_name:.*?[^/]}/changelog', repo_route=True)
196 config.add_route(
196 config.add_route(
197 name='repo_changelog_file',
197 name='repo_changelog_file',
198 pattern='/{repo_name:.*?[^/]}/changelog/{commit_id}/{f_path:.*}', repo_route=True)
198 pattern='/{repo_name:.*?[^/]}/changelog/{commit_id}/{f_path:.*}', repo_route=True)
199 config.add_route(
199 config.add_route(
200 name='repo_changelog_elements',
200 name='repo_changelog_elements',
201 pattern='/{repo_name:.*?[^/]}/changelog_elements', repo_route=True)
201 pattern='/{repo_name:.*?[^/]}/changelog_elements', repo_route=True)
202 config.add_route(
203 name='repo_changelog_elements_file',
204 pattern='/{repo_name:.*?[^/]}/changelog_elements/{commit_id}/{f_path:.*}', repo_route=True)
202
205
203 # Compare
206 # Compare
204 config.add_route(
207 config.add_route(
205 name='repo_compare_select',
208 name='repo_compare_select',
206 pattern='/{repo_name:.*?[^/]}/compare', repo_route=True)
209 pattern='/{repo_name:.*?[^/]}/compare', repo_route=True)
207
210
208 config.add_route(
211 config.add_route(
209 name='repo_compare',
212 name='repo_compare',
210 pattern='/{repo_name:.*?[^/]}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}', repo_route=True)
213 pattern='/{repo_name:.*?[^/]}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}', repo_route=True)
211
214
212 # Tags
215 # Tags
213 config.add_route(
216 config.add_route(
214 name='tags_home',
217 name='tags_home',
215 pattern='/{repo_name:.*?[^/]}/tags', repo_route=True)
218 pattern='/{repo_name:.*?[^/]}/tags', repo_route=True)
216
219
217 # Branches
220 # Branches
218 config.add_route(
221 config.add_route(
219 name='branches_home',
222 name='branches_home',
220 pattern='/{repo_name:.*?[^/]}/branches', repo_route=True)
223 pattern='/{repo_name:.*?[^/]}/branches', repo_route=True)
221
224
222 # Bookmarks
225 # Bookmarks
223 config.add_route(
226 config.add_route(
224 name='bookmarks_home',
227 name='bookmarks_home',
225 pattern='/{repo_name:.*?[^/]}/bookmarks', repo_route=True)
228 pattern='/{repo_name:.*?[^/]}/bookmarks', repo_route=True)
226
229
227 # Forks
230 # Forks
228 config.add_route(
231 config.add_route(
229 name='repo_fork_new',
232 name='repo_fork_new',
230 pattern='/{repo_name:.*?[^/]}/fork', repo_route=True,
233 pattern='/{repo_name:.*?[^/]}/fork', repo_route=True,
231 repo_accepted_types=['hg', 'git'])
234 repo_accepted_types=['hg', 'git'])
232
235
233 config.add_route(
236 config.add_route(
234 name='repo_fork_create',
237 name='repo_fork_create',
235 pattern='/{repo_name:.*?[^/]}/fork/create', repo_route=True,
238 pattern='/{repo_name:.*?[^/]}/fork/create', repo_route=True,
236 repo_accepted_types=['hg', 'git'])
239 repo_accepted_types=['hg', 'git'])
237
240
238 config.add_route(
241 config.add_route(
239 name='repo_forks_show_all',
242 name='repo_forks_show_all',
240 pattern='/{repo_name:.*?[^/]}/forks', repo_route=True,
243 pattern='/{repo_name:.*?[^/]}/forks', repo_route=True,
241 repo_accepted_types=['hg', 'git'])
244 repo_accepted_types=['hg', 'git'])
242 config.add_route(
245 config.add_route(
243 name='repo_forks_data',
246 name='repo_forks_data',
244 pattern='/{repo_name:.*?[^/]}/forks/data', repo_route=True,
247 pattern='/{repo_name:.*?[^/]}/forks/data', repo_route=True,
245 repo_accepted_types=['hg', 'git'])
248 repo_accepted_types=['hg', 'git'])
246
249
247 # Pull Requests
250 # Pull Requests
248 config.add_route(
251 config.add_route(
249 name='pullrequest_show',
252 name='pullrequest_show',
250 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}',
253 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}',
251 repo_route=True)
254 repo_route=True)
252
255
253 config.add_route(
256 config.add_route(
254 name='pullrequest_show_all',
257 name='pullrequest_show_all',
255 pattern='/{repo_name:.*?[^/]}/pull-request',
258 pattern='/{repo_name:.*?[^/]}/pull-request',
256 repo_route=True, repo_accepted_types=['hg', 'git'])
259 repo_route=True, repo_accepted_types=['hg', 'git'])
257
260
258 config.add_route(
261 config.add_route(
259 name='pullrequest_show_all_data',
262 name='pullrequest_show_all_data',
260 pattern='/{repo_name:.*?[^/]}/pull-request-data',
263 pattern='/{repo_name:.*?[^/]}/pull-request-data',
261 repo_route=True, repo_accepted_types=['hg', 'git'])
264 repo_route=True, repo_accepted_types=['hg', 'git'])
262
265
263 config.add_route(
266 config.add_route(
264 name='pullrequest_repo_refs',
267 name='pullrequest_repo_refs',
265 pattern='/{repo_name:.*?[^/]}/pull-request/refs/{target_repo_name:.*?[^/]}',
268 pattern='/{repo_name:.*?[^/]}/pull-request/refs/{target_repo_name:.*?[^/]}',
266 repo_route=True)
269 repo_route=True)
267
270
268 config.add_route(
271 config.add_route(
269 name='pullrequest_repo_destinations',
272 name='pullrequest_repo_destinations',
270 pattern='/{repo_name:.*?[^/]}/pull-request/repo-destinations',
273 pattern='/{repo_name:.*?[^/]}/pull-request/repo-destinations',
271 repo_route=True)
274 repo_route=True)
272
275
273 config.add_route(
276 config.add_route(
274 name='pullrequest_new',
277 name='pullrequest_new',
275 pattern='/{repo_name:.*?[^/]}/pull-request/new',
278 pattern='/{repo_name:.*?[^/]}/pull-request/new',
276 repo_route=True, repo_accepted_types=['hg', 'git'])
279 repo_route=True, repo_accepted_types=['hg', 'git'])
277
280
278 config.add_route(
281 config.add_route(
279 name='pullrequest_create',
282 name='pullrequest_create',
280 pattern='/{repo_name:.*?[^/]}/pull-request/create',
283 pattern='/{repo_name:.*?[^/]}/pull-request/create',
281 repo_route=True, repo_accepted_types=['hg', 'git'])
284 repo_route=True, repo_accepted_types=['hg', 'git'])
282
285
283 config.add_route(
286 config.add_route(
284 name='pullrequest_update',
287 name='pullrequest_update',
285 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/update',
288 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/update',
286 repo_route=True)
289 repo_route=True)
287
290
288 config.add_route(
291 config.add_route(
289 name='pullrequest_merge',
292 name='pullrequest_merge',
290 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/merge',
293 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/merge',
291 repo_route=True)
294 repo_route=True)
292
295
293 config.add_route(
296 config.add_route(
294 name='pullrequest_delete',
297 name='pullrequest_delete',
295 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/delete',
298 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/delete',
296 repo_route=True)
299 repo_route=True)
297
300
298 config.add_route(
301 config.add_route(
299 name='pullrequest_comment_create',
302 name='pullrequest_comment_create',
300 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/comment',
303 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/comment',
301 repo_route=True)
304 repo_route=True)
302
305
303 config.add_route(
306 config.add_route(
304 name='pullrequest_comment_delete',
307 name='pullrequest_comment_delete',
305 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/comment/{comment_id}/delete',
308 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/comment/{comment_id}/delete',
306 repo_route=True, repo_accepted_types=['hg', 'git'])
309 repo_route=True, repo_accepted_types=['hg', 'git'])
307
310
308 # Settings
311 # Settings
309 config.add_route(
312 config.add_route(
310 name='edit_repo',
313 name='edit_repo',
311 pattern='/{repo_name:.*?[^/]}/settings', repo_route=True)
314 pattern='/{repo_name:.*?[^/]}/settings', repo_route=True)
312
315
313 # Settings advanced
316 # Settings advanced
314 config.add_route(
317 config.add_route(
315 name='edit_repo_advanced',
318 name='edit_repo_advanced',
316 pattern='/{repo_name:.*?[^/]}/settings/advanced', repo_route=True)
319 pattern='/{repo_name:.*?[^/]}/settings/advanced', repo_route=True)
317 config.add_route(
320 config.add_route(
318 name='edit_repo_advanced_delete',
321 name='edit_repo_advanced_delete',
319 pattern='/{repo_name:.*?[^/]}/settings/advanced/delete', repo_route=True)
322 pattern='/{repo_name:.*?[^/]}/settings/advanced/delete', repo_route=True)
320 config.add_route(
323 config.add_route(
321 name='edit_repo_advanced_locking',
324 name='edit_repo_advanced_locking',
322 pattern='/{repo_name:.*?[^/]}/settings/advanced/locking', repo_route=True)
325 pattern='/{repo_name:.*?[^/]}/settings/advanced/locking', repo_route=True)
323 config.add_route(
326 config.add_route(
324 name='edit_repo_advanced_journal',
327 name='edit_repo_advanced_journal',
325 pattern='/{repo_name:.*?[^/]}/settings/advanced/journal', repo_route=True)
328 pattern='/{repo_name:.*?[^/]}/settings/advanced/journal', repo_route=True)
326 config.add_route(
329 config.add_route(
327 name='edit_repo_advanced_fork',
330 name='edit_repo_advanced_fork',
328 pattern='/{repo_name:.*?[^/]}/settings/advanced/fork', repo_route=True)
331 pattern='/{repo_name:.*?[^/]}/settings/advanced/fork', repo_route=True)
329
332
330 # Caches
333 # Caches
331 config.add_route(
334 config.add_route(
332 name='edit_repo_caches',
335 name='edit_repo_caches',
333 pattern='/{repo_name:.*?[^/]}/settings/caches', repo_route=True)
336 pattern='/{repo_name:.*?[^/]}/settings/caches', repo_route=True)
334
337
335 # Permissions
338 # Permissions
336 config.add_route(
339 config.add_route(
337 name='edit_repo_perms',
340 name='edit_repo_perms',
338 pattern='/{repo_name:.*?[^/]}/settings/permissions', repo_route=True)
341 pattern='/{repo_name:.*?[^/]}/settings/permissions', repo_route=True)
339
342
340 # Maintenance
343 # Maintenance
341 config.add_route(
344 config.add_route(
342 name='edit_repo_maintenance',
345 name='edit_repo_maintenance',
343 pattern='/{repo_name:.*?[^/]}/settings/maintenance', repo_route=True)
346 pattern='/{repo_name:.*?[^/]}/settings/maintenance', repo_route=True)
344
347
345 config.add_route(
348 config.add_route(
346 name='edit_repo_maintenance_execute',
349 name='edit_repo_maintenance_execute',
347 pattern='/{repo_name:.*?[^/]}/settings/maintenance/execute', repo_route=True)
350 pattern='/{repo_name:.*?[^/]}/settings/maintenance/execute', repo_route=True)
348
351
349 # Fields
352 # Fields
350 config.add_route(
353 config.add_route(
351 name='edit_repo_fields',
354 name='edit_repo_fields',
352 pattern='/{repo_name:.*?[^/]}/settings/fields', repo_route=True)
355 pattern='/{repo_name:.*?[^/]}/settings/fields', repo_route=True)
353 config.add_route(
356 config.add_route(
354 name='edit_repo_fields_create',
357 name='edit_repo_fields_create',
355 pattern='/{repo_name:.*?[^/]}/settings/fields/create', repo_route=True)
358 pattern='/{repo_name:.*?[^/]}/settings/fields/create', repo_route=True)
356 config.add_route(
359 config.add_route(
357 name='edit_repo_fields_delete',
360 name='edit_repo_fields_delete',
358 pattern='/{repo_name:.*?[^/]}/settings/fields/{field_id}/delete', repo_route=True)
361 pattern='/{repo_name:.*?[^/]}/settings/fields/{field_id}/delete', repo_route=True)
359
362
360 # Locking
363 # Locking
361 config.add_route(
364 config.add_route(
362 name='repo_edit_toggle_locking',
365 name='repo_edit_toggle_locking',
363 pattern='/{repo_name:.*?[^/]}/settings/toggle_locking', repo_route=True)
366 pattern='/{repo_name:.*?[^/]}/settings/toggle_locking', repo_route=True)
364
367
365 # Remote
368 # Remote
366 config.add_route(
369 config.add_route(
367 name='edit_repo_remote',
370 name='edit_repo_remote',
368 pattern='/{repo_name:.*?[^/]}/settings/remote', repo_route=True)
371 pattern='/{repo_name:.*?[^/]}/settings/remote', repo_route=True)
369 config.add_route(
372 config.add_route(
370 name='edit_repo_remote_pull',
373 name='edit_repo_remote_pull',
371 pattern='/{repo_name:.*?[^/]}/settings/remote/pull', repo_route=True)
374 pattern='/{repo_name:.*?[^/]}/settings/remote/pull', repo_route=True)
372
375
373
376
374 # Statistics
377 # Statistics
375 config.add_route(
378 config.add_route(
376 name='edit_repo_statistics',
379 name='edit_repo_statistics',
377 pattern='/{repo_name:.*?[^/]}/settings/statistics', repo_route=True)
380 pattern='/{repo_name:.*?[^/]}/settings/statistics', repo_route=True)
378 config.add_route(
381 config.add_route(
379 name='edit_repo_statistics_reset',
382 name='edit_repo_statistics_reset',
380 pattern='/{repo_name:.*?[^/]}/settings/statistics/update', repo_route=True)
383 pattern='/{repo_name:.*?[^/]}/settings/statistics/update', repo_route=True)
381
384
382 # Issue trackers
385 # Issue trackers
383 config.add_route(
386 config.add_route(
384 name='edit_repo_issuetracker',
387 name='edit_repo_issuetracker',
385 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers', repo_route=True)
388 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers', repo_route=True)
386 config.add_route(
389 config.add_route(
387 name='edit_repo_issuetracker_test',
390 name='edit_repo_issuetracker_test',
388 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/test', repo_route=True)
391 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/test', repo_route=True)
389 config.add_route(
392 config.add_route(
390 name='edit_repo_issuetracker_delete',
393 name='edit_repo_issuetracker_delete',
391 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/delete', repo_route=True)
394 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/delete', repo_route=True)
392 config.add_route(
395 config.add_route(
393 name='edit_repo_issuetracker_update',
396 name='edit_repo_issuetracker_update',
394 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/update', repo_route=True)
397 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/update', repo_route=True)
395
398
396 # VCS Settings
399 # VCS Settings
397 config.add_route(
400 config.add_route(
398 name='edit_repo_vcs',
401 name='edit_repo_vcs',
399 pattern='/{repo_name:.*?[^/]}/settings/vcs', repo_route=True)
402 pattern='/{repo_name:.*?[^/]}/settings/vcs', repo_route=True)
400 config.add_route(
403 config.add_route(
401 name='edit_repo_vcs_update',
404 name='edit_repo_vcs_update',
402 pattern='/{repo_name:.*?[^/]}/settings/vcs/update', repo_route=True)
405 pattern='/{repo_name:.*?[^/]}/settings/vcs/update', repo_route=True)
403
406
404 # svn pattern
407 # svn pattern
405 config.add_route(
408 config.add_route(
406 name='edit_repo_vcs_svn_pattern_delete',
409 name='edit_repo_vcs_svn_pattern_delete',
407 pattern='/{repo_name:.*?[^/]}/settings/vcs/svn_pattern/delete', repo_route=True)
410 pattern='/{repo_name:.*?[^/]}/settings/vcs/svn_pattern/delete', repo_route=True)
408
411
409 # Repo Review Rules (EE feature)
412 # Repo Review Rules (EE feature)
410 config.add_route(
413 config.add_route(
411 name='repo_reviewers',
414 name='repo_reviewers',
412 pattern='/{repo_name:.*?[^/]}/settings/review/rules', repo_route=True)
415 pattern='/{repo_name:.*?[^/]}/settings/review/rules', repo_route=True)
413
416
414 config.add_route(
417 config.add_route(
415 name='repo_default_reviewers_data',
418 name='repo_default_reviewers_data',
416 pattern='/{repo_name:.*?[^/]}/settings/review/default-reviewers', repo_route=True)
419 pattern='/{repo_name:.*?[^/]}/settings/review/default-reviewers', repo_route=True)
417
420
418 # Strip
421 # Strip
419 config.add_route(
422 config.add_route(
420 name='edit_repo_strip',
423 name='edit_repo_strip',
421 pattern='/{repo_name:.*?[^/]}/settings/strip', repo_route=True)
424 pattern='/{repo_name:.*?[^/]}/settings/strip', repo_route=True)
422
425
423 config.add_route(
426 config.add_route(
424 name='strip_check',
427 name='strip_check',
425 pattern='/{repo_name:.*?[^/]}/settings/strip_check', repo_route=True)
428 pattern='/{repo_name:.*?[^/]}/settings/strip_check', repo_route=True)
426
429
427 config.add_route(
430 config.add_route(
428 name='strip_execute',
431 name='strip_execute',
429 pattern='/{repo_name:.*?[^/]}/settings/strip_execute', repo_route=True)
432 pattern='/{repo_name:.*?[^/]}/settings/strip_execute', repo_route=True)
430
433
431 # ATOM/RSS Feed
434 # ATOM/RSS Feed
432 config.add_route(
435 config.add_route(
433 name='rss_feed_home',
436 name='rss_feed_home',
434 pattern='/{repo_name:.*?[^/]}/feed/rss', repo_route=True)
437 pattern='/{repo_name:.*?[^/]}/feed/rss', repo_route=True)
435
438
436 config.add_route(
439 config.add_route(
437 name='atom_feed_home',
440 name='atom_feed_home',
438 pattern='/{repo_name:.*?[^/]}/feed/atom', repo_route=True)
441 pattern='/{repo_name:.*?[^/]}/feed/atom', repo_route=True)
439
442
440 # NOTE(marcink): needs to be at the end for catch-all
443 # NOTE(marcink): needs to be at the end for catch-all
441 add_route_with_slash(
444 add_route_with_slash(
442 config,
445 config,
443 name='repo_summary',
446 name='repo_summary',
444 pattern='/{repo_name:.*?[^/]}', repo_route=True)
447 pattern='/{repo_name:.*?[^/]}', repo_route=True)
445
448
446 # Scan module for configuration decorators.
449 # Scan module for configuration decorators.
447 config.scan('.views', ignore='.tests')
450 config.scan('.views', ignore='.tests')
@@ -1,310 +1,336 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import logging
22 import logging
23
23
24 from pyramid.httpexceptions import HTTPNotFound, HTTPFound
24 from pyramid.httpexceptions import HTTPNotFound, HTTPFound
25 from pyramid.view import view_config
25 from pyramid.view import view_config
26 from pyramid.renderers import render
26 from pyramid.renderers import render
27 from pyramid.response import Response
27 from pyramid.response import Response
28
28
29 from rhodecode.apps._base import RepoAppView
29 from rhodecode.apps._base import RepoAppView
30 import rhodecode.lib.helpers as h
30 import rhodecode.lib.helpers as h
31 from rhodecode.lib.auth import (
31 from rhodecode.lib.auth import (
32 LoginRequired, HasRepoPermissionAnyDecorator)
32 LoginRequired, HasRepoPermissionAnyDecorator)
33
33
34 from rhodecode.lib.ext_json import json
34 from rhodecode.lib.ext_json import json
35 from rhodecode.lib.graphmod import _colored, _dagwalker
35 from rhodecode.lib.graphmod import _colored, _dagwalker
36 from rhodecode.lib.helpers import RepoPage
36 from rhodecode.lib.helpers import RepoPage
37 from rhodecode.lib.utils2 import safe_int, safe_str
37 from rhodecode.lib.utils2 import safe_int, safe_str
38 from rhodecode.lib.vcs.exceptions import (
38 from rhodecode.lib.vcs.exceptions import (
39 RepositoryError, CommitDoesNotExistError,
39 RepositoryError, CommitDoesNotExistError,
40 CommitError, NodeDoesNotExistError, EmptyRepositoryError)
40 CommitError, NodeDoesNotExistError, EmptyRepositoryError)
41
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
44 DEFAULT_CHANGELOG_SIZE = 20
44 DEFAULT_CHANGELOG_SIZE = 20
45
45
46
46
47 class RepoChangelogView(RepoAppView):
47 class RepoChangelogView(RepoAppView):
48
48
49 def _get_commit_or_redirect(self, commit_id, redirect_after=True):
49 def _get_commit_or_redirect(self, commit_id, redirect_after=True):
50 """
50 """
51 This is a safe way to get commit. If an error occurs it redirects to
51 This is a safe way to get commit. If an error occurs it redirects to
52 tip with proper message
52 tip with proper message
53
53
54 :param commit_id: id of commit to fetch
54 :param commit_id: id of commit to fetch
55 :param redirect_after: toggle redirection
55 :param redirect_after: toggle redirection
56 """
56 """
57 _ = self.request.translate
57 _ = self.request.translate
58
58
59 try:
59 try:
60 return self.rhodecode_vcs_repo.get_commit(commit_id)
60 return self.rhodecode_vcs_repo.get_commit(commit_id)
61 except EmptyRepositoryError:
61 except EmptyRepositoryError:
62 if not redirect_after:
62 if not redirect_after:
63 return None
63 return None
64
64
65 h.flash(h.literal(
65 h.flash(h.literal(
66 _('There are no commits yet')), category='warning')
66 _('There are no commits yet')), category='warning')
67 raise HTTPFound(
67 raise HTTPFound(
68 h.route_path('repo_summary', repo_name=self.db_repo_name))
68 h.route_path('repo_summary', repo_name=self.db_repo_name))
69
69
70 except (CommitDoesNotExistError, LookupError):
70 except (CommitDoesNotExistError, LookupError):
71 msg = _('No such commit exists for this repository')
71 msg = _('No such commit exists for this repository')
72 h.flash(msg, category='error')
72 h.flash(msg, category='error')
73 raise HTTPNotFound()
73 raise HTTPNotFound()
74 except RepositoryError as e:
74 except RepositoryError as e:
75 h.flash(safe_str(h.escape(e)), category='error')
75 h.flash(safe_str(h.escape(e)), category='error')
76 raise HTTPNotFound()
76 raise HTTPNotFound()
77
77
78 def _graph(self, repo, commits, prev_data=None, next_data=None):
78 def _graph(self, repo, commits, prev_data=None, next_data=None):
79 """
79 """
80 Generates a DAG graph for repo
80 Generates a DAG graph for repo
81
81
82 :param repo: repo instance
82 :param repo: repo instance
83 :param commits: list of commits
83 :param commits: list of commits
84 """
84 """
85 if not commits:
85 if not commits:
86 return json.dumps([])
86 return json.dumps([])
87
87
88 def serialize(commit, parents=True):
88 def serialize(commit, parents=True):
89 data = dict(
89 data = dict(
90 raw_id=commit.raw_id,
90 raw_id=commit.raw_id,
91 idx=commit.idx,
91 idx=commit.idx,
92 branch=commit.branch,
92 branch=commit.branch,
93 )
93 )
94 if parents:
94 if parents:
95 data['parents'] = [
95 data['parents'] = [
96 serialize(x, parents=False) for x in commit.parents]
96 serialize(x, parents=False) for x in commit.parents]
97 return data
97 return data
98
98
99 prev_data = prev_data or []
99 prev_data = prev_data or []
100 next_data = next_data or []
100 next_data = next_data or []
101
101
102 current = [serialize(x) for x in commits]
102 current = [serialize(x) for x in commits]
103 commits = prev_data + current + next_data
103 commits = prev_data + current + next_data
104
104
105 dag = _dagwalker(repo, commits)
105 dag = _dagwalker(repo, commits)
106
106
107 data = [[commit_id, vtx, edges, branch]
107 data = [[commit_id, vtx, edges, branch]
108 for commit_id, vtx, edges, branch in _colored(dag)]
108 for commit_id, vtx, edges, branch in _colored(dag)]
109 return json.dumps(data), json.dumps(current)
109 return json.dumps(data), json.dumps(current)
110
110
111 def _check_if_valid_branch(self, branch_name, repo_name, f_path):
111 def _check_if_valid_branch(self, branch_name, repo_name, f_path):
112 if branch_name not in self.rhodecode_vcs_repo.branches_all:
112 if branch_name not in self.rhodecode_vcs_repo.branches_all:
113 h.flash('Branch {} is not found.'.format(h.escape(branch_name)),
113 h.flash('Branch {} is not found.'.format(h.escape(branch_name)),
114 category='warning')
114 category='warning')
115 redirect_url = h.route_path(
115 redirect_url = h.route_path(
116 'repo_changelog_file', repo_name=repo_name,
116 'repo_changelog_file', repo_name=repo_name,
117 commit_id=branch_name, f_path=f_path or '')
117 commit_id=branch_name, f_path=f_path or '')
118 raise HTTPFound(redirect_url)
118 raise HTTPFound(redirect_url)
119
119
120 def _load_changelog_data(
120 def _load_changelog_data(
121 self, c, collection, page, chunk_size, branch_name=None,
121 self, c, collection, page, chunk_size, branch_name=None,
122 dynamic=False, f_path=None, commit_id=None):
122 dynamic=False, f_path=None, commit_id=None):
123
123
124 def url_generator(**kw):
124 def url_generator(**kw):
125 query_params = {}
125 query_params = {}
126 query_params.update(kw)
126 query_params.update(kw)
127 if f_path:
127 if f_path:
128 # changelog for file
128 # changelog for file
129 return h.route_path(
129 return h.route_path(
130 'repo_changelog_file',
130 'repo_changelog_file',
131 repo_name=c.rhodecode_db_repo.repo_name,
131 repo_name=c.rhodecode_db_repo.repo_name,
132 commit_id=commit_id, f_path=f_path,
132 commit_id=commit_id, f_path=f_path,
133 _query=query_params)
133 _query=query_params)
134 else:
134 else:
135 return h.route_path(
135 return h.route_path(
136 'repo_changelog',
136 'repo_changelog',
137 repo_name=c.rhodecode_db_repo.repo_name, _query=query_params)
137 repo_name=c.rhodecode_db_repo.repo_name, _query=query_params)
138
138
139 c.total_cs = len(collection)
139 c.total_cs = len(collection)
140 c.showing_commits = min(chunk_size, c.total_cs)
140 c.showing_commits = min(chunk_size, c.total_cs)
141 c.pagination = RepoPage(collection, page=page, item_count=c.total_cs,
141 c.pagination = RepoPage(collection, page=page, item_count=c.total_cs,
142 items_per_page=chunk_size, branch=branch_name,
142 items_per_page=chunk_size, branch=branch_name,
143 url=url_generator)
143 url=url_generator)
144
144
145 c.next_page = c.pagination.next_page
145 c.next_page = c.pagination.next_page
146 c.prev_page = c.pagination.previous_page
146 c.prev_page = c.pagination.previous_page
147
147
148 if dynamic:
148 if dynamic:
149 if self.request.GET.get('chunk') != 'next':
149 if self.request.GET.get('chunk') != 'next':
150 c.next_page = None
150 c.next_page = None
151 if self.request.GET.get('chunk') != 'prev':
151 if self.request.GET.get('chunk') != 'prev':
152 c.prev_page = None
152 c.prev_page = None
153
153
154 page_commit_ids = [x.raw_id for x in c.pagination]
154 page_commit_ids = [x.raw_id for x in c.pagination]
155 c.comments = c.rhodecode_db_repo.get_comments(page_commit_ids)
155 c.comments = c.rhodecode_db_repo.get_comments(page_commit_ids)
156 c.statuses = c.rhodecode_db_repo.statuses(page_commit_ids)
156 c.statuses = c.rhodecode_db_repo.statuses(page_commit_ids)
157
157
158 def load_default_context(self):
158 def load_default_context(self):
159 c = self._get_local_tmpl_context(include_app_defaults=True)
159 c = self._get_local_tmpl_context(include_app_defaults=True)
160
160
161 c.rhodecode_repo = self.rhodecode_vcs_repo
161 c.rhodecode_repo = self.rhodecode_vcs_repo
162 self._register_global_c(c)
162 self._register_global_c(c)
163 return c
163 return c
164
164
165 def _get_preload_attrs(self):
166 pre_load = ['author', 'branch', 'date', 'message', 'parents',
167 'obsolete', 'phase', 'hidden']
168 return pre_load
169
165 @LoginRequired()
170 @LoginRequired()
166 @HasRepoPermissionAnyDecorator(
171 @HasRepoPermissionAnyDecorator(
167 'repository.read', 'repository.write', 'repository.admin')
172 'repository.read', 'repository.write', 'repository.admin')
168 @view_config(
173 @view_config(
169 route_name='repo_changelog', request_method='GET',
174 route_name='repo_changelog', request_method='GET',
170 renderer='rhodecode:templates/changelog/changelog.mako')
175 renderer='rhodecode:templates/changelog/changelog.mako')
171 @view_config(
176 @view_config(
172 route_name='repo_changelog_file', request_method='GET',
177 route_name='repo_changelog_file', request_method='GET',
173 renderer='rhodecode:templates/changelog/changelog.mako')
178 renderer='rhodecode:templates/changelog/changelog.mako')
174 def repo_changelog(self):
179 def repo_changelog(self):
175 c = self.load_default_context()
180 c = self.load_default_context()
176
181
177 commit_id = self.request.matchdict.get('commit_id')
182 commit_id = self.request.matchdict.get('commit_id')
178 f_path = self._get_f_path(self.request.matchdict)
183 f_path = self._get_f_path(self.request.matchdict)
179
184
180 chunk_size = 20
185 chunk_size = 20
181
186
182 c.branch_name = branch_name = self.request.GET.get('branch') or ''
187 c.branch_name = branch_name = self.request.GET.get('branch') or ''
183 c.book_name = book_name = self.request.GET.get('bookmark') or ''
188 c.book_name = book_name = self.request.GET.get('bookmark') or ''
189 c.f_path = f_path
190 c.commit_id = commit_id
184 hist_limit = safe_int(self.request.GET.get('limit')) or None
191 hist_limit = safe_int(self.request.GET.get('limit')) or None
185
192
186 p = safe_int(self.request.GET.get('page', 1), 1)
193 p = safe_int(self.request.GET.get('page', 1), 1)
187
194
188 c.selected_name = branch_name or book_name
195 c.selected_name = branch_name or book_name
189 if not commit_id and branch_name:
196 if not commit_id and branch_name:
190 self._check_if_valid_branch(branch_name, self.db_repo_name, f_path)
197 self._check_if_valid_branch(branch_name, self.db_repo_name, f_path)
191
198
192 c.changelog_for_path = f_path
199 c.changelog_for_path = f_path
193 pre_load = ['author', 'branch', 'date', 'message', 'parents']
200 pre_load = self._get_preload_attrs()
194 commit_ids = []
201 commit_ids = []
195
202
196 partial_xhr = self.request.environ.get('HTTP_X_PARTIAL_XHR')
203 partial_xhr = self.request.environ.get('HTTP_X_PARTIAL_XHR')
197
204
198 try:
205 try:
199 if f_path:
206 if f_path:
200 log.debug('generating changelog for path %s', f_path)
207 log.debug('generating changelog for path %s', f_path)
201 # get the history for the file !
208 # get the history for the file !
202 base_commit = self.rhodecode_vcs_repo.get_commit(commit_id)
209 base_commit = self.rhodecode_vcs_repo.get_commit(commit_id)
210
203 try:
211 try:
204 collection = base_commit.get_file_history(
212 collection = base_commit.get_file_history(
205 f_path, limit=hist_limit, pre_load=pre_load)
213 f_path, limit=hist_limit, pre_load=pre_load)
206 if collection and partial_xhr:
214 if collection and partial_xhr:
207 # for ajax call we remove first one since we're looking
215 # for ajax call we remove first one since we're looking
208 # at it right now in the context of a file commit
216 # at it right now in the context of a file commit
209 collection.pop(0)
217 collection.pop(0)
210 except (NodeDoesNotExistError, CommitError):
218 except (NodeDoesNotExistError, CommitError):
211 # this node is not present at tip!
219 # this node is not present at tip!
212 try:
220 try:
213 commit = self._get_commit_or_redirect(commit_id)
221 commit = self._get_commit_or_redirect(commit_id)
214 collection = commit.get_file_history(f_path)
222 collection = commit.get_file_history(f_path)
215 except RepositoryError as e:
223 except RepositoryError as e:
216 h.flash(safe_str(e), category='warning')
224 h.flash(safe_str(e), category='warning')
217 redirect_url = h.route_path(
225 redirect_url = h.route_path(
218 'repo_changelog', repo_name=self.db_repo_name)
226 'repo_changelog', repo_name=self.db_repo_name)
219 raise HTTPFound(redirect_url)
227 raise HTTPFound(redirect_url)
220 collection = list(reversed(collection))
228 collection = list(reversed(collection))
221 else:
229 else:
222 collection = self.rhodecode_vcs_repo.get_commits(
230 collection = self.rhodecode_vcs_repo.get_commits(
223 branch_name=branch_name, pre_load=pre_load)
231 branch_name=branch_name, pre_load=pre_load)
224
232
225 self._load_changelog_data(
233 self._load_changelog_data(
226 c, collection, p, chunk_size, c.branch_name,
234 c, collection, p, chunk_size, c.branch_name,
227 f_path=f_path, commit_id=commit_id)
235 f_path=f_path, commit_id=commit_id)
228
236
229 except EmptyRepositoryError as e:
237 except EmptyRepositoryError as e:
230 h.flash(safe_str(h.escape(e)), category='warning')
238 h.flash(safe_str(h.escape(e)), category='warning')
231 raise HTTPFound(
239 raise HTTPFound(
232 h.route_path('repo_summary', repo_name=self.db_repo_name))
240 h.route_path('repo_summary', repo_name=self.db_repo_name))
233 except HTTPFound:
241 except HTTPFound:
234 raise
242 raise
235 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
243 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
236 log.exception(safe_str(e))
244 log.exception(safe_str(e))
237 h.flash(safe_str(h.escape(e)), category='error')
245 h.flash(safe_str(h.escape(e)), category='error')
238 raise HTTPFound(
246 raise HTTPFound(
239 h.route_path('repo_changelog', repo_name=self.db_repo_name))
247 h.route_path('repo_changelog', repo_name=self.db_repo_name))
240
248
241 if partial_xhr or self.request.environ.get('HTTP_X_PJAX'):
249 if partial_xhr or self.request.environ.get('HTTP_X_PJAX'):
250 # case when loading dynamic file history in file view
242 # loading from ajax, we don't want the first result, it's popped
251 # loading from ajax, we don't want the first result, it's popped
243 # in the code above
252 # in the code above
244 html = render(
253 html = render(
245 'rhodecode:templates/changelog/changelog_file_history.mako',
254 'rhodecode:templates/changelog/changelog_file_history.mako',
246 self._get_template_context(c), self.request)
255 self._get_template_context(c), self.request)
247 return Response(html)
256 return Response(html)
248
257
249 if not f_path:
258 if not f_path:
250 commit_ids = c.pagination
259 commit_ids = c.pagination
251
260
252 c.graph_data, c.graph_commits = self._graph(
261 c.graph_data, c.graph_commits = self._graph(
253 self.rhodecode_vcs_repo, commit_ids)
262 self.rhodecode_vcs_repo, commit_ids)
254
263
255 return self._get_template_context(c)
264 return self._get_template_context(c)
256
265
257 @LoginRequired()
266 @LoginRequired()
258 @HasRepoPermissionAnyDecorator(
267 @HasRepoPermissionAnyDecorator(
259 'repository.read', 'repository.write', 'repository.admin')
268 'repository.read', 'repository.write', 'repository.admin')
260 @view_config(
269 @view_config(
261 route_name='repo_changelog_elements', request_method=('GET', 'POST'),
270 route_name='repo_changelog_elements', request_method=('GET', 'POST'),
262 renderer='rhodecode:templates/changelog/changelog_elements.mako',
271 renderer='rhodecode:templates/changelog/changelog_elements.mako',
263 xhr=True)
272 xhr=True)
273 @view_config(
274 route_name='repo_changelog_elements_file', request_method=('GET', 'POST'),
275 renderer='rhodecode:templates/changelog/changelog_elements.mako',
276 xhr=True)
264 def repo_changelog_elements(self):
277 def repo_changelog_elements(self):
265 c = self.load_default_context()
278 c = self.load_default_context()
279 commit_id = self.request.matchdict.get('commit_id')
280 f_path = self._get_f_path(self.request.matchdict)
266 chunk_size = 20
281 chunk_size = 20
282 hist_limit = safe_int(self.request.GET.get('limit')) or None
267
283
268 def wrap_for_error(err):
284 def wrap_for_error(err):
269 html = '<tr>' \
285 html = '<tr>' \
270 '<td colspan="9" class="alert alert-error">ERROR: {}</td>' \
286 '<td colspan="9" class="alert alert-error">ERROR: {}</td>' \
271 '</tr>'.format(err)
287 '</tr>'.format(err)
272 return Response(html)
288 return Response(html)
273
289
274 c.branch_name = branch_name = self.request.GET.get('branch') or ''
290 c.branch_name = branch_name = self.request.GET.get('branch') or ''
275 c.book_name = book_name = self.request.GET.get('bookmark') or ''
291 c.book_name = book_name = self.request.GET.get('bookmark') or ''
292 c.f_path = f_path
293 c.commit_id = commit_id
276
294
277 c.selected_name = branch_name or book_name
295 c.selected_name = branch_name or book_name
278 if branch_name and branch_name not in self.rhodecode_vcs_repo.branches_all:
296 if branch_name and branch_name not in self.rhodecode_vcs_repo.branches_all:
279 return wrap_for_error(
297 return wrap_for_error(
280 safe_str('Branch: {} is not valid'.format(branch_name)))
298 safe_str('Branch: {} is not valid'.format(branch_name)))
281
299
282 pre_load = ['author', 'branch', 'date', 'message', 'parents']
300 pre_load = self._get_preload_attrs()
301
302 if f_path:
303 base_commit = self.rhodecode_vcs_repo.get_commit(commit_id)
304 collection = base_commit.get_file_history(
305 f_path, limit=hist_limit, pre_load=pre_load)
306 collection = list(reversed(collection))
307 else:
283 collection = self.rhodecode_vcs_repo.get_commits(
308 collection = self.rhodecode_vcs_repo.get_commits(
284 branch_name=branch_name, pre_load=pre_load)
309 branch_name=branch_name, pre_load=pre_load)
285
310
286 p = safe_int(self.request.GET.get('page', 1), 1)
311 p = safe_int(self.request.GET.get('page', 1), 1)
287 try:
312 try:
288 self._load_changelog_data(
313 self._load_changelog_data(
289 c, collection, p, chunk_size, dynamic=True)
314 c, collection, p, chunk_size, dynamic=True,
315 f_path=f_path, commit_id=commit_id)
290 except EmptyRepositoryError as e:
316 except EmptyRepositoryError as e:
291 return wrap_for_error(safe_str(e))
317 return wrap_for_error(safe_str(e))
292 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
318 except (RepositoryError, CommitDoesNotExistError, Exception) as e:
293 log.exception('Failed to fetch commits')
319 log.exception('Failed to fetch commits')
294 return wrap_for_error(safe_str(e))
320 return wrap_for_error(safe_str(e))
295
321
296 prev_data = None
322 prev_data = None
297 next_data = None
323 next_data = None
298
324
299 prev_graph = json.loads(self.request.POST.get('graph', ''))
325 prev_graph = json.loads(self.request.POST.get('graph') or '{}')
300
326
301 if self.request.GET.get('chunk') == 'prev':
327 if self.request.GET.get('chunk') == 'prev':
302 next_data = prev_graph
328 next_data = prev_graph
303 elif self.request.GET.get('chunk') == 'next':
329 elif self.request.GET.get('chunk') == 'next':
304 prev_data = prev_graph
330 prev_data = prev_graph
305
331
306 c.graph_data, c.graph_commits = self._graph(
332 c.graph_data, c.graph_commits = self._graph(
307 self.rhodecode_vcs_repo, c.pagination,
333 self.rhodecode_vcs_repo, c.pagination,
308 prev_data=prev_data, next_data=next_data)
334 prev_data=prev_data, next_data=next_data)
309
335
310 return self._get_template_context(c)
336 return self._get_template_context(c)
@@ -1,538 +1,545 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2017 RhodeCode GmbH
3 # Copyright (C) 2014-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 GIT commit module
22 GIT commit module
23 """
23 """
24
24
25 import re
25 import re
26 import stat
26 import stat
27 from ConfigParser import ConfigParser
27 from ConfigParser import ConfigParser
28 from itertools import chain
28 from itertools import chain
29 from StringIO import StringIO
29 from StringIO import StringIO
30
30
31 from zope.cachedescriptors.property import Lazy as LazyProperty
31 from zope.cachedescriptors.property import Lazy as LazyProperty
32
32
33 from rhodecode.lib.datelib import utcdate_fromtimestamp
33 from rhodecode.lib.datelib import utcdate_fromtimestamp
34 from rhodecode.lib.utils import safe_unicode, safe_str
34 from rhodecode.lib.utils import safe_unicode, safe_str
35 from rhodecode.lib.utils2 import safe_int
35 from rhodecode.lib.utils2 import safe_int
36 from rhodecode.lib.vcs.conf import settings
36 from rhodecode.lib.vcs.conf import settings
37 from rhodecode.lib.vcs.backends import base
37 from rhodecode.lib.vcs.backends import base
38 from rhodecode.lib.vcs.exceptions import CommitError, NodeDoesNotExistError
38 from rhodecode.lib.vcs.exceptions import CommitError, NodeDoesNotExistError
39 from rhodecode.lib.vcs.nodes import (
39 from rhodecode.lib.vcs.nodes import (
40 FileNode, DirNode, NodeKind, RootNode, SubModuleNode,
40 FileNode, DirNode, NodeKind, RootNode, SubModuleNode,
41 ChangedFileNodesGenerator, AddedFileNodesGenerator,
41 ChangedFileNodesGenerator, AddedFileNodesGenerator,
42 RemovedFileNodesGenerator, LargeFileNode)
42 RemovedFileNodesGenerator, LargeFileNode)
43
43
44
44
45 class GitCommit(base.BaseCommit):
45 class GitCommit(base.BaseCommit):
46 """
46 """
47 Represents state of the repository at single commit id.
47 Represents state of the repository at single commit id.
48 """
48 """
49 _author_property = 'author'
49 _author_property = 'author'
50 _committer_property = 'committer'
50 _committer_property = 'committer'
51 _date_property = 'commit_time'
51 _date_property = 'commit_time'
52 _date_tz_property = 'commit_timezone'
52 _date_tz_property = 'commit_timezone'
53 _message_property = 'message'
53 _message_property = 'message'
54 _parents_property = 'parents'
54 _parents_property = 'parents'
55
55
56 _filter_pre_load = [
56 _filter_pre_load = [
57 # done through a more complex tree walk on parents
57 # done through a more complex tree walk on parents
58 "affected_files",
58 "affected_files",
59 # based on repository cached property
59 # based on repository cached property
60 "branch",
60 "branch",
61 # done through subprocess not remote call
61 # done through subprocess not remote call
62 "children",
62 "children",
63 # done through a more complex tree walk on parents
63 # done through a more complex tree walk on parents
64 "status",
64 "status",
65 # mercurial specific property not supported here
65 # mercurial specific property not supported here
66 "_file_paths",
66 "_file_paths",
67 # mercurial specific property not supported here
68 'obsolete',
69 # mercurial specific property not supported here
70 'phase',
71 # mercurial specific property not supported here
72 'hidden'
67 ]
73 ]
68
74
69 def __init__(self, repository, raw_id, idx, pre_load=None):
75 def __init__(self, repository, raw_id, idx, pre_load=None):
70 self.repository = repository
76 self.repository = repository
71 self._remote = repository._remote
77 self._remote = repository._remote
72 # TODO: johbo: Tweak of raw_id should not be necessary
78 # TODO: johbo: Tweak of raw_id should not be necessary
73 self.raw_id = safe_str(raw_id)
79 self.raw_id = safe_str(raw_id)
74 self.idx = idx
80 self.idx = idx
75
81
76 self._set_bulk_properties(pre_load)
82 self._set_bulk_properties(pre_load)
77
83
78 # caches
84 # caches
79 self._stat_modes = {} # stat info for paths
85 self._stat_modes = {} # stat info for paths
80 self._paths = {} # path processed with parse_tree
86 self._paths = {} # path processed with parse_tree
81 self.nodes = {}
87 self.nodes = {}
82 self._submodules = None
88 self._submodules = None
83
89
84 def _set_bulk_properties(self, pre_load):
90 def _set_bulk_properties(self, pre_load):
85 if not pre_load:
91 if not pre_load:
86 return
92 return
87 pre_load = [entry for entry in pre_load
93 pre_load = [entry for entry in pre_load
88 if entry not in self._filter_pre_load]
94 if entry not in self._filter_pre_load]
89 if not pre_load:
95 if not pre_load:
90 return
96 return
91
97
92 result = self._remote.bulk_request(self.raw_id, pre_load)
98 result = self._remote.bulk_request(self.raw_id, pre_load)
93 for attr, value in result.items():
99 for attr, value in result.items():
94 if attr in ["author", "message"]:
100 if attr in ["author", "message"]:
95 if value:
101 if value:
96 value = safe_unicode(value)
102 value = safe_unicode(value)
97 elif attr == "date":
103 elif attr == "date":
98 value = utcdate_fromtimestamp(*value)
104 value = utcdate_fromtimestamp(*value)
99 elif attr == "parents":
105 elif attr == "parents":
100 value = self._make_commits(value)
106 value = self._make_commits(value)
101 self.__dict__[attr] = value
107 self.__dict__[attr] = value
102
108
103 @LazyProperty
109 @LazyProperty
104 def _commit(self):
110 def _commit(self):
105 return self._remote[self.raw_id]
111 return self._remote[self.raw_id]
106
112
107 @LazyProperty
113 @LazyProperty
108 def _tree_id(self):
114 def _tree_id(self):
109 return self._remote[self._commit['tree']]['id']
115 return self._remote[self._commit['tree']]['id']
110
116
111 @LazyProperty
117 @LazyProperty
112 def id(self):
118 def id(self):
113 return self.raw_id
119 return self.raw_id
114
120
115 @LazyProperty
121 @LazyProperty
116 def short_id(self):
122 def short_id(self):
117 return self.raw_id[:12]
123 return self.raw_id[:12]
118
124
119 @LazyProperty
125 @LazyProperty
120 def message(self):
126 def message(self):
121 return safe_unicode(
127 return safe_unicode(
122 self._remote.commit_attribute(self.id, self._message_property))
128 self._remote.commit_attribute(self.id, self._message_property))
123
129
124 @LazyProperty
130 @LazyProperty
125 def committer(self):
131 def committer(self):
126 return safe_unicode(
132 return safe_unicode(
127 self._remote.commit_attribute(self.id, self._committer_property))
133 self._remote.commit_attribute(self.id, self._committer_property))
128
134
129 @LazyProperty
135 @LazyProperty
130 def author(self):
136 def author(self):
131 return safe_unicode(
137 return safe_unicode(
132 self._remote.commit_attribute(self.id, self._author_property))
138 self._remote.commit_attribute(self.id, self._author_property))
133
139
134 @LazyProperty
140 @LazyProperty
135 def date(self):
141 def date(self):
136 unix_ts, tz = self._remote.get_object_attrs(
142 unix_ts, tz = self._remote.get_object_attrs(
137 self.raw_id, self._date_property, self._date_tz_property)
143 self.raw_id, self._date_property, self._date_tz_property)
138 return utcdate_fromtimestamp(unix_ts, tz)
144 return utcdate_fromtimestamp(unix_ts, tz)
139
145
140 @LazyProperty
146 @LazyProperty
141 def status(self):
147 def status(self):
142 """
148 """
143 Returns modified, added, removed, deleted files for current commit
149 Returns modified, added, removed, deleted files for current commit
144 """
150 """
145 return self.changed, self.added, self.removed
151 return self.changed, self.added, self.removed
146
152
147 @LazyProperty
153 @LazyProperty
148 def tags(self):
154 def tags(self):
149 tags = [safe_unicode(name) for name,
155 tags = [safe_unicode(name) for name,
150 commit_id in self.repository.tags.iteritems()
156 commit_id in self.repository.tags.iteritems()
151 if commit_id == self.raw_id]
157 if commit_id == self.raw_id]
152 return tags
158 return tags
153
159
154 @LazyProperty
160 @LazyProperty
155 def branch(self):
161 def branch(self):
156 for name, commit_id in self.repository.branches.iteritems():
162 for name, commit_id in self.repository.branches.iteritems():
157 if commit_id == self.raw_id:
163 if commit_id == self.raw_id:
158 return safe_unicode(name)
164 return safe_unicode(name)
159 return None
165 return None
160
166
161 def _get_id_for_path(self, path):
167 def _get_id_for_path(self, path):
162 path = safe_str(path)
168 path = safe_str(path)
163 if path in self._paths:
169 if path in self._paths:
164 return self._paths[path]
170 return self._paths[path]
165
171
166 tree_id = self._tree_id
172 tree_id = self._tree_id
167
173
168 path = path.strip('/')
174 path = path.strip('/')
169 if path == '':
175 if path == '':
170 data = [tree_id, "tree"]
176 data = [tree_id, "tree"]
171 self._paths[''] = data
177 self._paths[''] = data
172 return data
178 return data
173
179
174 parts = path.split('/')
180 parts = path.split('/')
175 dirs, name = parts[:-1], parts[-1]
181 dirs, name = parts[:-1], parts[-1]
176 cur_dir = ''
182 cur_dir = ''
177
183
178 # initially extract things from root dir
184 # initially extract things from root dir
179 tree_items = self._remote.tree_items(tree_id)
185 tree_items = self._remote.tree_items(tree_id)
180 self._process_tree_items(tree_items, cur_dir)
186 self._process_tree_items(tree_items, cur_dir)
181
187
182 for dir in dirs:
188 for dir in dirs:
183 if cur_dir:
189 if cur_dir:
184 cur_dir = '/'.join((cur_dir, dir))
190 cur_dir = '/'.join((cur_dir, dir))
185 else:
191 else:
186 cur_dir = dir
192 cur_dir = dir
187 dir_id = None
193 dir_id = None
188 for item, stat_, id_, type_ in tree_items:
194 for item, stat_, id_, type_ in tree_items:
189 if item == dir:
195 if item == dir:
190 dir_id = id_
196 dir_id = id_
191 break
197 break
192 if dir_id:
198 if dir_id:
193 if type_ != "tree":
199 if type_ != "tree":
194 raise CommitError('%s is not a directory' % cur_dir)
200 raise CommitError('%s is not a directory' % cur_dir)
195 # update tree
201 # update tree
196 tree_items = self._remote.tree_items(dir_id)
202 tree_items = self._remote.tree_items(dir_id)
197 else:
203 else:
198 raise CommitError('%s have not been found' % cur_dir)
204 raise CommitError('%s have not been found' % cur_dir)
199
205
200 # cache all items from the given traversed tree
206 # cache all items from the given traversed tree
201 self._process_tree_items(tree_items, cur_dir)
207 self._process_tree_items(tree_items, cur_dir)
202
208
203 if path not in self._paths:
209 if path not in self._paths:
204 raise self.no_node_at_path(path)
210 raise self.no_node_at_path(path)
205
211
206 return self._paths[path]
212 return self._paths[path]
207
213
208 def _process_tree_items(self, items, cur_dir):
214 def _process_tree_items(self, items, cur_dir):
209 for item, stat_, id_, type_ in items:
215 for item, stat_, id_, type_ in items:
210 if cur_dir:
216 if cur_dir:
211 name = '/'.join((cur_dir, item))
217 name = '/'.join((cur_dir, item))
212 else:
218 else:
213 name = item
219 name = item
214 self._paths[name] = [id_, type_]
220 self._paths[name] = [id_, type_]
215 self._stat_modes[name] = stat_
221 self._stat_modes[name] = stat_
216
222
217 def _get_kind(self, path):
223 def _get_kind(self, path):
218 path_id, type_ = self._get_id_for_path(path)
224 path_id, type_ = self._get_id_for_path(path)
219 if type_ == 'blob':
225 if type_ == 'blob':
220 return NodeKind.FILE
226 return NodeKind.FILE
221 elif type_ == 'tree':
227 elif type_ == 'tree':
222 return NodeKind.DIR
228 return NodeKind.DIR
223 elif type == 'link':
229 elif type == 'link':
224 return NodeKind.SUBMODULE
230 return NodeKind.SUBMODULE
225 return None
231 return None
226
232
227 def _get_filectx(self, path):
233 def _get_filectx(self, path):
228 path = self._fix_path(path)
234 path = self._fix_path(path)
229 if self._get_kind(path) != NodeKind.FILE:
235 if self._get_kind(path) != NodeKind.FILE:
230 raise CommitError(
236 raise CommitError(
231 "File does not exist for commit %s at '%s'" %
237 "File does not exist for commit %s at '%s'" %
232 (self.raw_id, path))
238 (self.raw_id, path))
233 return path
239 return path
234
240
235 def _get_file_nodes(self):
241 def _get_file_nodes(self):
236 return chain(*(t[2] for t in self.walk()))
242 return chain(*(t[2] for t in self.walk()))
237
243
238 @LazyProperty
244 @LazyProperty
239 def parents(self):
245 def parents(self):
240 """
246 """
241 Returns list of parent commits.
247 Returns list of parent commits.
242 """
248 """
243 parent_ids = self._remote.commit_attribute(
249 parent_ids = self._remote.commit_attribute(
244 self.id, self._parents_property)
250 self.id, self._parents_property)
245 return self._make_commits(parent_ids)
251 return self._make_commits(parent_ids)
246
252
247 @LazyProperty
253 @LazyProperty
248 def children(self):
254 def children(self):
249 """
255 """
250 Returns list of child commits.
256 Returns list of child commits.
251 """
257 """
252 rev_filter = settings.GIT_REV_FILTER
258 rev_filter = settings.GIT_REV_FILTER
253 output, __ = self.repository.run_git_command(
259 output, __ = self.repository.run_git_command(
254 ['rev-list', '--children'] + rev_filter)
260 ['rev-list', '--children'] + rev_filter)
255
261
256 child_ids = []
262 child_ids = []
257 pat = re.compile(r'^%s' % self.raw_id)
263 pat = re.compile(r'^%s' % self.raw_id)
258 for l in output.splitlines():
264 for l in output.splitlines():
259 if pat.match(l):
265 if pat.match(l):
260 found_ids = l.split(' ')[1:]
266 found_ids = l.split(' ')[1:]
261 child_ids.extend(found_ids)
267 child_ids.extend(found_ids)
262 return self._make_commits(child_ids)
268 return self._make_commits(child_ids)
263
269
264 def _make_commits(self, commit_ids):
270 def _make_commits(self, commit_ids, pre_load=None):
265 return [self.repository.get_commit(commit_id=commit_id)
271 return [
272 self.repository.get_commit(commit_id=commit_id, pre_load=pre_load)
266 for commit_id in commit_ids]
273 for commit_id in commit_ids]
267
274
268 def get_file_mode(self, path):
275 def get_file_mode(self, path):
269 """
276 """
270 Returns stat mode of the file at the given `path`.
277 Returns stat mode of the file at the given `path`.
271 """
278 """
272 path = safe_str(path)
279 path = safe_str(path)
273 # ensure path is traversed
280 # ensure path is traversed
274 self._get_id_for_path(path)
281 self._get_id_for_path(path)
275 return self._stat_modes[path]
282 return self._stat_modes[path]
276
283
277 def is_link(self, path):
284 def is_link(self, path):
278 return stat.S_ISLNK(self.get_file_mode(path))
285 return stat.S_ISLNK(self.get_file_mode(path))
279
286
280 def get_file_content(self, path):
287 def get_file_content(self, path):
281 """
288 """
282 Returns content of the file at given `path`.
289 Returns content of the file at given `path`.
283 """
290 """
284 id_, _ = self._get_id_for_path(path)
291 id_, _ = self._get_id_for_path(path)
285 return self._remote.blob_as_pretty_string(id_)
292 return self._remote.blob_as_pretty_string(id_)
286
293
287 def get_file_size(self, path):
294 def get_file_size(self, path):
288 """
295 """
289 Returns size of the file at given `path`.
296 Returns size of the file at given `path`.
290 """
297 """
291 id_, _ = self._get_id_for_path(path)
298 id_, _ = self._get_id_for_path(path)
292 return self._remote.blob_raw_length(id_)
299 return self._remote.blob_raw_length(id_)
293
300
294 def get_file_history(self, path, limit=None, pre_load=None):
301 def get_file_history(self, path, limit=None, pre_load=None):
295 """
302 """
296 Returns history of file as reversed list of `GitCommit` objects for
303 Returns history of file as reversed list of `GitCommit` objects for
297 which file at given `path` has been modified.
304 which file at given `path` has been modified.
298
305
299 TODO: This function now uses an underlying 'git' command which works
306 TODO: This function now uses an underlying 'git' command which works
300 quickly but ideally we should replace with an algorithm.
307 quickly but ideally we should replace with an algorithm.
301 """
308 """
302 self._get_filectx(path)
309 self._get_filectx(path)
303 f_path = safe_str(path)
310 f_path = safe_str(path)
304
311
305 cmd = ['log']
312 cmd = ['log']
306 if limit:
313 if limit:
307 cmd.extend(['-n', str(safe_int(limit, 0))])
314 cmd.extend(['-n', str(safe_int(limit, 0))])
308 cmd.extend(['--pretty=format: %H', '-s', self.raw_id, '--', f_path])
315 cmd.extend(['--pretty=format: %H', '-s', self.raw_id, '--', f_path])
309
316
310 output, __ = self.repository.run_git_command(cmd)
317 output, __ = self.repository.run_git_command(cmd)
311 commit_ids = re.findall(r'[0-9a-fA-F]{40}', output)
318 commit_ids = re.findall(r'[0-9a-fA-F]{40}', output)
312
319
313 return [
320 return [
314 self.repository.get_commit(commit_id=commit_id, pre_load=pre_load)
321 self.repository.get_commit(commit_id=commit_id, pre_load=pre_load)
315 for commit_id in commit_ids]
322 for commit_id in commit_ids]
316
323
317 # TODO: unused for now potential replacement for subprocess
324 # TODO: unused for now potential replacement for subprocess
318 def get_file_history_2(self, path, limit=None, pre_load=None):
325 def get_file_history_2(self, path, limit=None, pre_load=None):
319 """
326 """
320 Returns history of file as reversed list of `Commit` objects for
327 Returns history of file as reversed list of `Commit` objects for
321 which file at given `path` has been modified.
328 which file at given `path` has been modified.
322 """
329 """
323 self._get_filectx(path)
330 self._get_filectx(path)
324 f_path = safe_str(path)
331 f_path = safe_str(path)
325
332
326 commit_ids = self._remote.get_file_history(f_path, self.id, limit)
333 commit_ids = self._remote.get_file_history(f_path, self.id, limit)
327
334
328 return [
335 return [
329 self.repository.get_commit(commit_id=commit_id, pre_load=pre_load)
336 self.repository.get_commit(commit_id=commit_id, pre_load=pre_load)
330 for commit_id in commit_ids]
337 for commit_id in commit_ids]
331
338
332 def get_file_annotate(self, path, pre_load=None):
339 def get_file_annotate(self, path, pre_load=None):
333 """
340 """
334 Returns a generator of four element tuples with
341 Returns a generator of four element tuples with
335 lineno, commit_id, commit lazy loader and line
342 lineno, commit_id, commit lazy loader and line
336
343
337 TODO: This function now uses os underlying 'git' command which is
344 TODO: This function now uses os underlying 'git' command which is
338 generally not good. Should be replaced with algorithm iterating
345 generally not good. Should be replaced with algorithm iterating
339 commits.
346 commits.
340 """
347 """
341 cmd = ['blame', '-l', '--root', '-r', self.raw_id, '--', path]
348 cmd = ['blame', '-l', '--root', '-r', self.raw_id, '--', path]
342 # -l ==> outputs long shas (and we need all 40 characters)
349 # -l ==> outputs long shas (and we need all 40 characters)
343 # --root ==> doesn't put '^' character for bounderies
350 # --root ==> doesn't put '^' character for bounderies
344 # -r commit_id ==> blames for the given commit
351 # -r commit_id ==> blames for the given commit
345 output, __ = self.repository.run_git_command(cmd)
352 output, __ = self.repository.run_git_command(cmd)
346
353
347 for i, blame_line in enumerate(output.split('\n')[:-1]):
354 for i, blame_line in enumerate(output.split('\n')[:-1]):
348 line_no = i + 1
355 line_no = i + 1
349 commit_id, line = re.split(r' ', blame_line, 1)
356 commit_id, line = re.split(r' ', blame_line, 1)
350 yield (
357 yield (
351 line_no, commit_id,
358 line_no, commit_id,
352 lambda: self.repository.get_commit(commit_id=commit_id,
359 lambda: self.repository.get_commit(commit_id=commit_id,
353 pre_load=pre_load),
360 pre_load=pre_load),
354 line)
361 line)
355
362
356 def get_nodes(self, path):
363 def get_nodes(self, path):
357 if self._get_kind(path) != NodeKind.DIR:
364 if self._get_kind(path) != NodeKind.DIR:
358 raise CommitError(
365 raise CommitError(
359 "Directory does not exist for commit %s at "
366 "Directory does not exist for commit %s at "
360 " '%s'" % (self.raw_id, path))
367 " '%s'" % (self.raw_id, path))
361 path = self._fix_path(path)
368 path = self._fix_path(path)
362 id_, _ = self._get_id_for_path(path)
369 id_, _ = self._get_id_for_path(path)
363 tree_id = self._remote[id_]['id']
370 tree_id = self._remote[id_]['id']
364 dirnodes = []
371 dirnodes = []
365 filenodes = []
372 filenodes = []
366 alias = self.repository.alias
373 alias = self.repository.alias
367 for name, stat_, id_, type_ in self._remote.tree_items(tree_id):
374 for name, stat_, id_, type_ in self._remote.tree_items(tree_id):
368 if type_ == 'link':
375 if type_ == 'link':
369 url = self._get_submodule_url('/'.join((path, name)))
376 url = self._get_submodule_url('/'.join((path, name)))
370 dirnodes.append(SubModuleNode(
377 dirnodes.append(SubModuleNode(
371 name, url=url, commit=id_, alias=alias))
378 name, url=url, commit=id_, alias=alias))
372 continue
379 continue
373
380
374 if path != '':
381 if path != '':
375 obj_path = '/'.join((path, name))
382 obj_path = '/'.join((path, name))
376 else:
383 else:
377 obj_path = name
384 obj_path = name
378 if obj_path not in self._stat_modes:
385 if obj_path not in self._stat_modes:
379 self._stat_modes[obj_path] = stat_
386 self._stat_modes[obj_path] = stat_
380
387
381 if type_ == 'tree':
388 if type_ == 'tree':
382 dirnodes.append(DirNode(obj_path, commit=self))
389 dirnodes.append(DirNode(obj_path, commit=self))
383 elif type_ == 'blob':
390 elif type_ == 'blob':
384 filenodes.append(FileNode(obj_path, commit=self, mode=stat_))
391 filenodes.append(FileNode(obj_path, commit=self, mode=stat_))
385 else:
392 else:
386 raise CommitError(
393 raise CommitError(
387 "Requested object should be Tree or Blob, is %s", type_)
394 "Requested object should be Tree or Blob, is %s", type_)
388
395
389 nodes = dirnodes + filenodes
396 nodes = dirnodes + filenodes
390 for node in nodes:
397 for node in nodes:
391 if node.path not in self.nodes:
398 if node.path not in self.nodes:
392 self.nodes[node.path] = node
399 self.nodes[node.path] = node
393 nodes.sort()
400 nodes.sort()
394 return nodes
401 return nodes
395
402
396 def get_node(self, path, pre_load=None):
403 def get_node(self, path, pre_load=None):
397 if isinstance(path, unicode):
404 if isinstance(path, unicode):
398 path = path.encode('utf-8')
405 path = path.encode('utf-8')
399 path = self._fix_path(path)
406 path = self._fix_path(path)
400 if path not in self.nodes:
407 if path not in self.nodes:
401 try:
408 try:
402 id_, type_ = self._get_id_for_path(path)
409 id_, type_ = self._get_id_for_path(path)
403 except CommitError:
410 except CommitError:
404 raise NodeDoesNotExistError(
411 raise NodeDoesNotExistError(
405 "Cannot find one of parents' directories for a given "
412 "Cannot find one of parents' directories for a given "
406 "path: %s" % path)
413 "path: %s" % path)
407
414
408 if type_ == 'link':
415 if type_ == 'link':
409 url = self._get_submodule_url(path)
416 url = self._get_submodule_url(path)
410 node = SubModuleNode(path, url=url, commit=id_,
417 node = SubModuleNode(path, url=url, commit=id_,
411 alias=self.repository.alias)
418 alias=self.repository.alias)
412 elif type_ == 'tree':
419 elif type_ == 'tree':
413 if path == '':
420 if path == '':
414 node = RootNode(commit=self)
421 node = RootNode(commit=self)
415 else:
422 else:
416 node = DirNode(path, commit=self)
423 node = DirNode(path, commit=self)
417 elif type_ == 'blob':
424 elif type_ == 'blob':
418 node = FileNode(path, commit=self, pre_load=pre_load)
425 node = FileNode(path, commit=self, pre_load=pre_load)
419 else:
426 else:
420 raise self.no_node_at_path(path)
427 raise self.no_node_at_path(path)
421
428
422 # cache node
429 # cache node
423 self.nodes[path] = node
430 self.nodes[path] = node
424 return self.nodes[path]
431 return self.nodes[path]
425
432
426 def get_largefile_node(self, path):
433 def get_largefile_node(self, path):
427 id_, _ = self._get_id_for_path(path)
434 id_, _ = self._get_id_for_path(path)
428 pointer_spec = self._remote.is_large_file(id_)
435 pointer_spec = self._remote.is_large_file(id_)
429
436
430 if pointer_spec:
437 if pointer_spec:
431 # content of that file regular FileNode is the hash of largefile
438 # content of that file regular FileNode is the hash of largefile
432 file_id = pointer_spec.get('oid_hash')
439 file_id = pointer_spec.get('oid_hash')
433 if self._remote.in_largefiles_store(file_id):
440 if self._remote.in_largefiles_store(file_id):
434 lf_path = self._remote.store_path(file_id)
441 lf_path = self._remote.store_path(file_id)
435 return LargeFileNode(lf_path, commit=self, org_path=path)
442 return LargeFileNode(lf_path, commit=self, org_path=path)
436
443
437 @LazyProperty
444 @LazyProperty
438 def affected_files(self):
445 def affected_files(self):
439 """
446 """
440 Gets a fast accessible file changes for given commit
447 Gets a fast accessible file changes for given commit
441 """
448 """
442 added, modified, deleted = self._changes_cache
449 added, modified, deleted = self._changes_cache
443 return list(added.union(modified).union(deleted))
450 return list(added.union(modified).union(deleted))
444
451
445 @LazyProperty
452 @LazyProperty
446 def _changes_cache(self):
453 def _changes_cache(self):
447 added = set()
454 added = set()
448 modified = set()
455 modified = set()
449 deleted = set()
456 deleted = set()
450 _r = self._remote
457 _r = self._remote
451
458
452 parents = self.parents
459 parents = self.parents
453 if not self.parents:
460 if not self.parents:
454 parents = [base.EmptyCommit()]
461 parents = [base.EmptyCommit()]
455 for parent in parents:
462 for parent in parents:
456 if isinstance(parent, base.EmptyCommit):
463 if isinstance(parent, base.EmptyCommit):
457 oid = None
464 oid = None
458 else:
465 else:
459 oid = parent.raw_id
466 oid = parent.raw_id
460 changes = _r.tree_changes(oid, self.raw_id)
467 changes = _r.tree_changes(oid, self.raw_id)
461 for (oldpath, newpath), (_, _), (_, _) in changes:
468 for (oldpath, newpath), (_, _), (_, _) in changes:
462 if newpath and oldpath:
469 if newpath and oldpath:
463 modified.add(newpath)
470 modified.add(newpath)
464 elif newpath and not oldpath:
471 elif newpath and not oldpath:
465 added.add(newpath)
472 added.add(newpath)
466 elif not newpath and oldpath:
473 elif not newpath and oldpath:
467 deleted.add(oldpath)
474 deleted.add(oldpath)
468 return added, modified, deleted
475 return added, modified, deleted
469
476
470 def _get_paths_for_status(self, status):
477 def _get_paths_for_status(self, status):
471 """
478 """
472 Returns sorted list of paths for given ``status``.
479 Returns sorted list of paths for given ``status``.
473
480
474 :param status: one of: *added*, *modified* or *deleted*
481 :param status: one of: *added*, *modified* or *deleted*
475 """
482 """
476 added, modified, deleted = self._changes_cache
483 added, modified, deleted = self._changes_cache
477 return sorted({
484 return sorted({
478 'added': list(added),
485 'added': list(added),
479 'modified': list(modified),
486 'modified': list(modified),
480 'deleted': list(deleted)}[status]
487 'deleted': list(deleted)}[status]
481 )
488 )
482
489
483 @LazyProperty
490 @LazyProperty
484 def added(self):
491 def added(self):
485 """
492 """
486 Returns list of added ``FileNode`` objects.
493 Returns list of added ``FileNode`` objects.
487 """
494 """
488 if not self.parents:
495 if not self.parents:
489 return list(self._get_file_nodes())
496 return list(self._get_file_nodes())
490 return AddedFileNodesGenerator(
497 return AddedFileNodesGenerator(
491 [n for n in self._get_paths_for_status('added')], self)
498 [n for n in self._get_paths_for_status('added')], self)
492
499
493 @LazyProperty
500 @LazyProperty
494 def changed(self):
501 def changed(self):
495 """
502 """
496 Returns list of modified ``FileNode`` objects.
503 Returns list of modified ``FileNode`` objects.
497 """
504 """
498 if not self.parents:
505 if not self.parents:
499 return []
506 return []
500 return ChangedFileNodesGenerator(
507 return ChangedFileNodesGenerator(
501 [n for n in self._get_paths_for_status('modified')], self)
508 [n for n in self._get_paths_for_status('modified')], self)
502
509
503 @LazyProperty
510 @LazyProperty
504 def removed(self):
511 def removed(self):
505 """
512 """
506 Returns list of removed ``FileNode`` objects.
513 Returns list of removed ``FileNode`` objects.
507 """
514 """
508 if not self.parents:
515 if not self.parents:
509 return []
516 return []
510 return RemovedFileNodesGenerator(
517 return RemovedFileNodesGenerator(
511 [n for n in self._get_paths_for_status('deleted')], self)
518 [n for n in self._get_paths_for_status('deleted')], self)
512
519
513 def _get_submodule_url(self, submodule_path):
520 def _get_submodule_url(self, submodule_path):
514 git_modules_path = '.gitmodules'
521 git_modules_path = '.gitmodules'
515
522
516 if self._submodules is None:
523 if self._submodules is None:
517 self._submodules = {}
524 self._submodules = {}
518
525
519 try:
526 try:
520 submodules_node = self.get_node(git_modules_path)
527 submodules_node = self.get_node(git_modules_path)
521 except NodeDoesNotExistError:
528 except NodeDoesNotExistError:
522 return None
529 return None
523
530
524 content = submodules_node.content
531 content = submodules_node.content
525
532
526 # ConfigParser fails if there are whitespaces
533 # ConfigParser fails if there are whitespaces
527 content = '\n'.join(l.strip() for l in content.split('\n'))
534 content = '\n'.join(l.strip() for l in content.split('\n'))
528
535
529 parser = ConfigParser()
536 parser = ConfigParser()
530 parser.readfp(StringIO(content))
537 parser.readfp(StringIO(content))
531
538
532 for section in parser.sections():
539 for section in parser.sections():
533 path = parser.get(section, 'path')
540 path = parser.get(section, 'path')
534 url = parser.get(section, 'url')
541 url = parser.get(section, 'url')
535 if path and url:
542 if path and url:
536 self._submodules[path.strip('/')] = url
543 self._submodules[path.strip('/')] = url
537
544
538 return self._submodules.get(submodule_path.strip('/'))
545 return self._submodules.get(submodule_path.strip('/'))
@@ -1,383 +1,388 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2014-2017 RhodeCode GmbH
3 # Copyright (C) 2014-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 HG commit module
22 HG commit module
23 """
23 """
24
24
25 import os
25 import os
26
26
27 from zope.cachedescriptors.property import Lazy as LazyProperty
27 from zope.cachedescriptors.property import Lazy as LazyProperty
28
28
29 from rhodecode.lib.datelib import utcdate_fromtimestamp
29 from rhodecode.lib.datelib import utcdate_fromtimestamp
30 from rhodecode.lib.utils import safe_str, safe_unicode
30 from rhodecode.lib.utils import safe_str, safe_unicode
31 from rhodecode.lib.vcs import path as vcspath
31 from rhodecode.lib.vcs import path as vcspath
32 from rhodecode.lib.vcs.backends import base
32 from rhodecode.lib.vcs.backends import base
33 from rhodecode.lib.vcs.backends.hg.diff import MercurialDiff
33 from rhodecode.lib.vcs.backends.hg.diff import MercurialDiff
34 from rhodecode.lib.vcs.exceptions import CommitError
34 from rhodecode.lib.vcs.exceptions import CommitError
35 from rhodecode.lib.vcs.nodes import (
35 from rhodecode.lib.vcs.nodes import (
36 AddedFileNodesGenerator, ChangedFileNodesGenerator, DirNode, FileNode,
36 AddedFileNodesGenerator, ChangedFileNodesGenerator, DirNode, FileNode,
37 NodeKind, RemovedFileNodesGenerator, RootNode, SubModuleNode,
37 NodeKind, RemovedFileNodesGenerator, RootNode, SubModuleNode,
38 LargeFileNode, LARGEFILE_PREFIX)
38 LargeFileNode, LARGEFILE_PREFIX)
39 from rhodecode.lib.vcs.utils.paths import get_dirs_for_path
39 from rhodecode.lib.vcs.utils.paths import get_dirs_for_path
40
40
41
41
42 class MercurialCommit(base.BaseCommit):
42 class MercurialCommit(base.BaseCommit):
43 """
43 """
44 Represents state of the repository at the single commit.
44 Represents state of the repository at the single commit.
45 """
45 """
46
46
47 _filter_pre_load = [
47 _filter_pre_load = [
48 # git specific property not supported here
48 # git specific property not supported here
49 "_commit",
49 "_commit",
50 ]
50 ]
51
51
52 def __init__(self, repository, raw_id, idx, pre_load=None):
52 def __init__(self, repository, raw_id, idx, pre_load=None):
53 raw_id = safe_str(raw_id)
53 raw_id = safe_str(raw_id)
54
54
55 self.repository = repository
55 self.repository = repository
56 self._remote = repository._remote
56 self._remote = repository._remote
57
57
58 self.raw_id = raw_id
58 self.raw_id = raw_id
59 self.idx = repository._sanitize_commit_idx(idx)
59 self.idx = repository._sanitize_commit_idx(idx)
60
60
61 self._set_bulk_properties(pre_load)
61 self._set_bulk_properties(pre_load)
62
62
63 # caches
63 # caches
64 self.nodes = {}
64 self.nodes = {}
65
65
66 def _set_bulk_properties(self, pre_load):
66 def _set_bulk_properties(self, pre_load):
67 if not pre_load:
67 if not pre_load:
68 return
68 return
69 pre_load = [entry for entry in pre_load
69 pre_load = [entry for entry in pre_load
70 if entry not in self._filter_pre_load]
70 if entry not in self._filter_pre_load]
71 if not pre_load:
71 if not pre_load:
72 return
72 return
73
73
74 result = self._remote.bulk_request(self.idx, pre_load)
74 result = self._remote.bulk_request(self.idx, pre_load)
75 for attr, value in result.items():
75 for attr, value in result.items():
76 if attr in ["author", "branch", "message"]:
76 if attr in ["author", "branch", "message"]:
77 value = safe_unicode(value)
77 value = safe_unicode(value)
78 elif attr == "affected_files":
78 elif attr == "affected_files":
79 value = map(safe_unicode, value)
79 value = map(safe_unicode, value)
80 elif attr == "date":
80 elif attr == "date":
81 value = utcdate_fromtimestamp(*value)
81 value = utcdate_fromtimestamp(*value)
82 elif attr in ["children", "parents"]:
82 elif attr in ["children", "parents"]:
83 value = self._make_commits(value)
83 value = self._make_commits(value)
84 elif attr in ["phase"]:
85 value = self._get_phase_text(value)
84 self.__dict__[attr] = value
86 self.__dict__[attr] = value
85
87
86 @LazyProperty
88 @LazyProperty
87 def tags(self):
89 def tags(self):
88 tags = [name for name, commit_id in self.repository.tags.iteritems()
90 tags = [name for name, commit_id in self.repository.tags.iteritems()
89 if commit_id == self.raw_id]
91 if commit_id == self.raw_id]
90 return tags
92 return tags
91
93
92 @LazyProperty
94 @LazyProperty
93 def branch(self):
95 def branch(self):
94 return safe_unicode(self._remote.ctx_branch(self.idx))
96 return safe_unicode(self._remote.ctx_branch(self.idx))
95
97
96 @LazyProperty
98 @LazyProperty
97 def bookmarks(self):
99 def bookmarks(self):
98 bookmarks = [
100 bookmarks = [
99 name for name, commit_id in self.repository.bookmarks.iteritems()
101 name for name, commit_id in self.repository.bookmarks.iteritems()
100 if commit_id == self.raw_id]
102 if commit_id == self.raw_id]
101 return bookmarks
103 return bookmarks
102
104
103 @LazyProperty
105 @LazyProperty
104 def message(self):
106 def message(self):
105 return safe_unicode(self._remote.ctx_description(self.idx))
107 return safe_unicode(self._remote.ctx_description(self.idx))
106
108
107 @LazyProperty
109 @LazyProperty
108 def committer(self):
110 def committer(self):
109 return safe_unicode(self.author)
111 return safe_unicode(self.author)
110
112
111 @LazyProperty
113 @LazyProperty
112 def author(self):
114 def author(self):
113 return safe_unicode(self._remote.ctx_user(self.idx))
115 return safe_unicode(self._remote.ctx_user(self.idx))
114
116
115 @LazyProperty
117 @LazyProperty
116 def date(self):
118 def date(self):
117 return utcdate_fromtimestamp(*self._remote.ctx_date(self.idx))
119 return utcdate_fromtimestamp(*self._remote.ctx_date(self.idx))
118
120
119 @LazyProperty
121 @LazyProperty
120 def status(self):
122 def status(self):
121 """
123 """
122 Returns modified, added, removed, deleted files for current commit
124 Returns modified, added, removed, deleted files for current commit
123 """
125 """
124 return self._remote.ctx_status(self.idx)
126 return self._remote.ctx_status(self.idx)
125
127
126 @LazyProperty
128 @LazyProperty
127 def _file_paths(self):
129 def _file_paths(self):
128 return self._remote.ctx_list(self.idx)
130 return self._remote.ctx_list(self.idx)
129
131
130 @LazyProperty
132 @LazyProperty
131 def _dir_paths(self):
133 def _dir_paths(self):
132 p = list(set(get_dirs_for_path(*self._file_paths)))
134 p = list(set(get_dirs_for_path(*self._file_paths)))
133 p.insert(0, '')
135 p.insert(0, '')
134 return p
136 return p
135
137
136 @LazyProperty
138 @LazyProperty
137 def _paths(self):
139 def _paths(self):
138 return self._dir_paths + self._file_paths
140 return self._dir_paths + self._file_paths
139
141
140 @LazyProperty
142 @LazyProperty
141 def id(self):
143 def id(self):
142 if self.last:
144 if self.last:
143 return u'tip'
145 return u'tip'
144 return self.short_id
146 return self.short_id
145
147
146 @LazyProperty
148 @LazyProperty
147 def short_id(self):
149 def short_id(self):
148 return self.raw_id[:12]
150 return self.raw_id[:12]
149
151
150 def _make_commits(self, indexes):
152 def _make_commits(self, indexes, pre_load=None):
151 return [self.repository.get_commit(commit_idx=idx)
153 return [self.repository.get_commit(commit_idx=idx, pre_load=pre_load)
152 for idx in indexes if idx >= 0]
154 for idx in indexes if idx >= 0]
153
155
154 @LazyProperty
156 @LazyProperty
155 def parents(self):
157 def parents(self):
156 """
158 """
157 Returns list of parent commits.
159 Returns list of parent commits.
158 """
160 """
159 parents = self._remote.ctx_parents(self.idx)
161 parents = self._remote.ctx_parents(self.idx)
160 return self._make_commits(parents)
162 return self._make_commits(parents)
161
163
162 @LazyProperty
164 def _get_phase_text(self, phase_id):
163 def phase(self):
165 return {
164 phase_id = self._remote.ctx_phase(self.idx)
165 phase_text = {
166 0: 'public',
166 0: 'public',
167 1: 'draft',
167 1: 'draft',
168 2: 'secret',
168 2: 'secret',
169 }.get(phase_id) or ''
169 }.get(phase_id) or ''
170
170
171 @LazyProperty
172 def phase(self):
173 phase_id = self._remote.ctx_phase(self.idx)
174 phase_text = self._get_phase_text(phase_id)
175
171 return safe_unicode(phase_text)
176 return safe_unicode(phase_text)
172
177
173 @LazyProperty
178 @LazyProperty
174 def obsolete(self):
179 def obsolete(self):
175 obsolete = self._remote.ctx_obsolete(self.idx)
180 obsolete = self._remote.ctx_obsolete(self.idx)
176 return obsolete
181 return obsolete
177
182
178 @LazyProperty
183 @LazyProperty
179 def hidden(self):
184 def hidden(self):
180 hidden = self._remote.ctx_hidden(self.idx)
185 hidden = self._remote.ctx_hidden(self.idx)
181 return hidden
186 return hidden
182
187
183 @LazyProperty
188 @LazyProperty
184 def children(self):
189 def children(self):
185 """
190 """
186 Returns list of child commits.
191 Returns list of child commits.
187 """
192 """
188 children = self._remote.ctx_children(self.idx)
193 children = self._remote.ctx_children(self.idx)
189 return self._make_commits(children)
194 return self._make_commits(children)
190
195
191 def diff(self, ignore_whitespace=True, context=3):
196 def diff(self, ignore_whitespace=True, context=3):
192 result = self._remote.ctx_diff(
197 result = self._remote.ctx_diff(
193 self.idx,
198 self.idx,
194 git=True, ignore_whitespace=ignore_whitespace, context=context)
199 git=True, ignore_whitespace=ignore_whitespace, context=context)
195 diff = ''.join(result)
200 diff = ''.join(result)
196 return MercurialDiff(diff)
201 return MercurialDiff(diff)
197
202
198 def _fix_path(self, path):
203 def _fix_path(self, path):
199 """
204 """
200 Mercurial keeps filenodes as str so we need to encode from unicode
205 Mercurial keeps filenodes as str so we need to encode from unicode
201 to str.
206 to str.
202 """
207 """
203 return safe_str(super(MercurialCommit, self)._fix_path(path))
208 return safe_str(super(MercurialCommit, self)._fix_path(path))
204
209
205 def _get_kind(self, path):
210 def _get_kind(self, path):
206 path = self._fix_path(path)
211 path = self._fix_path(path)
207 if path in self._file_paths:
212 if path in self._file_paths:
208 return NodeKind.FILE
213 return NodeKind.FILE
209 elif path in self._dir_paths:
214 elif path in self._dir_paths:
210 return NodeKind.DIR
215 return NodeKind.DIR
211 else:
216 else:
212 raise CommitError(
217 raise CommitError(
213 "Node does not exist at the given path '%s'" % (path, ))
218 "Node does not exist at the given path '%s'" % (path, ))
214
219
215 def _get_filectx(self, path):
220 def _get_filectx(self, path):
216 path = self._fix_path(path)
221 path = self._fix_path(path)
217 if self._get_kind(path) != NodeKind.FILE:
222 if self._get_kind(path) != NodeKind.FILE:
218 raise CommitError(
223 raise CommitError(
219 "File does not exist for idx %s at '%s'" % (self.raw_id, path))
224 "File does not exist for idx %s at '%s'" % (self.raw_id, path))
220 return path
225 return path
221
226
222 def get_file_mode(self, path):
227 def get_file_mode(self, path):
223 """
228 """
224 Returns stat mode of the file at the given ``path``.
229 Returns stat mode of the file at the given ``path``.
225 """
230 """
226 path = self._get_filectx(path)
231 path = self._get_filectx(path)
227 if 'x' in self._remote.fctx_flags(self.idx, path):
232 if 'x' in self._remote.fctx_flags(self.idx, path):
228 return base.FILEMODE_EXECUTABLE
233 return base.FILEMODE_EXECUTABLE
229 else:
234 else:
230 return base.FILEMODE_DEFAULT
235 return base.FILEMODE_DEFAULT
231
236
232 def is_link(self, path):
237 def is_link(self, path):
233 path = self._get_filectx(path)
238 path = self._get_filectx(path)
234 return 'l' in self._remote.fctx_flags(self.idx, path)
239 return 'l' in self._remote.fctx_flags(self.idx, path)
235
240
236 def get_file_content(self, path):
241 def get_file_content(self, path):
237 """
242 """
238 Returns content of the file at given ``path``.
243 Returns content of the file at given ``path``.
239 """
244 """
240 path = self._get_filectx(path)
245 path = self._get_filectx(path)
241 return self._remote.fctx_data(self.idx, path)
246 return self._remote.fctx_data(self.idx, path)
242
247
243 def get_file_size(self, path):
248 def get_file_size(self, path):
244 """
249 """
245 Returns size of the file at given ``path``.
250 Returns size of the file at given ``path``.
246 """
251 """
247 path = self._get_filectx(path)
252 path = self._get_filectx(path)
248 return self._remote.fctx_size(self.idx, path)
253 return self._remote.fctx_size(self.idx, path)
249
254
250 def get_file_history(self, path, limit=None, pre_load=None):
255 def get_file_history(self, path, limit=None, pre_load=None):
251 """
256 """
252 Returns history of file as reversed list of `MercurialCommit` objects
257 Returns history of file as reversed list of `MercurialCommit` objects
253 for which file at given ``path`` has been modified.
258 for which file at given ``path`` has been modified.
254 """
259 """
255 path = self._get_filectx(path)
260 path = self._get_filectx(path)
256 hist = self._remote.file_history(self.idx, path, limit)
261 hist = self._remote.file_history(self.idx, path, limit)
257 return [
262 return [
258 self.repository.get_commit(commit_id=commit_id, pre_load=pre_load)
263 self.repository.get_commit(commit_id=commit_id, pre_load=pre_load)
259 for commit_id in hist]
264 for commit_id in hist]
260
265
261 def get_file_annotate(self, path, pre_load=None):
266 def get_file_annotate(self, path, pre_load=None):
262 """
267 """
263 Returns a generator of four element tuples with
268 Returns a generator of four element tuples with
264 lineno, commit_id, commit lazy loader and line
269 lineno, commit_id, commit lazy loader and line
265 """
270 """
266 result = self._remote.fctx_annotate(self.idx, path)
271 result = self._remote.fctx_annotate(self.idx, path)
267
272
268 for ln_no, commit_id, content in result:
273 for ln_no, commit_id, content in result:
269 yield (
274 yield (
270 ln_no, commit_id,
275 ln_no, commit_id,
271 lambda: self.repository.get_commit(commit_id=commit_id,
276 lambda: self.repository.get_commit(commit_id=commit_id,
272 pre_load=pre_load),
277 pre_load=pre_load),
273 content)
278 content)
274
279
275 def get_nodes(self, path):
280 def get_nodes(self, path):
276 """
281 """
277 Returns combined ``DirNode`` and ``FileNode`` objects list representing
282 Returns combined ``DirNode`` and ``FileNode`` objects list representing
278 state of commit at the given ``path``. If node at the given ``path``
283 state of commit at the given ``path``. If node at the given ``path``
279 is not instance of ``DirNode``, CommitError would be raised.
284 is not instance of ``DirNode``, CommitError would be raised.
280 """
285 """
281
286
282 if self._get_kind(path) != NodeKind.DIR:
287 if self._get_kind(path) != NodeKind.DIR:
283 raise CommitError(
288 raise CommitError(
284 "Directory does not exist for idx %s at '%s'" %
289 "Directory does not exist for idx %s at '%s'" %
285 (self.idx, path))
290 (self.idx, path))
286 path = self._fix_path(path)
291 path = self._fix_path(path)
287
292
288 filenodes = [
293 filenodes = [
289 FileNode(f, commit=self) for f in self._file_paths
294 FileNode(f, commit=self) for f in self._file_paths
290 if os.path.dirname(f) == path]
295 if os.path.dirname(f) == path]
291 # TODO: johbo: Check if this can be done in a more obvious way
296 # TODO: johbo: Check if this can be done in a more obvious way
292 dirs = path == '' and '' or [
297 dirs = path == '' and '' or [
293 d for d in self._dir_paths
298 d for d in self._dir_paths
294 if d and vcspath.dirname(d) == path]
299 if d and vcspath.dirname(d) == path]
295 dirnodes = [
300 dirnodes = [
296 DirNode(d, commit=self) for d in dirs
301 DirNode(d, commit=self) for d in dirs
297 if os.path.dirname(d) == path]
302 if os.path.dirname(d) == path]
298
303
299 alias = self.repository.alias
304 alias = self.repository.alias
300 for k, vals in self._submodules.iteritems():
305 for k, vals in self._submodules.iteritems():
301 loc = vals[0]
306 loc = vals[0]
302 commit = vals[1]
307 commit = vals[1]
303 dirnodes.append(
308 dirnodes.append(
304 SubModuleNode(k, url=loc, commit=commit, alias=alias))
309 SubModuleNode(k, url=loc, commit=commit, alias=alias))
305 nodes = dirnodes + filenodes
310 nodes = dirnodes + filenodes
306 # cache nodes
311 # cache nodes
307 for node in nodes:
312 for node in nodes:
308 self.nodes[node.path] = node
313 self.nodes[node.path] = node
309 nodes.sort()
314 nodes.sort()
310
315
311 return nodes
316 return nodes
312
317
313 def get_node(self, path, pre_load=None):
318 def get_node(self, path, pre_load=None):
314 """
319 """
315 Returns `Node` object from the given `path`. If there is no node at
320 Returns `Node` object from the given `path`. If there is no node at
316 the given `path`, `NodeDoesNotExistError` would be raised.
321 the given `path`, `NodeDoesNotExistError` would be raised.
317 """
322 """
318 path = self._fix_path(path)
323 path = self._fix_path(path)
319
324
320 if path not in self.nodes:
325 if path not in self.nodes:
321 if path in self._file_paths:
326 if path in self._file_paths:
322 node = FileNode(path, commit=self, pre_load=pre_load)
327 node = FileNode(path, commit=self, pre_load=pre_load)
323 elif path in self._dir_paths:
328 elif path in self._dir_paths:
324 if path == '':
329 if path == '':
325 node = RootNode(commit=self)
330 node = RootNode(commit=self)
326 else:
331 else:
327 node = DirNode(path, commit=self)
332 node = DirNode(path, commit=self)
328 else:
333 else:
329 raise self.no_node_at_path(path)
334 raise self.no_node_at_path(path)
330
335
331 # cache node
336 # cache node
332 self.nodes[path] = node
337 self.nodes[path] = node
333 return self.nodes[path]
338 return self.nodes[path]
334
339
335 def get_largefile_node(self, path):
340 def get_largefile_node(self, path):
336
341
337 if self._remote.is_large_file(path):
342 if self._remote.is_large_file(path):
338 # content of that file regular FileNode is the hash of largefile
343 # content of that file regular FileNode is the hash of largefile
339 file_id = self.get_file_content(path).strip()
344 file_id = self.get_file_content(path).strip()
340
345
341 if self._remote.in_largefiles_store(file_id):
346 if self._remote.in_largefiles_store(file_id):
342 lf_path = self._remote.store_path(file_id)
347 lf_path = self._remote.store_path(file_id)
343 return LargeFileNode(lf_path, commit=self, org_path=path)
348 return LargeFileNode(lf_path, commit=self, org_path=path)
344 elif self._remote.in_user_cache(file_id):
349 elif self._remote.in_user_cache(file_id):
345 lf_path = self._remote.store_path(file_id)
350 lf_path = self._remote.store_path(file_id)
346 self._remote.link(file_id, path)
351 self._remote.link(file_id, path)
347 return LargeFileNode(lf_path, commit=self, org_path=path)
352 return LargeFileNode(lf_path, commit=self, org_path=path)
348
353
349 @LazyProperty
354 @LazyProperty
350 def _submodules(self):
355 def _submodules(self):
351 """
356 """
352 Returns a dictionary with submodule information from substate file
357 Returns a dictionary with submodule information from substate file
353 of hg repository.
358 of hg repository.
354 """
359 """
355 return self._remote.ctx_substate(self.idx)
360 return self._remote.ctx_substate(self.idx)
356
361
357 @LazyProperty
362 @LazyProperty
358 def affected_files(self):
363 def affected_files(self):
359 """
364 """
360 Gets a fast accessible file changes for given commit
365 Gets a fast accessible file changes for given commit
361 """
366 """
362 return self._remote.ctx_files(self.idx)
367 return self._remote.ctx_files(self.idx)
363
368
364 @property
369 @property
365 def added(self):
370 def added(self):
366 """
371 """
367 Returns list of added ``FileNode`` objects.
372 Returns list of added ``FileNode`` objects.
368 """
373 """
369 return AddedFileNodesGenerator([n for n in self.status[1]], self)
374 return AddedFileNodesGenerator([n for n in self.status[1]], self)
370
375
371 @property
376 @property
372 def changed(self):
377 def changed(self):
373 """
378 """
374 Returns list of modified ``FileNode`` objects.
379 Returns list of modified ``FileNode`` objects.
375 """
380 """
376 return ChangedFileNodesGenerator([n for n in self.status[0]], self)
381 return ChangedFileNodesGenerator([n for n in self.status[0]], self)
377
382
378 @property
383 @property
379 def removed(self):
384 def removed(self):
380 """
385 """
381 Returns list of removed ``FileNode`` objects.
386 Returns list of removed ``FileNode`` objects.
382 """
387 """
383 return RemovedFileNodesGenerator([n for n in self.status[2]], self)
388 return RemovedFileNodesGenerator([n for n in self.status[2]], self)
@@ -1,283 +1,284 b''
1
1
2 /******************************************************************************
2 /******************************************************************************
3 * *
3 * *
4 * DO NOT CHANGE THIS FILE MANUALLY *
4 * DO NOT CHANGE THIS FILE MANUALLY *
5 * *
5 * *
6 * *
6 * *
7 * This file is automatically generated when the app starts up with *
7 * This file is automatically generated when the app starts up with *
8 * generate_js_files = true *
8 * generate_js_files = true *
9 * *
9 * *
10 * To add a route here pass jsroute=True to the route definition in the app *
10 * To add a route here pass jsroute=True to the route definition in the app *
11 * *
11 * *
12 ******************************************************************************/
12 ******************************************************************************/
13 function registerRCRoutes() {
13 function registerRCRoutes() {
14 // routes registration
14 // routes registration
15 pyroutes.register('favicon', '/favicon.ico', []);
15 pyroutes.register('favicon', '/favicon.ico', []);
16 pyroutes.register('robots', '/robots.txt', []);
16 pyroutes.register('robots', '/robots.txt', []);
17 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
17 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
18 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
18 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
19 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
19 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
20 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
20 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
21 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
21 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
22 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
22 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
23 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/settings/integrations', ['repo_group_name']);
23 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/settings/integrations', ['repo_group_name']);
24 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/settings/integrations/new', ['repo_group_name']);
24 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/settings/integrations/new', ['repo_group_name']);
25 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
25 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
26 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
26 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
27 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
27 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
28 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
28 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
29 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
29 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
30 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
30 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
31 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
31 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
32 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
32 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
33 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
33 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
34 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
34 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
35 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
35 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
36 pyroutes.register('ops_ping_legacy', '/_admin/ping', []);
36 pyroutes.register('ops_ping_legacy', '/_admin/ping', []);
37 pyroutes.register('ops_error_test_legacy', '/_admin/error_test', []);
37 pyroutes.register('ops_error_test_legacy', '/_admin/error_test', []);
38 pyroutes.register('admin_home', '/_admin', []);
38 pyroutes.register('admin_home', '/_admin', []);
39 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
39 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
40 pyroutes.register('admin_audit_log_entry', '/_admin/audit_logs/%(audit_log_id)s', ['audit_log_id']);
40 pyroutes.register('admin_audit_log_entry', '/_admin/audit_logs/%(audit_log_id)s', ['audit_log_id']);
41 pyroutes.register('pull_requests_global_0', '/_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
41 pyroutes.register('pull_requests_global_0', '/_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
42 pyroutes.register('pull_requests_global_1', '/_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
42 pyroutes.register('pull_requests_global_1', '/_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
43 pyroutes.register('pull_requests_global', '/_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
43 pyroutes.register('pull_requests_global', '/_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
44 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
44 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
45 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '/_admin/settings/vcs/svn_generate_cfg', []);
45 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '/_admin/settings/vcs/svn_generate_cfg', []);
46 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
46 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
47 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
47 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
48 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
48 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
49 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
49 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
50 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
50 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
51 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
51 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
52 pyroutes.register('admin_defaults_repositories', '/_admin/defaults/repositories', []);
52 pyroutes.register('admin_defaults_repositories', '/_admin/defaults/repositories', []);
53 pyroutes.register('admin_defaults_repositories_update', '/_admin/defaults/repositories/update', []);
53 pyroutes.register('admin_defaults_repositories_update', '/_admin/defaults/repositories/update', []);
54 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
54 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
55 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
55 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
56 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
56 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
57 pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
57 pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
58 pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
58 pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
59 pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
59 pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
60 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
60 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
61 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
61 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
62 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
62 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
63 pyroutes.register('admin_permissions_ssh_keys', '/_admin/permissions/ssh_keys', []);
63 pyroutes.register('admin_permissions_ssh_keys', '/_admin/permissions/ssh_keys', []);
64 pyroutes.register('admin_permissions_ssh_keys_data', '/_admin/permissions/ssh_keys/data', []);
64 pyroutes.register('admin_permissions_ssh_keys_data', '/_admin/permissions/ssh_keys/data', []);
65 pyroutes.register('admin_permissions_ssh_keys_update', '/_admin/permissions/ssh_keys/update', []);
65 pyroutes.register('admin_permissions_ssh_keys_update', '/_admin/permissions/ssh_keys/update', []);
66 pyroutes.register('users', '/_admin/users', []);
66 pyroutes.register('users', '/_admin/users', []);
67 pyroutes.register('users_data', '/_admin/users_data', []);
67 pyroutes.register('users_data', '/_admin/users_data', []);
68 pyroutes.register('users_create', '/_admin/users/create', []);
68 pyroutes.register('users_create', '/_admin/users/create', []);
69 pyroutes.register('users_new', '/_admin/users/new', []);
69 pyroutes.register('users_new', '/_admin/users/new', []);
70 pyroutes.register('user_edit', '/_admin/users/%(user_id)s/edit', ['user_id']);
70 pyroutes.register('user_edit', '/_admin/users/%(user_id)s/edit', ['user_id']);
71 pyroutes.register('user_edit_advanced', '/_admin/users/%(user_id)s/edit/advanced', ['user_id']);
71 pyroutes.register('user_edit_advanced', '/_admin/users/%(user_id)s/edit/advanced', ['user_id']);
72 pyroutes.register('user_edit_global_perms', '/_admin/users/%(user_id)s/edit/global_permissions', ['user_id']);
72 pyroutes.register('user_edit_global_perms', '/_admin/users/%(user_id)s/edit/global_permissions', ['user_id']);
73 pyroutes.register('user_edit_global_perms_update', '/_admin/users/%(user_id)s/edit/global_permissions/update', ['user_id']);
73 pyroutes.register('user_edit_global_perms_update', '/_admin/users/%(user_id)s/edit/global_permissions/update', ['user_id']);
74 pyroutes.register('user_update', '/_admin/users/%(user_id)s/update', ['user_id']);
74 pyroutes.register('user_update', '/_admin/users/%(user_id)s/update', ['user_id']);
75 pyroutes.register('user_delete', '/_admin/users/%(user_id)s/delete', ['user_id']);
75 pyroutes.register('user_delete', '/_admin/users/%(user_id)s/delete', ['user_id']);
76 pyroutes.register('user_force_password_reset', '/_admin/users/%(user_id)s/password_reset', ['user_id']);
76 pyroutes.register('user_force_password_reset', '/_admin/users/%(user_id)s/password_reset', ['user_id']);
77 pyroutes.register('user_create_personal_repo_group', '/_admin/users/%(user_id)s/create_repo_group', ['user_id']);
77 pyroutes.register('user_create_personal_repo_group', '/_admin/users/%(user_id)s/create_repo_group', ['user_id']);
78 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
78 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
79 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
79 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
80 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
80 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
81 pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']);
81 pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']);
82 pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']);
82 pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']);
83 pyroutes.register('edit_user_ssh_keys_add', '/_admin/users/%(user_id)s/edit/ssh_keys/new', ['user_id']);
83 pyroutes.register('edit_user_ssh_keys_add', '/_admin/users/%(user_id)s/edit/ssh_keys/new', ['user_id']);
84 pyroutes.register('edit_user_ssh_keys_delete', '/_admin/users/%(user_id)s/edit/ssh_keys/delete', ['user_id']);
84 pyroutes.register('edit_user_ssh_keys_delete', '/_admin/users/%(user_id)s/edit/ssh_keys/delete', ['user_id']);
85 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
85 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
86 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
86 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
87 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
87 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
88 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
88 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
89 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
89 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
90 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
90 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
91 pyroutes.register('edit_user_perms_summary', '/_admin/users/%(user_id)s/edit/permissions_summary', ['user_id']);
91 pyroutes.register('edit_user_perms_summary', '/_admin/users/%(user_id)s/edit/permissions_summary', ['user_id']);
92 pyroutes.register('edit_user_perms_summary_json', '/_admin/users/%(user_id)s/edit/permissions_summary/json', ['user_id']);
92 pyroutes.register('edit_user_perms_summary_json', '/_admin/users/%(user_id)s/edit/permissions_summary/json', ['user_id']);
93 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
93 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
94 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
94 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
95 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
95 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
96 pyroutes.register('user_groups', '/_admin/user_groups', []);
96 pyroutes.register('user_groups', '/_admin/user_groups', []);
97 pyroutes.register('user_groups_data', '/_admin/user_groups_data', []);
97 pyroutes.register('user_groups_data', '/_admin/user_groups_data', []);
98 pyroutes.register('user_groups_new', '/_admin/user_groups/new', []);
98 pyroutes.register('user_groups_new', '/_admin/user_groups/new', []);
99 pyroutes.register('user_groups_create', '/_admin/user_groups/create', []);
99 pyroutes.register('user_groups_create', '/_admin/user_groups/create', []);
100 pyroutes.register('repos', '/_admin/repos', []);
100 pyroutes.register('repos', '/_admin/repos', []);
101 pyroutes.register('repo_new', '/_admin/repos/new', []);
101 pyroutes.register('repo_new', '/_admin/repos/new', []);
102 pyroutes.register('repo_create', '/_admin/repos/create', []);
102 pyroutes.register('repo_create', '/_admin/repos/create', []);
103 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
103 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
104 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
104 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
105 pyroutes.register('channelstream_proxy', '/_channelstream', []);
105 pyroutes.register('channelstream_proxy', '/_channelstream', []);
106 pyroutes.register('login', '/_admin/login', []);
106 pyroutes.register('login', '/_admin/login', []);
107 pyroutes.register('logout', '/_admin/logout', []);
107 pyroutes.register('logout', '/_admin/logout', []);
108 pyroutes.register('register', '/_admin/register', []);
108 pyroutes.register('register', '/_admin/register', []);
109 pyroutes.register('reset_password', '/_admin/password_reset', []);
109 pyroutes.register('reset_password', '/_admin/password_reset', []);
110 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
110 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
111 pyroutes.register('home', '/', []);
111 pyroutes.register('home', '/', []);
112 pyroutes.register('user_autocomplete_data', '/_users', []);
112 pyroutes.register('user_autocomplete_data', '/_users', []);
113 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
113 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
114 pyroutes.register('repo_list_data', '/_repos', []);
114 pyroutes.register('repo_list_data', '/_repos', []);
115 pyroutes.register('goto_switcher_data', '/_goto_data', []);
115 pyroutes.register('goto_switcher_data', '/_goto_data', []);
116 pyroutes.register('journal', '/_admin/journal', []);
116 pyroutes.register('journal', '/_admin/journal', []);
117 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
117 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
118 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
118 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
119 pyroutes.register('journal_public', '/_admin/public_journal', []);
119 pyroutes.register('journal_public', '/_admin/public_journal', []);
120 pyroutes.register('journal_public_atom', '/_admin/public_journal/atom', []);
120 pyroutes.register('journal_public_atom', '/_admin/public_journal/atom', []);
121 pyroutes.register('journal_public_atom_old', '/_admin/public_journal_atom', []);
121 pyroutes.register('journal_public_atom_old', '/_admin/public_journal_atom', []);
122 pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
122 pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
123 pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
123 pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
124 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
124 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
125 pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']);
125 pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']);
126 pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']);
126 pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']);
127 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
127 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
128 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
128 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
129 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
129 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
130 pyroutes.register('repo_commit_children', '/%(repo_name)s/changeset_children/%(commit_id)s', ['repo_name', 'commit_id']);
130 pyroutes.register('repo_commit_children', '/%(repo_name)s/changeset_children/%(commit_id)s', ['repo_name', 'commit_id']);
131 pyroutes.register('repo_commit_parents', '/%(repo_name)s/changeset_parents/%(commit_id)s', ['repo_name', 'commit_id']);
131 pyroutes.register('repo_commit_parents', '/%(repo_name)s/changeset_parents/%(commit_id)s', ['repo_name', 'commit_id']);
132 pyroutes.register('repo_commit_raw', '/%(repo_name)s/changeset-diff/%(commit_id)s', ['repo_name', 'commit_id']);
132 pyroutes.register('repo_commit_raw', '/%(repo_name)s/changeset-diff/%(commit_id)s', ['repo_name', 'commit_id']);
133 pyroutes.register('repo_commit_patch', '/%(repo_name)s/changeset-patch/%(commit_id)s', ['repo_name', 'commit_id']);
133 pyroutes.register('repo_commit_patch', '/%(repo_name)s/changeset-patch/%(commit_id)s', ['repo_name', 'commit_id']);
134 pyroutes.register('repo_commit_download', '/%(repo_name)s/changeset-download/%(commit_id)s', ['repo_name', 'commit_id']);
134 pyroutes.register('repo_commit_download', '/%(repo_name)s/changeset-download/%(commit_id)s', ['repo_name', 'commit_id']);
135 pyroutes.register('repo_commit_data', '/%(repo_name)s/changeset-data/%(commit_id)s', ['repo_name', 'commit_id']);
135 pyroutes.register('repo_commit_data', '/%(repo_name)s/changeset-data/%(commit_id)s', ['repo_name', 'commit_id']);
136 pyroutes.register('repo_commit_comment_create', '/%(repo_name)s/changeset/%(commit_id)s/comment/create', ['repo_name', 'commit_id']);
136 pyroutes.register('repo_commit_comment_create', '/%(repo_name)s/changeset/%(commit_id)s/comment/create', ['repo_name', 'commit_id']);
137 pyroutes.register('repo_commit_comment_preview', '/%(repo_name)s/changeset/%(commit_id)s/comment/preview', ['repo_name', 'commit_id']);
137 pyroutes.register('repo_commit_comment_preview', '/%(repo_name)s/changeset/%(commit_id)s/comment/preview', ['repo_name', 'commit_id']);
138 pyroutes.register('repo_commit_comment_delete', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/delete', ['repo_name', 'commit_id', 'comment_id']);
138 pyroutes.register('repo_commit_comment_delete', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/delete', ['repo_name', 'commit_id', 'comment_id']);
139 pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']);
139 pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']);
140 pyroutes.register('repo_archivefile', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
140 pyroutes.register('repo_archivefile', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
141 pyroutes.register('repo_files_diff', '/%(repo_name)s/diff/%(f_path)s', ['repo_name', 'f_path']);
141 pyroutes.register('repo_files_diff', '/%(repo_name)s/diff/%(f_path)s', ['repo_name', 'f_path']);
142 pyroutes.register('repo_files_diff_2way_redirect', '/%(repo_name)s/diff-2way/%(f_path)s', ['repo_name', 'f_path']);
142 pyroutes.register('repo_files_diff_2way_redirect', '/%(repo_name)s/diff-2way/%(f_path)s', ['repo_name', 'f_path']);
143 pyroutes.register('repo_files', '/%(repo_name)s/files/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
143 pyroutes.register('repo_files', '/%(repo_name)s/files/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
144 pyroutes.register('repo_files:default_path', '/%(repo_name)s/files/%(commit_id)s/', ['repo_name', 'commit_id']);
144 pyroutes.register('repo_files:default_path', '/%(repo_name)s/files/%(commit_id)s/', ['repo_name', 'commit_id']);
145 pyroutes.register('repo_files:default_commit', '/%(repo_name)s/files', ['repo_name']);
145 pyroutes.register('repo_files:default_commit', '/%(repo_name)s/files', ['repo_name']);
146 pyroutes.register('repo_files:rendered', '/%(repo_name)s/render/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
146 pyroutes.register('repo_files:rendered', '/%(repo_name)s/render/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
147 pyroutes.register('repo_files:annotated', '/%(repo_name)s/annotate/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
147 pyroutes.register('repo_files:annotated', '/%(repo_name)s/annotate/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
148 pyroutes.register('repo_files:annotated_previous', '/%(repo_name)s/annotate-previous/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
148 pyroutes.register('repo_files:annotated_previous', '/%(repo_name)s/annotate-previous/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
149 pyroutes.register('repo_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
149 pyroutes.register('repo_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
150 pyroutes.register('repo_nodetree_full:default_path', '/%(repo_name)s/nodetree_full/%(commit_id)s/', ['repo_name', 'commit_id']);
150 pyroutes.register('repo_nodetree_full:default_path', '/%(repo_name)s/nodetree_full/%(commit_id)s/', ['repo_name', 'commit_id']);
151 pyroutes.register('repo_files_nodelist', '/%(repo_name)s/nodelist/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
151 pyroutes.register('repo_files_nodelist', '/%(repo_name)s/nodelist/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
152 pyroutes.register('repo_file_raw', '/%(repo_name)s/raw/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
152 pyroutes.register('repo_file_raw', '/%(repo_name)s/raw/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
153 pyroutes.register('repo_file_download', '/%(repo_name)s/download/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
153 pyroutes.register('repo_file_download', '/%(repo_name)s/download/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
154 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
154 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
155 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
155 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
156 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
156 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
157 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
157 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
158 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
158 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
159 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
159 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
160 pyroutes.register('repo_files_update_file', '/%(repo_name)s/update_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
160 pyroutes.register('repo_files_update_file', '/%(repo_name)s/update_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
161 pyroutes.register('repo_files_add_file', '/%(repo_name)s/add_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
161 pyroutes.register('repo_files_add_file', '/%(repo_name)s/add_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
162 pyroutes.register('repo_files_create_file', '/%(repo_name)s/create_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
162 pyroutes.register('repo_files_create_file', '/%(repo_name)s/create_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
163 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
163 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
164 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
164 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
165 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
165 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
166 pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']);
166 pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']);
167 pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
167 pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
168 pyroutes.register('repo_changelog_elements', '/%(repo_name)s/changelog_elements', ['repo_name']);
168 pyroutes.register('repo_changelog_elements', '/%(repo_name)s/changelog_elements', ['repo_name']);
169 pyroutes.register('repo_changelog_elements_file', '/%(repo_name)s/changelog_elements/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
169 pyroutes.register('repo_compare_select', '/%(repo_name)s/compare', ['repo_name']);
170 pyroutes.register('repo_compare_select', '/%(repo_name)s/compare', ['repo_name']);
170 pyroutes.register('repo_compare', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
171 pyroutes.register('repo_compare', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
171 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
172 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
172 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
173 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
173 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
174 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
174 pyroutes.register('repo_fork_new', '/%(repo_name)s/fork', ['repo_name']);
175 pyroutes.register('repo_fork_new', '/%(repo_name)s/fork', ['repo_name']);
175 pyroutes.register('repo_fork_create', '/%(repo_name)s/fork/create', ['repo_name']);
176 pyroutes.register('repo_fork_create', '/%(repo_name)s/fork/create', ['repo_name']);
176 pyroutes.register('repo_forks_show_all', '/%(repo_name)s/forks', ['repo_name']);
177 pyroutes.register('repo_forks_show_all', '/%(repo_name)s/forks', ['repo_name']);
177 pyroutes.register('repo_forks_data', '/%(repo_name)s/forks/data', ['repo_name']);
178 pyroutes.register('repo_forks_data', '/%(repo_name)s/forks/data', ['repo_name']);
178 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
179 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
179 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
180 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
180 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
181 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
181 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
182 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
182 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
183 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
183 pyroutes.register('pullrequest_new', '/%(repo_name)s/pull-request/new', ['repo_name']);
184 pyroutes.register('pullrequest_new', '/%(repo_name)s/pull-request/new', ['repo_name']);
184 pyroutes.register('pullrequest_create', '/%(repo_name)s/pull-request/create', ['repo_name']);
185 pyroutes.register('pullrequest_create', '/%(repo_name)s/pull-request/create', ['repo_name']);
185 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
186 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
186 pyroutes.register('pullrequest_merge', '/%(repo_name)s/pull-request/%(pull_request_id)s/merge', ['repo_name', 'pull_request_id']);
187 pyroutes.register('pullrequest_merge', '/%(repo_name)s/pull-request/%(pull_request_id)s/merge', ['repo_name', 'pull_request_id']);
187 pyroutes.register('pullrequest_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/delete', ['repo_name', 'pull_request_id']);
188 pyroutes.register('pullrequest_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/delete', ['repo_name', 'pull_request_id']);
188 pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']);
189 pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']);
189 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/delete', ['repo_name', 'pull_request_id', 'comment_id']);
190 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/delete', ['repo_name', 'pull_request_id', 'comment_id']);
190 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
191 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
191 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
192 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
192 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
193 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
193 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
194 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
194 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
195 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
195 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
196 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
196 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
197 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
197 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
198 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
198 pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
199 pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
199 pyroutes.register('edit_repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
200 pyroutes.register('edit_repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
200 pyroutes.register('edit_repo_fields', '/%(repo_name)s/settings/fields', ['repo_name']);
201 pyroutes.register('edit_repo_fields', '/%(repo_name)s/settings/fields', ['repo_name']);
201 pyroutes.register('edit_repo_fields_create', '/%(repo_name)s/settings/fields/create', ['repo_name']);
202 pyroutes.register('edit_repo_fields_create', '/%(repo_name)s/settings/fields/create', ['repo_name']);
202 pyroutes.register('edit_repo_fields_delete', '/%(repo_name)s/settings/fields/%(field_id)s/delete', ['repo_name', 'field_id']);
203 pyroutes.register('edit_repo_fields_delete', '/%(repo_name)s/settings/fields/%(field_id)s/delete', ['repo_name', 'field_id']);
203 pyroutes.register('repo_edit_toggle_locking', '/%(repo_name)s/settings/toggle_locking', ['repo_name']);
204 pyroutes.register('repo_edit_toggle_locking', '/%(repo_name)s/settings/toggle_locking', ['repo_name']);
204 pyroutes.register('edit_repo_remote', '/%(repo_name)s/settings/remote', ['repo_name']);
205 pyroutes.register('edit_repo_remote', '/%(repo_name)s/settings/remote', ['repo_name']);
205 pyroutes.register('edit_repo_remote_pull', '/%(repo_name)s/settings/remote/pull', ['repo_name']);
206 pyroutes.register('edit_repo_remote_pull', '/%(repo_name)s/settings/remote/pull', ['repo_name']);
206 pyroutes.register('edit_repo_statistics', '/%(repo_name)s/settings/statistics', ['repo_name']);
207 pyroutes.register('edit_repo_statistics', '/%(repo_name)s/settings/statistics', ['repo_name']);
207 pyroutes.register('edit_repo_statistics_reset', '/%(repo_name)s/settings/statistics/update', ['repo_name']);
208 pyroutes.register('edit_repo_statistics_reset', '/%(repo_name)s/settings/statistics/update', ['repo_name']);
208 pyroutes.register('edit_repo_issuetracker', '/%(repo_name)s/settings/issue_trackers', ['repo_name']);
209 pyroutes.register('edit_repo_issuetracker', '/%(repo_name)s/settings/issue_trackers', ['repo_name']);
209 pyroutes.register('edit_repo_issuetracker_test', '/%(repo_name)s/settings/issue_trackers/test', ['repo_name']);
210 pyroutes.register('edit_repo_issuetracker_test', '/%(repo_name)s/settings/issue_trackers/test', ['repo_name']);
210 pyroutes.register('edit_repo_issuetracker_delete', '/%(repo_name)s/settings/issue_trackers/delete', ['repo_name']);
211 pyroutes.register('edit_repo_issuetracker_delete', '/%(repo_name)s/settings/issue_trackers/delete', ['repo_name']);
211 pyroutes.register('edit_repo_issuetracker_update', '/%(repo_name)s/settings/issue_trackers/update', ['repo_name']);
212 pyroutes.register('edit_repo_issuetracker_update', '/%(repo_name)s/settings/issue_trackers/update', ['repo_name']);
212 pyroutes.register('edit_repo_vcs', '/%(repo_name)s/settings/vcs', ['repo_name']);
213 pyroutes.register('edit_repo_vcs', '/%(repo_name)s/settings/vcs', ['repo_name']);
213 pyroutes.register('edit_repo_vcs_update', '/%(repo_name)s/settings/vcs/update', ['repo_name']);
214 pyroutes.register('edit_repo_vcs_update', '/%(repo_name)s/settings/vcs/update', ['repo_name']);
214 pyroutes.register('edit_repo_vcs_svn_pattern_delete', '/%(repo_name)s/settings/vcs/svn_pattern/delete', ['repo_name']);
215 pyroutes.register('edit_repo_vcs_svn_pattern_delete', '/%(repo_name)s/settings/vcs/svn_pattern/delete', ['repo_name']);
215 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
216 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
216 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
217 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
217 pyroutes.register('edit_repo_strip', '/%(repo_name)s/settings/strip', ['repo_name']);
218 pyroutes.register('edit_repo_strip', '/%(repo_name)s/settings/strip', ['repo_name']);
218 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
219 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
219 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
220 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
220 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed/rss', ['repo_name']);
221 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed/rss', ['repo_name']);
221 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed/atom', ['repo_name']);
222 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed/atom', ['repo_name']);
222 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
223 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
223 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
224 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
224 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
225 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
225 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
226 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
226 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
227 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
227 pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']);
228 pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']);
228 pyroutes.register('edit_user_group_perms_summary_json', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary/json', ['user_group_id']);
229 pyroutes.register('edit_user_group_perms_summary_json', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary/json', ['user_group_id']);
229 pyroutes.register('edit_user_group', '/_admin/user_groups/%(user_group_id)s/edit', ['user_group_id']);
230 pyroutes.register('edit_user_group', '/_admin/user_groups/%(user_group_id)s/edit', ['user_group_id']);
230 pyroutes.register('user_groups_update', '/_admin/user_groups/%(user_group_id)s/update', ['user_group_id']);
231 pyroutes.register('user_groups_update', '/_admin/user_groups/%(user_group_id)s/update', ['user_group_id']);
231 pyroutes.register('edit_user_group_global_perms', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions', ['user_group_id']);
232 pyroutes.register('edit_user_group_global_perms', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions', ['user_group_id']);
232 pyroutes.register('edit_user_group_global_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions/update', ['user_group_id']);
233 pyroutes.register('edit_user_group_global_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions/update', ['user_group_id']);
233 pyroutes.register('edit_user_group_perms', '/_admin/user_groups/%(user_group_id)s/edit/permissions', ['user_group_id']);
234 pyroutes.register('edit_user_group_perms', '/_admin/user_groups/%(user_group_id)s/edit/permissions', ['user_group_id']);
234 pyroutes.register('edit_user_group_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/permissions/update', ['user_group_id']);
235 pyroutes.register('edit_user_group_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/permissions/update', ['user_group_id']);
235 pyroutes.register('edit_user_group_advanced', '/_admin/user_groups/%(user_group_id)s/edit/advanced', ['user_group_id']);
236 pyroutes.register('edit_user_group_advanced', '/_admin/user_groups/%(user_group_id)s/edit/advanced', ['user_group_id']);
236 pyroutes.register('edit_user_group_advanced_sync', '/_admin/user_groups/%(user_group_id)s/edit/advanced/sync', ['user_group_id']);
237 pyroutes.register('edit_user_group_advanced_sync', '/_admin/user_groups/%(user_group_id)s/edit/advanced/sync', ['user_group_id']);
237 pyroutes.register('user_groups_delete', '/_admin/user_groups/%(user_group_id)s/delete', ['user_group_id']);
238 pyroutes.register('user_groups_delete', '/_admin/user_groups/%(user_group_id)s/delete', ['user_group_id']);
238 pyroutes.register('search', '/_admin/search', []);
239 pyroutes.register('search', '/_admin/search', []);
239 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
240 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
240 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
241 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
241 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
242 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
242 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
243 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
243 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
244 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
244 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
245 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
245 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
246 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
246 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
247 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
247 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
248 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
248 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
249 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
249 pyroutes.register('my_account_ssh_keys', '/_admin/my_account/ssh_keys', []);
250 pyroutes.register('my_account_ssh_keys', '/_admin/my_account/ssh_keys', []);
250 pyroutes.register('my_account_ssh_keys_generate', '/_admin/my_account/ssh_keys/generate', []);
251 pyroutes.register('my_account_ssh_keys_generate', '/_admin/my_account/ssh_keys/generate', []);
251 pyroutes.register('my_account_ssh_keys_add', '/_admin/my_account/ssh_keys/new', []);
252 pyroutes.register('my_account_ssh_keys_add', '/_admin/my_account/ssh_keys/new', []);
252 pyroutes.register('my_account_ssh_keys_delete', '/_admin/my_account/ssh_keys/delete', []);
253 pyroutes.register('my_account_ssh_keys_delete', '/_admin/my_account/ssh_keys/delete', []);
253 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
254 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
254 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
255 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
255 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
256 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
256 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
257 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
257 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
258 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
258 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
259 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
259 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
260 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
260 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
261 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
261 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
262 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
262 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
263 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
263 pyroutes.register('notifications_show_all', '/_admin/notifications', []);
264 pyroutes.register('notifications_show_all', '/_admin/notifications', []);
264 pyroutes.register('notifications_mark_all_read', '/_admin/notifications/mark_all_read', []);
265 pyroutes.register('notifications_mark_all_read', '/_admin/notifications/mark_all_read', []);
265 pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
266 pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
266 pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
267 pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
267 pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
268 pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
268 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
269 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
269 pyroutes.register('gists_show', '/_admin/gists', []);
270 pyroutes.register('gists_show', '/_admin/gists', []);
270 pyroutes.register('gists_new', '/_admin/gists/new', []);
271 pyroutes.register('gists_new', '/_admin/gists/new', []);
271 pyroutes.register('gists_create', '/_admin/gists/create', []);
272 pyroutes.register('gists_create', '/_admin/gists/create', []);
272 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
273 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
273 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
274 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
274 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
275 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
275 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
276 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
276 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
277 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
277 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
278 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
278 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
279 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
279 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
280 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
280 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
281 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
281 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
282 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
282 pyroutes.register('apiv2', '/_admin/api', []);
283 pyroutes.register('apiv2', '/_admin/api', []);
283 }
284 }
@@ -1,173 +1,185 b''
1 // # Copyright (C) 2016-2017 RhodeCode GmbH
1 // # Copyright (C) 2016-2017 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
6 // #
6 // #
7 // # This program is distributed in the hope that it will be useful,
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
10 // # GNU General Public License for more details.
11 // #
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19
19
20 var CommitsController = function () {
20 var CommitsController = function () {
21 var self = this;
21 var self = this;
22 this.$graphCanvas = $('#graph_canvas');
22 this.$graphCanvas = $('#graph_canvas');
23 this.$commitCounter = $('#commit-counter');
23 this.$commitCounter = $('#commit-counter');
24
24
25 this.getCurrentGraphData = function () {
25 this.getCurrentGraphData = function () {
26 // raw form
26 // raw form
27 return self.$graphCanvas.data('commits');
27 return self.$graphCanvas.data('commits');
28 };
28 };
29
29
30 this.setLabelText = function (graphData) {
30 this.setLabelText = function (graphData) {
31 var shown = $('.commit_hash').length;
31 var shown = $('.commit_hash').length;
32 var total = self.$commitCounter.data('total');
32 var total = self.$commitCounter.data('total');
33
33
34 if (shown == 1) {
34 if (shown == 1) {
35 var text = _gettext('showing {0} out of {1} commit').format(shown, total);
35 var text = _gettext('showing {0} out of {1} commit').format(shown, total);
36 } else {
36 } else {
37 var text = _gettext('showing {0} out of {1} commits').format(shown, total);
37 var text = _gettext('showing {0} out of {1} commits').format(shown, total);
38 }
38 }
39 self.$commitCounter.html(text)
39 self.$commitCounter.html(text)
40 };
40 };
41
41
42 this.reloadGraph = function (chunk) {
42 this.reloadGraph = function (chunk) {
43 chunk = chunk || 'next';
43 chunk = chunk || 'next';
44
44
45 // reset state on re-render !
45 // reset state on re-render !
46 self.$graphCanvas.html('');
46 self.$graphCanvas.html('');
47
47
48 var edgeData = $("[data-graph]").data('graph') || this.$graphCanvas.data('graph') || [];
48 var edgeData = $("[data-graph]").data('graph') || this.$graphCanvas.data('graph') || [];
49
49
50 // Determine max number of edges per row in graph
50 // Determine max number of edges per row in graph
51 var edgeCount = 1;
51 var edgeCount = 1;
52 $.each(edgeData, function (i, item) {
52 $.each(edgeData, function (i, item) {
53 $.each(item[2], function (key, value) {
53 $.each(item[2], function (key, value) {
54 if (value[1] > edgeCount) {
54 if (value[1] > edgeCount) {
55 edgeCount = value[1];
55 edgeCount = value[1];
56 }
56 }
57 });
57 });
58 });
58 });
59
59
60 var x_step = Math.min(10, Math.floor(86 / edgeCount));
60 var x_step = Math.min(10, Math.floor(86 / edgeCount));
61 var graph_options = {
61 var graph_options = {
62 width: 100,
62 width: 100,
63 height: $('#changesets').find('.commits-range').height(),
63 height: $('#changesets').find('.commits-range').height(),
64 x_step: x_step,
64 x_step: x_step,
65 y_step: 42,
65 y_step: 42,
66 dotRadius: 3.5,
66 dotRadius: 3.5,
67 lineWidth: 2.5
67 lineWidth: 2.5
68 };
68 };
69
69
70 var prevCommitsData = this.$graphCanvas.data('commits') || [];
70 var prevCommitsData = this.$graphCanvas.data('commits') || [];
71 var nextCommitsData = $("[data-graph]").data('commits') || [];
71 var nextCommitsData = $("[data-graph]").data('commits') || [];
72
72
73 if (chunk == 'next') {
73 if (chunk == 'next') {
74 var commitData = $.merge(prevCommitsData, nextCommitsData);
74 var commitData = $.merge(prevCommitsData, nextCommitsData);
75 } else {
75 } else {
76 var commitData = $.merge(nextCommitsData, prevCommitsData);
76 var commitData = $.merge(nextCommitsData, prevCommitsData);
77 }
77 }
78
78
79 this.$graphCanvas.data('graph', edgeData);
79 this.$graphCanvas.data('graph', edgeData);
80 this.$graphCanvas.data('commits', commitData);
80 this.$graphCanvas.data('commits', commitData);
81
81
82 // destroy dynamic loaded graph
82 // destroy dynamic loaded graph
83 $("[data-graph]").remove();
83 $("[data-graph]").remove();
84
84
85 this.$graphCanvas.commits(graph_options);
85 this.$graphCanvas.commits(graph_options);
86
86
87 this.setLabelText(edgeData);
87 this.setLabelText(edgeData);
88 if ($('.load-more-commits').find('.prev-commits').get(0)) {
88 if ($('.load-more-commits').find('.prev-commits').get(0)) {
89 var padding = 75;
89 var padding = 75;
90
90
91 } else {
91 } else {
92 var padding = 43;
92 var padding = 43;
93 }
93 }
94 $('#graph_nodes').css({'padding-top': padding});
94 $('#graph_nodes').css({'padding-top': padding});
95 };
95 };
96
96
97 this.getChunkUrl = function (page, chunk, branch) {
97 this.getChunkUrl = function (page, chunk, branch, commit_id, f_path) {
98 var urlData = {
98 var urlData = {
99 'repo_name': templateContext.repo_name,
99 'repo_name': templateContext.repo_name,
100 'page': page,
100 'page': page,
101 'chunk': chunk
101 'chunk': chunk
102 };
102 };
103
103
104 if (branch !== undefined && branch !== '') {
104 if (branch !== undefined && branch !== '') {
105 urlData['branch'] = branch;
105 urlData['branch'] = branch;
106 }
106 }
107 if (commit_id !== undefined && commit_id !== '') {
108 urlData['commit_id'] = commit_id;
109 }
110 if (f_path !== undefined && f_path !== '') {
111 urlData['f_path'] = f_path;
112 }
107
113
114 if (urlData['commit_id'] && urlData['f_path']) {
115 return pyroutes.url('repo_changelog_elements_file', urlData);
116 }
117 else {
108 return pyroutes.url('repo_changelog_elements', urlData);
118 return pyroutes.url('repo_changelog_elements', urlData);
119 }
120
109 };
121 };
110
122
111 this.loadNext = function (node, page, branch) {
123 this.loadNext = function (node, page, branch, commit_id, f_path) {
112 var loadUrl = this.getChunkUrl(page, 'next', branch);
124 var loadUrl = this.getChunkUrl(page, 'next', branch, commit_id, f_path);
113 var postData = {'graph': JSON.stringify(this.getCurrentGraphData())};
125 var postData = {'graph': JSON.stringify(this.getCurrentGraphData())};
114
126
115 $.post(loadUrl, postData, function (data) {
127 $.post(loadUrl, postData, function (data) {
116 $(node).closest('tbody').append(data);
128 $(node).closest('tbody').append(data);
117 $(node).closest('td').remove();
129 $(node).closest('td').remove();
118 self.reloadGraph('next');
130 self.reloadGraph('next');
119 })
131 })
120 };
132 };
121
133
122 this.loadPrev = function (node, page, branch) {
134 this.loadPrev = function (node, page, branch, commit_id, f_path) {
123 var loadUrl = this.getChunkUrl(page, 'prev', branch);
135 var loadUrl = this.getChunkUrl(page, 'prev', branch, commit_id, f_path);
124 var postData = {'graph': JSON.stringify(this.getCurrentGraphData())};
136 var postData = {'graph': JSON.stringify(this.getCurrentGraphData())};
125
137
126 $.post(loadUrl, postData, function (data) {
138 $.post(loadUrl, postData, function (data) {
127 $(node).closest('tbody').prepend(data);
139 $(node).closest('tbody').prepend(data);
128 $(node).closest('td').remove();
140 $(node).closest('td').remove();
129 self.reloadGraph('prev');
141 self.reloadGraph('prev');
130 })
142 })
131 };
143 };
132
144
133 this.expandCommit = function (node) {
145 this.expandCommit = function (node) {
134
146
135 var target_expand = $(node);
147 var target_expand = $(node);
136 var cid = target_expand.data('commitId');
148 var cid = target_expand.data('commitId');
137
149
138 if (target_expand.hasClass('open')) {
150 if (target_expand.hasClass('open')) {
139 $('#c-' + cid).css({
151 $('#c-' + cid).css({
140 'height': '1.5em',
152 'height': '1.5em',
141 'white-space': 'nowrap',
153 'white-space': 'nowrap',
142 'text-overflow': 'ellipsis',
154 'text-overflow': 'ellipsis',
143 'overflow': 'hidden'
155 'overflow': 'hidden'
144 });
156 });
145 $('#t-' + cid).css({
157 $('#t-' + cid).css({
146 'height': 'auto',
158 'height': 'auto',
147 'line-height': '.9em',
159 'line-height': '.9em',
148 'text-overflow': 'ellipsis',
160 'text-overflow': 'ellipsis',
149 'overflow': 'hidden',
161 'overflow': 'hidden',
150 'white-space': 'nowrap'
162 'white-space': 'nowrap'
151 });
163 });
152 target_expand.removeClass('open');
164 target_expand.removeClass('open');
153 }
165 }
154 else {
166 else {
155 $('#c-' + cid).css({
167 $('#c-' + cid).css({
156 'height': 'auto',
168 'height': 'auto',
157 'white-space': 'pre-line',
169 'white-space': 'pre-line',
158 'text-overflow': 'initial',
170 'text-overflow': 'initial',
159 'overflow': 'visible'
171 'overflow': 'visible'
160 });
172 });
161 $('#t-' + cid).css({
173 $('#t-' + cid).css({
162 'height': 'auto',
174 'height': 'auto',
163 'max-height': 'none',
175 'max-height': 'none',
164 'text-overflow': 'initial',
176 'text-overflow': 'initial',
165 'overflow': 'visible',
177 'overflow': 'visible',
166 'white-space': 'normal'
178 'white-space': 'normal'
167 });
179 });
168 target_expand.addClass('open');
180 target_expand.addClass('open');
169 }
181 }
170 // redraw the graph
182 // redraw the graph
171 self.reloadGraph();
183 self.reloadGraph();
172 }
184 }
173 };
185 };
@@ -1,144 +1,144 b''
1 ## small box that displays changed/added/removed details fetched by AJAX
1 ## small box that displays changed/added/removed details fetched by AJAX
2 <%namespace name="base" file="/base/base.mako"/>
2 <%namespace name="base" file="/base/base.mako"/>
3
3
4
4
5 % if c.prev_page:
5 % if c.prev_page:
6 <tr>
6 <tr>
7 <td colspan="9" class="load-more-commits">
7 <td colspan="9" class="load-more-commits">
8 <a class="prev-commits" href="#loadPrevCommits" onclick="commitsController.loadPrev(this, ${c.prev_page}, '${c.branch_name}');return false">
8 <a class="prev-commits" href="#loadPrevCommits" onclick="commitsController.loadPrev(this, ${c.prev_page}, '${c.branch_name}', '${c.commit_id}', '${c.f_path}');return false">
9 ${_('load previous')}
9 ${_('load previous')}
10 </a>
10 </a>
11 </td>
11 </td>
12 </tr>
12 </tr>
13 % endif
13 % endif
14
14
15 % for cnt,commit in enumerate(c.pagination):
15 % for cnt,commit in enumerate(c.pagination):
16 <tr id="sha_${commit.raw_id}" class="changelogRow container ${'tablerow%s' % (cnt%2)}">
16 <tr id="sha_${commit.raw_id}" class="changelogRow container ${'tablerow%s' % (cnt%2)}">
17
17
18 <td class="td-checkbox">
18 <td class="td-checkbox">
19 ${h.checkbox(commit.raw_id,class_="commit-range")}
19 ${h.checkbox(commit.raw_id,class_="commit-range")}
20 </td>
20 </td>
21 <td class="td-status">
21 <td class="td-status">
22
22
23 %if c.statuses.get(commit.raw_id):
23 %if c.statuses.get(commit.raw_id):
24 <div class="changeset-status-ico">
24 <div class="changeset-status-ico">
25 %if c.statuses.get(commit.raw_id)[2]:
25 %if c.statuses.get(commit.raw_id)[2]:
26 <a class="tooltip" title="${_('Commit status: %s\nClick to open associated pull request #%s') % (h.commit_status_lbl(c.statuses.get(commit.raw_id)[0]), c.statuses.get(commit.raw_id)[2])}" href="${h.route_path('pullrequest_show',repo_name=c.statuses.get(commit.raw_id)[3],pull_request_id=c.statuses.get(commit.raw_id)[2])}">
26 <a class="tooltip" title="${_('Commit status: %s\nClick to open associated pull request #%s') % (h.commit_status_lbl(c.statuses.get(commit.raw_id)[0]), c.statuses.get(commit.raw_id)[2])}" href="${h.route_path('pullrequest_show',repo_name=c.statuses.get(commit.raw_id)[3],pull_request_id=c.statuses.get(commit.raw_id)[2])}">
27 <div class="${'flag_status {}'.format(c.statuses.get(commit.raw_id)[0])}"></div>
27 <div class="${'flag_status {}'.format(c.statuses.get(commit.raw_id)[0])}"></div>
28 </a>
28 </a>
29 %else:
29 %else:
30 <a class="tooltip" title="${_('Commit status: {}').format(h.commit_status_lbl(c.statuses.get(commit.raw_id)[0]))}" href="${h.route_path('repo_commit',repo_name=c.repo_name,commit_id=commit.raw_id,_anchor='comment-%s' % c.comments[commit.raw_id][0].comment_id)}">
30 <a class="tooltip" title="${_('Commit status: {}').format(h.commit_status_lbl(c.statuses.get(commit.raw_id)[0]))}" href="${h.route_path('repo_commit',repo_name=c.repo_name,commit_id=commit.raw_id,_anchor='comment-%s' % c.comments[commit.raw_id][0].comment_id)}">
31 <div class="${'flag_status {}'.format(c.statuses.get(commit.raw_id)[0])}"></div>
31 <div class="${'flag_status {}'.format(c.statuses.get(commit.raw_id)[0])}"></div>
32 </a>
32 </a>
33 %endif
33 %endif
34 </div>
34 </div>
35 %else:
35 %else:
36 <div class="tooltip flag_status not_reviewed" title="${_('Commit status: Not Reviewed')}"></div>
36 <div class="tooltip flag_status not_reviewed" title="${_('Commit status: Not Reviewed')}"></div>
37 %endif
37 %endif
38 </td>
38 </td>
39 <td class="td-comments comments-col">
39 <td class="td-comments comments-col">
40 %if c.comments.get(commit.raw_id):
40 %if c.comments.get(commit.raw_id):
41 <a title="${_('Commit has comments')}" href="${h.route_path('repo_commit',repo_name=c.repo_name,commit_id=commit.raw_id,_anchor='comment-%s' % c.comments[commit.raw_id][0].comment_id)}">
41 <a title="${_('Commit has comments')}" href="${h.route_path('repo_commit',repo_name=c.repo_name,commit_id=commit.raw_id,_anchor='comment-%s' % c.comments[commit.raw_id][0].comment_id)}">
42 <i class="icon-comment"></i> ${len(c.comments[commit.raw_id])}
42 <i class="icon-comment"></i> ${len(c.comments[commit.raw_id])}
43 </a>
43 </a>
44 %endif
44 %endif
45 </td>
45 </td>
46 <td class="td-hash">
46 <td class="td-hash">
47 <code>
47 <code>
48
48
49 <a href="${h.route_path('repo_commit',repo_name=c.repo_name,commit_id=commit.raw_id)}">
49 <a href="${h.route_path('repo_commit',repo_name=c.repo_name,commit_id=commit.raw_id)}">
50 <span class="${'commit_hash obsolete' if getattr(commit, 'obsolete', None) else 'commit_hash'}">${h.show_id(commit)}</span>
50 <span class="${'commit_hash obsolete' if getattr(commit, 'obsolete', None) else 'commit_hash'}">${h.show_id(commit)}</span>
51 </a>
51 </a>
52 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${commit.raw_id}" title="${_('Copy the full commit id')}"></i>
52 <i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${commit.raw_id}" title="${_('Copy the full commit id')}"></i>
53 % if hasattr(commit, 'phase'):
53 % if hasattr(commit, 'phase'):
54 % if commit.phase != 'public':
54 % if commit.phase != 'public':
55 <span class="tag phase-${commit.phase} tooltip" title="${_('Commit phase')}">${commit.phase}</span>
55 <span class="tag phase-${commit.phase} tooltip" title="${_('Commit phase')}">${commit.phase}</span>
56 % endif
56 % endif
57 % endif
57 % endif
58
58
59 ## obsolete commits
59 ## obsolete commits
60 % if hasattr(commit, 'obsolete'):
60 % if hasattr(commit, 'obsolete'):
61 % if commit.obsolete:
61 % if commit.obsolete:
62 <span class="tag obsolete-${commit.obsolete} tooltip" title="${_('Evolve State')}">${_('obsolete')}</span>
62 <span class="tag obsolete-${commit.obsolete} tooltip" title="${_('Evolve State')}">${_('obsolete')}</span>
63 % endif
63 % endif
64 % endif
64 % endif
65
65
66 ## hidden commits
66 ## hidden commits
67 % if hasattr(commit, 'hidden'):
67 % if hasattr(commit, 'hidden'):
68 % if commit.hidden:
68 % if commit.hidden:
69 <span class="tag obsolete-${commit.hidden} tooltip" title="${_('Evolve State')}">${_('hidden')}</span>
69 <span class="tag obsolete-${commit.hidden} tooltip" title="${_('Evolve State')}">${_('hidden')}</span>
70 % endif
70 % endif
71 % endif
71 % endif
72
72
73 </code>
73 </code>
74 </td>
74 </td>
75 <td class="td-message expand_commit" data-commit-id="${commit.raw_id}" title="${_('Expand commit message')}" onclick="commitsController.expandCommit(this); return false">
75 <td class="td-message expand_commit" data-commit-id="${commit.raw_id}" title="${_('Expand commit message')}" onclick="commitsController.expandCommit(this); return false">
76 <div class="show_more_col">
76 <div class="show_more_col">
77 <i class="show_more"></i>&nbsp;
77 <i class="show_more"></i>&nbsp;
78 </div>
78 </div>
79 </td>
79 </td>
80 <td class="td-description mid">
80 <td class="td-description mid">
81 <div class="log-container truncate-wrap">
81 <div class="log-container truncate-wrap">
82 <div class="message truncate" id="c-${commit.raw_id}">${h.urlify_commit_message(commit.message, c.repo_name)}</div>
82 <div class="message truncate" id="c-${commit.raw_id}">${h.urlify_commit_message(commit.message, c.repo_name)}</div>
83 </div>
83 </div>
84 </td>
84 </td>
85
85
86 <td class="td-time">
86 <td class="td-time">
87 ${h.age_component(commit.date)}
87 ${h.age_component(commit.date)}
88 </td>
88 </td>
89 <td class="td-user">
89 <td class="td-user">
90 ${base.gravatar_with_user(commit.author)}
90 ${base.gravatar_with_user(commit.author)}
91 </td>
91 </td>
92
92
93 <td class="td-tags tags-col">
93 <td class="td-tags tags-col">
94 <div id="t-${commit.raw_id}">
94 <div id="t-${commit.raw_id}">
95
95
96 ## merge
96 ## merge
97 %if commit.merge:
97 %if commit.merge:
98 <span class="tag mergetag">
98 <span class="tag mergetag">
99 <i class="icon-merge"></i>${_('merge')}
99 <i class="icon-merge"></i>${_('merge')}
100 </span>
100 </span>
101 %endif
101 %endif
102
102
103 ## branch
103 ## branch
104 %if commit.branch:
104 %if commit.branch:
105 <span class="tag branchtag" title="${h.tooltip(_('Branch %s') % commit.branch)}">
105 <span class="tag branchtag" title="${h.tooltip(_('Branch %s') % commit.branch)}">
106 <a href="${h.route_path('repo_changelog',repo_name=c.repo_name,_query=dict(branch=commit.branch))}"><i class="icon-code-fork"></i>${h.shorter(commit.branch)}</a>
106 <a href="${h.route_path('repo_changelog',repo_name=c.repo_name,_query=dict(branch=commit.branch))}"><i class="icon-code-fork"></i>${h.shorter(commit.branch)}</a>
107 </span>
107 </span>
108 %endif
108 %endif
109
109
110 ## bookmarks
110 ## bookmarks
111 %if h.is_hg(c.rhodecode_repo):
111 %if h.is_hg(c.rhodecode_repo):
112 %for book in commit.bookmarks:
112 %for book in commit.bookmarks:
113 <span class="tag booktag" title="${h.tooltip(_('Bookmark %s') % book)}">
113 <span class="tag booktag" title="${h.tooltip(_('Bookmark %s') % book)}">
114 <a href="${h.route_path('repo_files:default_path',repo_name=c.repo_name,commit_id=commit.raw_id, _query=dict(at=book))}"><i class="icon-bookmark"></i>${h.shorter(book)}</a>
114 <a href="${h.route_path('repo_files:default_path',repo_name=c.repo_name,commit_id=commit.raw_id, _query=dict(at=book))}"><i class="icon-bookmark"></i>${h.shorter(book)}</a>
115 </span>
115 </span>
116 %endfor
116 %endfor
117 %endif
117 %endif
118
118
119 ## tags
119 ## tags
120 %for tag in commit.tags:
120 %for tag in commit.tags:
121 <span class="tag tagtag" title="${h.tooltip(_('Tag %s') % tag)}">
121 <span class="tag tagtag" title="${h.tooltip(_('Tag %s') % tag)}">
122 <a href="${h.route_path('repo_files:default_path',repo_name=c.repo_name,commit_id=commit.raw_id, _query=dict(at=tag))}"><i class="icon-tag"></i>${h.shorter(tag)}</a>
122 <a href="${h.route_path('repo_files:default_path',repo_name=c.repo_name,commit_id=commit.raw_id, _query=dict(at=tag))}"><i class="icon-tag"></i>${h.shorter(tag)}</a>
123 </span>
123 </span>
124 %endfor
124 %endfor
125
125
126 </div>
126 </div>
127 </td>
127 </td>
128 </tr>
128 </tr>
129 % endfor
129 % endfor
130
130
131 % if c.next_page:
131 % if c.next_page:
132 <tr>
132 <tr>
133 <td colspan="9" class="load-more-commits">
133 <td colspan="9" class="load-more-commits">
134 <a class="next-commits" href="#loadNextCommits" onclick="commitsController.loadNext(this, ${c.next_page}, '${c.branch_name}');return false">
134 <a class="next-commits" href="#loadNextCommits" onclick="commitsController.loadNext(this, ${c.next_page}, '${c.branch_name}', '${c.commit_id}', '${c.f_path}');return false">
135 ${_('load next')}
135 ${_('load next')}
136 </a>
136 </a>
137 </td>
137 </td>
138 </tr>
138 </tr>
139 % endif
139 % endif
140 <tr class="chunk-graph-data" style="display:none"
140 <tr class="chunk-graph-data" style="display:none"
141 data-graph='${c.graph_data|n}'
141 data-graph='${c.graph_data|n}'
142 data-node='${c.prev_page}:${c.next_page}'
142 data-node='${c.prev_page}:${c.next_page}'
143 data-commits='${c.graph_commits|n}'>
143 data-commits='${c.graph_commits|n}'>
144 </tr> No newline at end of file
144 </tr>
General Comments 0
You need to be logged in to leave comments. Login now