##// END OF EJS Templates
always post text about status changes of code-review
marcink -
r2796:bf3c976d beta
parent child Browse files
Show More
@@ -1,715 +1,731 b''
1 .. _changelog:
1 .. _changelog:
2
2
3 =========
3 =========
4 Changelog
4 Changelog
5 =========
5 =========
6
6
7 1.4.0 (**2012-XX-XX**)
7
8 1.4.1 (**2012-XX-XX**)
8 ----------------------
9 ----------------------
9
10
10 :status: in-progress
11 :status: in-progress
11 :branch: beta
12 :branch: beta
12
13
13 news
14 news
14 ++++
15 ++++
15
16
17 - always put a comment about code-review status change even if user send
18 empty data
19
20 fixes
21 +++++
22
23 - fixed migrations of permissions that can lead to inconsistency issue
24
25
26 1.4.0 (**2012-09-03**)
27 ----------------------
28
29 news
30 ++++
31
16 - new codereview system
32 - new codereview system
17 - email map, allowing users to have multiple email addresses mapped into
33 - email map, allowing users to have multiple email addresses mapped into
18 their accounts
34 their accounts
19 - improved git-hook system. Now all actions for git are logged into journal
35 - improved git-hook system. Now all actions for git are logged into journal
20 including pushed revisions, user and IP address
36 including pushed revisions, user and IP address
21 - changed setup-app into setup-rhodecode and added default options to it.
37 - changed setup-app into setup-rhodecode and added default options to it.
22 - new git repos are created as bare now by default
38 - new git repos are created as bare now by default
23 - #464 added links to groups in permission box
39 - #464 added links to groups in permission box
24 - #465 mentions autocomplete inside comments boxes
40 - #465 mentions autocomplete inside comments boxes
25 - #469 added --update-only option to whoosh to re-index only given list
41 - #469 added --update-only option to whoosh to re-index only given list
26 of repos in index
42 of repos in index
27 - rhodecode-api CLI client
43 - rhodecode-api CLI client
28 - new git http protocol replaced buggy dulwich implementation.
44 - new git http protocol replaced buggy dulwich implementation.
29 Now based on pygrack & gitweb
45 Now based on pygrack & gitweb
30 - Improved RSS/ATOM feeds. Discoverable by browsers using proper headers, and
46 - Improved RSS/ATOM feeds. Discoverable by browsers using proper headers, and
31 reformated based on user suggestions. Additional rss/atom feeds for user
47 reformated based on user suggestions. Additional rss/atom feeds for user
32 journal
48 journal
33 - various i18n improvements
49 - various i18n improvements
34 - #478 permissions overview for admin in user edit view
50 - #478 permissions overview for admin in user edit view
35 - File view now displays small gravatars off all authors of given file
51 - File view now displays small gravatars off all authors of given file
36 - Implemented landing revisions. Each repository will get landing_rev attribute
52 - Implemented landing revisions. Each repository will get landing_rev attribute
37 that defines 'default' revision/branch for generating readme files
53 that defines 'default' revision/branch for generating readme files
38 - Implemented #509, RhodeCode enforces SSL for push/pulling if requested at
54 - Implemented #509, RhodeCode enforces SSL for push/pulling if requested at
39 earliest possible call.
55 earliest possible call.
40 - Import remote svn repositories to mercurial using hgsubversion.
56 - Import remote svn repositories to mercurial using hgsubversion.
41 - Fixed #508 RhodeCode now has a option to explicitly set forking permissions
57 - Fixed #508 RhodeCode now has a option to explicitly set forking permissions
42 - RhodeCode can use alternative server for generating avatar icons
58 - RhodeCode can use alternative server for generating avatar icons
43 - implemented repositories locking. Pull locks, push unlocks. Also can be done
59 - implemented repositories locking. Pull locks, push unlocks. Also can be done
44 via API calls
60 via API calls
45 - #538 form for permissions can handle multiple users at once
61 - #538 form for permissions can handle multiple users at once
46
62
47 fixes
63 fixes
48 +++++
64 +++++
49
65
50 - improved translations
66 - improved translations
51 - fixes issue #455 Creating an archive generates an exception on Windows
67 - fixes issue #455 Creating an archive generates an exception on Windows
52 - fixes #448 Download ZIP archive keeps file in /tmp open and results
68 - fixes #448 Download ZIP archive keeps file in /tmp open and results
53 in out of disk space
69 in out of disk space
54 - fixes issue #454 Search results under Windows include proceeding
70 - fixes issue #454 Search results under Windows include proceeding
55 backslash
71 backslash
56 - fixed issue #450. Rhodecode no longer will crash when bad revision is
72 - fixed issue #450. Rhodecode no longer will crash when bad revision is
57 present in journal data.
73 present in journal data.
58 - fix for issue #417, git execution was broken on windows for certain
74 - fix for issue #417, git execution was broken on windows for certain
59 commands.
75 commands.
60 - fixed #413. Don't disable .git directory for bare repos on deleting
76 - fixed #413. Don't disable .git directory for bare repos on deleting
61 - fixed issue #459. Changed the way of obtaining logger in reindex task.
77 - fixed issue #459. Changed the way of obtaining logger in reindex task.
62 - fixed #453 added ID field in whoosh SCHEMA that solves the issue of
78 - fixed #453 added ID field in whoosh SCHEMA that solves the issue of
63 reindexing modified files
79 reindexing modified files
64 - fixed #481 rhodecode emails are sent without Date header
80 - fixed #481 rhodecode emails are sent without Date header
65 - fixed #458 wrong count when no repos are present
81 - fixed #458 wrong count when no repos are present
66 - fixed issue #492 missing `\ No newline at end of file` test at the end of
82 - fixed issue #492 missing `\ No newline at end of file` test at the end of
67 new chunk in html diff
83 new chunk in html diff
68 - full text search now works also for commit messages
84 - full text search now works also for commit messages
69
85
70 1.3.6 (**2012-05-17**)
86 1.3.6 (**2012-05-17**)
71 ----------------------
87 ----------------------
72
88
73 news
89 news
74 ++++
90 ++++
75
91
76 - chinese traditional translation
92 - chinese traditional translation
77 - changed setup-app into setup-rhodecode and added arguments for auto-setup
93 - changed setup-app into setup-rhodecode and added arguments for auto-setup
78 mode that doesn't need user interaction
94 mode that doesn't need user interaction
79
95
80 fixes
96 fixes
81 +++++
97 +++++
82
98
83 - fixed no scm found warning
99 - fixed no scm found warning
84 - fixed __future__ import error on rcextensions
100 - fixed __future__ import error on rcextensions
85 - made simplejson required lib for speedup on JSON encoding
101 - made simplejson required lib for speedup on JSON encoding
86 - fixes #449 bad regex could get more than revisions from parsing history
102 - fixes #449 bad regex could get more than revisions from parsing history
87 - don't clear DB session when CELERY_EAGER is turned ON
103 - don't clear DB session when CELERY_EAGER is turned ON
88
104
89 1.3.5 (**2012-05-10**)
105 1.3.5 (**2012-05-10**)
90 ----------------------
106 ----------------------
91
107
92 news
108 news
93 ++++
109 ++++
94
110
95 - use ext_json for json module
111 - use ext_json for json module
96 - unified annotation view with file source view
112 - unified annotation view with file source view
97 - notification improvements, better inbox + css
113 - notification improvements, better inbox + css
98 - #419 don't strip passwords for login forms, make rhodecode
114 - #419 don't strip passwords for login forms, make rhodecode
99 more compatible with LDAP servers
115 more compatible with LDAP servers
100 - Added HTTP_X_FORWARDED_FOR as another method of extracting
116 - Added HTTP_X_FORWARDED_FOR as another method of extracting
101 IP for pull/push logs. - moved all to base controller
117 IP for pull/push logs. - moved all to base controller
102 - #415: Adding comment to changeset causes reload.
118 - #415: Adding comment to changeset causes reload.
103 Comments are now added via ajax and doesn't reload the page
119 Comments are now added via ajax and doesn't reload the page
104 - #374 LDAP config is discarded when LDAP can't be activated
120 - #374 LDAP config is discarded when LDAP can't be activated
105 - limited push/pull operations are now logged for git in the journal
121 - limited push/pull operations are now logged for git in the journal
106 - bumped mercurial to 2.2.X series
122 - bumped mercurial to 2.2.X series
107 - added support for displaying submodules in file-browser
123 - added support for displaying submodules in file-browser
108 - #421 added bookmarks in changelog view
124 - #421 added bookmarks in changelog view
109
125
110 fixes
126 fixes
111 +++++
127 +++++
112
128
113 - fixed dev-version marker for stable when served from source codes
129 - fixed dev-version marker for stable when served from source codes
114 - fixed missing permission checks on show forks page
130 - fixed missing permission checks on show forks page
115 - #418 cast to unicode fixes in notification objects
131 - #418 cast to unicode fixes in notification objects
116 - #426 fixed mention extracting regex
132 - #426 fixed mention extracting regex
117 - fixed remote-pulling for git remotes remopositories
133 - fixed remote-pulling for git remotes remopositories
118 - fixed #434: Error when accessing files or changesets of a git repository
134 - fixed #434: Error when accessing files or changesets of a git repository
119 with submodules
135 with submodules
120 - fixed issue with empty APIKEYS for users after registration ref. #438
136 - fixed issue with empty APIKEYS for users after registration ref. #438
121 - fixed issue with getting README files from git repositories
137 - fixed issue with getting README files from git repositories
122
138
123 1.3.4 (**2012-03-28**)
139 1.3.4 (**2012-03-28**)
124 ----------------------
140 ----------------------
125
141
126 news
142 news
127 ++++
143 ++++
128
144
129 - Whoosh logging is now controlled by the .ini files logging setup
145 - Whoosh logging is now controlled by the .ini files logging setup
130 - added clone-url into edit form on /settings page
146 - added clone-url into edit form on /settings page
131 - added help text into repo add/edit forms
147 - added help text into repo add/edit forms
132 - created rcextensions module with additional mappings (ref #322) and
148 - created rcextensions module with additional mappings (ref #322) and
133 post push/pull/create repo hooks callbacks
149 post push/pull/create repo hooks callbacks
134 - implemented #377 Users view for his own permissions on account page
150 - implemented #377 Users view for his own permissions on account page
135 - #399 added inheritance of permissions for users group on repos groups
151 - #399 added inheritance of permissions for users group on repos groups
136 - #401 repository group is automatically pre-selected when adding repos
152 - #401 repository group is automatically pre-selected when adding repos
137 inside a repository group
153 inside a repository group
138 - added alternative HTTP 403 response when client failed to authenticate. Helps
154 - added alternative HTTP 403 response when client failed to authenticate. Helps
139 solving issues with Mercurial and LDAP
155 solving issues with Mercurial and LDAP
140 - #402 removed group prefix from repository name when listing repositories
156 - #402 removed group prefix from repository name when listing repositories
141 inside a group
157 inside a group
142 - added gravatars into permission view and permissions autocomplete
158 - added gravatars into permission view and permissions autocomplete
143 - #347 when running multiple RhodeCode instances, properly invalidates cache
159 - #347 when running multiple RhodeCode instances, properly invalidates cache
144 for all registered servers
160 for all registered servers
145
161
146 fixes
162 fixes
147 +++++
163 +++++
148
164
149 - fixed #390 cache invalidation problems on repos inside group
165 - fixed #390 cache invalidation problems on repos inside group
150 - fixed #385 clone by ID url was loosing proxy prefix in URL
166 - fixed #385 clone by ID url was loosing proxy prefix in URL
151 - fixed some unicode problems with waitress
167 - fixed some unicode problems with waitress
152 - fixed issue with escaping < and > in changeset commits
168 - fixed issue with escaping < and > in changeset commits
153 - fixed error occurring during recursive group creation in API
169 - fixed error occurring during recursive group creation in API
154 create_repo function
170 create_repo function
155 - fixed #393 py2.5 fixes for routes url generator
171 - fixed #393 py2.5 fixes for routes url generator
156 - fixed #397 Private repository groups shows up before login
172 - fixed #397 Private repository groups shows up before login
157 - fixed #396 fixed problems with revoking users in nested groups
173 - fixed #396 fixed problems with revoking users in nested groups
158 - fixed mysql unicode issues + specified InnoDB as default engine with
174 - fixed mysql unicode issues + specified InnoDB as default engine with
159 utf8 charset
175 utf8 charset
160 - #406 trim long branch/tag names in changelog to not break UI
176 - #406 trim long branch/tag names in changelog to not break UI
161
177
162 1.3.3 (**2012-03-02**)
178 1.3.3 (**2012-03-02**)
163 ----------------------
179 ----------------------
164
180
165 news
181 news
166 ++++
182 ++++
167
183
168
184
169 fixes
185 fixes
170 +++++
186 +++++
171
187
172 - fixed some python2.5 compatibility issues
188 - fixed some python2.5 compatibility issues
173 - fixed issues with removed repos was accidentally added as groups, after
189 - fixed issues with removed repos was accidentally added as groups, after
174 full rescan of paths
190 full rescan of paths
175 - fixes #376 Cannot edit user (using container auth)
191 - fixes #376 Cannot edit user (using container auth)
176 - fixes #378 Invalid image urls on changeset screen with proxy-prefix
192 - fixes #378 Invalid image urls on changeset screen with proxy-prefix
177 configuration
193 configuration
178 - fixed initial sorting of repos inside repo group
194 - fixed initial sorting of repos inside repo group
179 - fixes issue when user tried to resubmit same permission into user/user_groups
195 - fixes issue when user tried to resubmit same permission into user/user_groups
180 - bumped beaker version that fixes #375 leap error bug
196 - bumped beaker version that fixes #375 leap error bug
181 - fixed raw_changeset for git. It was generated with hg patch headers
197 - fixed raw_changeset for git. It was generated with hg patch headers
182 - fixed vcs issue with last_changeset for filenodes
198 - fixed vcs issue with last_changeset for filenodes
183 - fixed missing commit after hook delete
199 - fixed missing commit after hook delete
184 - fixed #372 issues with git operation detection that caused a security issue
200 - fixed #372 issues with git operation detection that caused a security issue
185 for git repos
201 for git repos
186
202
187 1.3.2 (**2012-02-28**)
203 1.3.2 (**2012-02-28**)
188 ----------------------
204 ----------------------
189
205
190 news
206 news
191 ++++
207 ++++
192
208
193
209
194 fixes
210 fixes
195 +++++
211 +++++
196
212
197 - fixed git protocol issues with repos-groups
213 - fixed git protocol issues with repos-groups
198 - fixed git remote repos validator that prevented from cloning remote git repos
214 - fixed git remote repos validator that prevented from cloning remote git repos
199 - fixes #370 ending slashes fixes for repo and groups
215 - fixes #370 ending slashes fixes for repo and groups
200 - fixes #368 improved git-protocol detection to handle other clients
216 - fixes #368 improved git-protocol detection to handle other clients
201 - fixes #366 When Setting Repository Group To Blank Repo Group Wont Be
217 - fixes #366 When Setting Repository Group To Blank Repo Group Wont Be
202 Moved To Root
218 Moved To Root
203 - fixes #371 fixed issues with beaker/sqlalchemy and non-ascii cache keys
219 - fixes #371 fixed issues with beaker/sqlalchemy and non-ascii cache keys
204 - fixed #373 missing cascade drop on user_group_to_perm table
220 - fixed #373 missing cascade drop on user_group_to_perm table
205
221
206 1.3.1 (**2012-02-27**)
222 1.3.1 (**2012-02-27**)
207 ----------------------
223 ----------------------
208
224
209 news
225 news
210 ++++
226 ++++
211
227
212
228
213 fixes
229 fixes
214 +++++
230 +++++
215
231
216 - redirection loop occurs when remember-me wasn't checked during login
232 - redirection loop occurs when remember-me wasn't checked during login
217 - fixes issues with git blob history generation
233 - fixes issues with git blob history generation
218 - don't fetch branch for git in file history dropdown. Causes unneeded slowness
234 - don't fetch branch for git in file history dropdown. Causes unneeded slowness
219
235
220 1.3.0 (**2012-02-26**)
236 1.3.0 (**2012-02-26**)
221 ----------------------
237 ----------------------
222
238
223 news
239 news
224 ++++
240 ++++
225
241
226 - code review, inspired by github code-comments
242 - code review, inspired by github code-comments
227 - #215 rst and markdown README files support
243 - #215 rst and markdown README files support
228 - #252 Container-based and proxy pass-through authentication support
244 - #252 Container-based and proxy pass-through authentication support
229 - #44 branch browser. Filtering of changelog by branches
245 - #44 branch browser. Filtering of changelog by branches
230 - mercurial bookmarks support
246 - mercurial bookmarks support
231 - new hover top menu, optimized to add maximum size for important views
247 - new hover top menu, optimized to add maximum size for important views
232 - configurable clone url template with possibility to specify protocol like
248 - configurable clone url template with possibility to specify protocol like
233 ssh:// or http:// and also manually alter other parts of clone_url.
249 ssh:// or http:// and also manually alter other parts of clone_url.
234 - enabled largefiles extension by default
250 - enabled largefiles extension by default
235 - optimized summary file pages and saved a lot of unused space in them
251 - optimized summary file pages and saved a lot of unused space in them
236 - #239 option to manually mark repository as fork
252 - #239 option to manually mark repository as fork
237 - #320 mapping of commit authors to RhodeCode users
253 - #320 mapping of commit authors to RhodeCode users
238 - #304 hashes are displayed using monospace font
254 - #304 hashes are displayed using monospace font
239 - diff configuration, toggle white lines and context lines
255 - diff configuration, toggle white lines and context lines
240 - #307 configurable diffs, whitespace toggle, increasing context lines
256 - #307 configurable diffs, whitespace toggle, increasing context lines
241 - sorting on branches, tags and bookmarks using YUI datatable
257 - sorting on branches, tags and bookmarks using YUI datatable
242 - improved file filter on files page
258 - improved file filter on files page
243 - implements #330 api method for listing nodes ar particular revision
259 - implements #330 api method for listing nodes ar particular revision
244 - #73 added linking issues in commit messages to chosen issue tracker url
260 - #73 added linking issues in commit messages to chosen issue tracker url
245 based on user defined regular expression
261 based on user defined regular expression
246 - added linking of changesets in commit messages
262 - added linking of changesets in commit messages
247 - new compact changelog with expandable commit messages
263 - new compact changelog with expandable commit messages
248 - firstname and lastname are optional in user creation
264 - firstname and lastname are optional in user creation
249 - #348 added post-create repository hook
265 - #348 added post-create repository hook
250 - #212 global encoding settings is now configurable from .ini files
266 - #212 global encoding settings is now configurable from .ini files
251 - #227 added repository groups permissions
267 - #227 added repository groups permissions
252 - markdown gets codehilite extensions
268 - markdown gets codehilite extensions
253 - new API methods, delete_repositories, grante/revoke permissions for groups
269 - new API methods, delete_repositories, grante/revoke permissions for groups
254 and repos
270 and repos
255
271
256
272
257 fixes
273 fixes
258 +++++
274 +++++
259
275
260 - rewrote dbsession management for atomic operations, and better error handling
276 - rewrote dbsession management for atomic operations, and better error handling
261 - fixed sorting of repo tables
277 - fixed sorting of repo tables
262 - #326 escape of special html entities in diffs
278 - #326 escape of special html entities in diffs
263 - normalized user_name => username in api attributes
279 - normalized user_name => username in api attributes
264 - fixes #298 ldap created users with mixed case emails created conflicts
280 - fixes #298 ldap created users with mixed case emails created conflicts
265 on saving a form
281 on saving a form
266 - fixes issue when owner of a repo couldn't revoke permissions for users
282 - fixes issue when owner of a repo couldn't revoke permissions for users
267 and groups
283 and groups
268 - fixes #271 rare JSON serialization problem with statistics
284 - fixes #271 rare JSON serialization problem with statistics
269 - fixes #337 missing validation check for conflicting names of a group with a
285 - fixes #337 missing validation check for conflicting names of a group with a
270 repositories group
286 repositories group
271 - #340 fixed session problem for mysql and celery tasks
287 - #340 fixed session problem for mysql and celery tasks
272 - fixed #331 RhodeCode mangles repository names if the a repository group
288 - fixed #331 RhodeCode mangles repository names if the a repository group
273 contains the "full path" to the repositories
289 contains the "full path" to the repositories
274 - #355 RhodeCode doesn't store encrypted LDAP passwords
290 - #355 RhodeCode doesn't store encrypted LDAP passwords
275
291
276 1.2.5 (**2012-01-28**)
292 1.2.5 (**2012-01-28**)
277 ----------------------
293 ----------------------
278
294
279 news
295 news
280 ++++
296 ++++
281
297
282 fixes
298 fixes
283 +++++
299 +++++
284
300
285 - #340 Celery complains about MySQL server gone away, added session cleanup
301 - #340 Celery complains about MySQL server gone away, added session cleanup
286 for celery tasks
302 for celery tasks
287 - #341 "scanning for repositories in None" log message during Rescan was missing
303 - #341 "scanning for repositories in None" log message during Rescan was missing
288 a parameter
304 a parameter
289 - fixed creating archives with subrepos. Some hooks were triggered during that
305 - fixed creating archives with subrepos. Some hooks were triggered during that
290 operation leading to crash.
306 operation leading to crash.
291 - fixed missing email in account page.
307 - fixed missing email in account page.
292 - Reverted Mercurial to 2.0.1 for windows due to bug in Mercurial that makes
308 - Reverted Mercurial to 2.0.1 for windows due to bug in Mercurial that makes
293 forking on windows impossible
309 forking on windows impossible
294
310
295 1.2.4 (**2012-01-19**)
311 1.2.4 (**2012-01-19**)
296 ----------------------
312 ----------------------
297
313
298 news
314 news
299 ++++
315 ++++
300
316
301 - RhodeCode is bundled with mercurial series 2.0.X by default, with
317 - RhodeCode is bundled with mercurial series 2.0.X by default, with
302 full support to largefiles extension. Enabled by default in new installations
318 full support to largefiles extension. Enabled by default in new installations
303 - #329 Ability to Add/Remove Groups to/from a Repository via AP
319 - #329 Ability to Add/Remove Groups to/from a Repository via AP
304 - added requires.txt file with requirements
320 - added requires.txt file with requirements
305
321
306 fixes
322 fixes
307 +++++
323 +++++
308
324
309 - fixes db session issues with celery when emailing admins
325 - fixes db session issues with celery when emailing admins
310 - #331 RhodeCode mangles repository names if the a repository group
326 - #331 RhodeCode mangles repository names if the a repository group
311 contains the "full path" to the repositories
327 contains the "full path" to the repositories
312 - #298 Conflicting e-mail addresses for LDAP and RhodeCode users
328 - #298 Conflicting e-mail addresses for LDAP and RhodeCode users
313 - DB session cleanup after hg protocol operations, fixes issues with
329 - DB session cleanup after hg protocol operations, fixes issues with
314 `mysql has gone away` errors
330 `mysql has gone away` errors
315 - #333 doc fixes for get_repo api function
331 - #333 doc fixes for get_repo api function
316 - #271 rare JSON serialization problem with statistics enabled
332 - #271 rare JSON serialization problem with statistics enabled
317 - #337 Fixes issues with validation of repository name conflicting with
333 - #337 Fixes issues with validation of repository name conflicting with
318 a group name. A proper message is now displayed.
334 a group name. A proper message is now displayed.
319 - #292 made ldap_dn in user edit readonly, to get rid of confusion that field
335 - #292 made ldap_dn in user edit readonly, to get rid of confusion that field
320 doesn't work
336 doesn't work
321 - #316 fixes issues with web description in hgrc files
337 - #316 fixes issues with web description in hgrc files
322
338
323 1.2.3 (**2011-11-02**)
339 1.2.3 (**2011-11-02**)
324 ----------------------
340 ----------------------
325
341
326 news
342 news
327 ++++
343 ++++
328
344
329 - added option to manage repos group for non admin users
345 - added option to manage repos group for non admin users
330 - added following API methods for get_users, create_user, get_users_groups,
346 - added following API methods for get_users, create_user, get_users_groups,
331 get_users_group, create_users_group, add_user_to_users_groups, get_repos,
347 get_users_group, create_users_group, add_user_to_users_groups, get_repos,
332 get_repo, create_repo, add_user_to_repo
348 get_repo, create_repo, add_user_to_repo
333 - implements #237 added password confirmation for my account
349 - implements #237 added password confirmation for my account
334 and admin edit user.
350 and admin edit user.
335 - implements #291 email notification for global events are now sent to all
351 - implements #291 email notification for global events are now sent to all
336 administrator users, and global config email.
352 administrator users, and global config email.
337
353
338 fixes
354 fixes
339 +++++
355 +++++
340
356
341 - added option for passing auth method for smtp mailer
357 - added option for passing auth method for smtp mailer
342 - #276 issue with adding a single user with id>10 to usergroups
358 - #276 issue with adding a single user with id>10 to usergroups
343 - #277 fixes windows LDAP settings in which missing values breaks the ldap auth
359 - #277 fixes windows LDAP settings in which missing values breaks the ldap auth
344 - #288 fixes managing of repos in a group for non admin user
360 - #288 fixes managing of repos in a group for non admin user
345
361
346 1.2.2 (**2011-10-17**)
362 1.2.2 (**2011-10-17**)
347 ----------------------
363 ----------------------
348
364
349 news
365 news
350 ++++
366 ++++
351
367
352 - #226 repo groups are available by path instead of numerical id
368 - #226 repo groups are available by path instead of numerical id
353
369
354 fixes
370 fixes
355 +++++
371 +++++
356
372
357 - #259 Groups with the same name but with different parent group
373 - #259 Groups with the same name but with different parent group
358 - #260 Put repo in group, then move group to another group -> repo becomes unavailable
374 - #260 Put repo in group, then move group to another group -> repo becomes unavailable
359 - #258 RhodeCode 1.2 assumes egg folder is writable (lockfiles problems)
375 - #258 RhodeCode 1.2 assumes egg folder is writable (lockfiles problems)
360 - #265 ldap save fails sometimes on converting attributes to booleans,
376 - #265 ldap save fails sometimes on converting attributes to booleans,
361 added getter and setter into model that will prevent from this on db model level
377 added getter and setter into model that will prevent from this on db model level
362 - fixed problems with timestamps issues #251 and #213
378 - fixed problems with timestamps issues #251 and #213
363 - fixes #266 RhodeCode allows to create repo with the same name and in
379 - fixes #266 RhodeCode allows to create repo with the same name and in
364 the same parent as group
380 the same parent as group
365 - fixes #245 Rescan of the repositories on Windows
381 - fixes #245 Rescan of the repositories on Windows
366 - fixes #248 cannot edit repos inside a group on windows
382 - fixes #248 cannot edit repos inside a group on windows
367 - fixes #219 forking problems on windows
383 - fixes #219 forking problems on windows
368
384
369 1.2.1 (**2011-10-08**)
385 1.2.1 (**2011-10-08**)
370 ----------------------
386 ----------------------
371
387
372 news
388 news
373 ++++
389 ++++
374
390
375
391
376 fixes
392 fixes
377 +++++
393 +++++
378
394
379 - fixed problems with basic auth and push problems
395 - fixed problems with basic auth and push problems
380 - gui fixes
396 - gui fixes
381 - fixed logger
397 - fixed logger
382
398
383 1.2.0 (**2011-10-07**)
399 1.2.0 (**2011-10-07**)
384 ----------------------
400 ----------------------
385
401
386 news
402 news
387 ++++
403 ++++
388
404
389 - implemented #47 repository groups
405 - implemented #47 repository groups
390 - implemented #89 Can setup google analytics code from settings menu
406 - implemented #89 Can setup google analytics code from settings menu
391 - implemented #91 added nicer looking archive urls with more download options
407 - implemented #91 added nicer looking archive urls with more download options
392 like tags, branches
408 like tags, branches
393 - implemented #44 into file browsing, and added follow branch option
409 - implemented #44 into file browsing, and added follow branch option
394 - implemented #84 downloads can be enabled/disabled for each repository
410 - implemented #84 downloads can be enabled/disabled for each repository
395 - anonymous repository can be cloned without having to pass default:default
411 - anonymous repository can be cloned without having to pass default:default
396 into clone url
412 into clone url
397 - fixed #90 whoosh indexer can index chooses repositories passed in command
413 - fixed #90 whoosh indexer can index chooses repositories passed in command
398 line
414 line
399 - extended journal with day aggregates and paging
415 - extended journal with day aggregates and paging
400 - implemented #107 source code lines highlight ranges
416 - implemented #107 source code lines highlight ranges
401 - implemented #93 customizable changelog on combined revision ranges -
417 - implemented #93 customizable changelog on combined revision ranges -
402 equivalent of githubs compare view
418 equivalent of githubs compare view
403 - implemented #108 extended and more powerful LDAP configuration
419 - implemented #108 extended and more powerful LDAP configuration
404 - implemented #56 users groups
420 - implemented #56 users groups
405 - major code rewrites optimized codes for speed and memory usage
421 - major code rewrites optimized codes for speed and memory usage
406 - raw and diff downloads are now in git format
422 - raw and diff downloads are now in git format
407 - setup command checks for write access to given path
423 - setup command checks for write access to given path
408 - fixed many issues with international characters and unicode. It uses utf8
424 - fixed many issues with international characters and unicode. It uses utf8
409 decode with replace to provide less errors even with non utf8 encoded strings
425 decode with replace to provide less errors even with non utf8 encoded strings
410 - #125 added API KEY access to feeds
426 - #125 added API KEY access to feeds
411 - #109 Repository can be created from external Mercurial link (aka. remote
427 - #109 Repository can be created from external Mercurial link (aka. remote
412 repository, and manually updated (via pull) from admin panel
428 repository, and manually updated (via pull) from admin panel
413 - beta git support - push/pull server + basic view for git repos
429 - beta git support - push/pull server + basic view for git repos
414 - added followers page and forks page
430 - added followers page and forks page
415 - server side file creation (with binary file upload interface)
431 - server side file creation (with binary file upload interface)
416 and edition with commits powered by codemirror
432 and edition with commits powered by codemirror
417 - #111 file browser file finder, quick lookup files on whole file tree
433 - #111 file browser file finder, quick lookup files on whole file tree
418 - added quick login sliding menu into main page
434 - added quick login sliding menu into main page
419 - changelog uses lazy loading of affected files details, in some scenarios
435 - changelog uses lazy loading of affected files details, in some scenarios
420 this can improve speed of changelog page dramatically especially for
436 this can improve speed of changelog page dramatically especially for
421 larger repositories.
437 larger repositories.
422 - implements #214 added support for downloading subrepos in download menu.
438 - implements #214 added support for downloading subrepos in download menu.
423 - Added basic API for direct operations on rhodecode via JSON
439 - Added basic API for direct operations on rhodecode via JSON
424 - Implemented advanced hook management
440 - Implemented advanced hook management
425
441
426 fixes
442 fixes
427 +++++
443 +++++
428
444
429 - fixed file browser bug, when switching into given form revision the url was
445 - fixed file browser bug, when switching into given form revision the url was
430 not changing
446 not changing
431 - fixed propagation to error controller on simplehg and simplegit middlewares
447 - fixed propagation to error controller on simplehg and simplegit middlewares
432 - fixed error when trying to make a download on empty repository
448 - fixed error when trying to make a download on empty repository
433 - fixed problem with '[' chars in commit messages in journal
449 - fixed problem with '[' chars in commit messages in journal
434 - fixed #99 Unicode errors, on file node paths with non utf-8 characters
450 - fixed #99 Unicode errors, on file node paths with non utf-8 characters
435 - journal fork fixes
451 - journal fork fixes
436 - removed issue with space inside renamed repository after deletion
452 - removed issue with space inside renamed repository after deletion
437 - fixed strange issue on formencode imports
453 - fixed strange issue on formencode imports
438 - fixed #126 Deleting repository on Windows, rename used incompatible chars.
454 - fixed #126 Deleting repository on Windows, rename used incompatible chars.
439 - #150 fixes for errors on repositories mapped in db but corrupted in
455 - #150 fixes for errors on repositories mapped in db but corrupted in
440 filesystem
456 filesystem
441 - fixed problem with ascendant characters in realm #181
457 - fixed problem with ascendant characters in realm #181
442 - fixed problem with sqlite file based database connection pool
458 - fixed problem with sqlite file based database connection pool
443 - whoosh indexer and code stats share the same dynamic extensions map
459 - whoosh indexer and code stats share the same dynamic extensions map
444 - fixes #188 - relationship delete of repo_to_perm entry on user removal
460 - fixes #188 - relationship delete of repo_to_perm entry on user removal
445 - fixes issue #189 Trending source files shows "show more" when no more exist
461 - fixes issue #189 Trending source files shows "show more" when no more exist
446 - fixes issue #197 Relative paths for pidlocks
462 - fixes issue #197 Relative paths for pidlocks
447 - fixes issue #198 password will require only 3 chars now for login form
463 - fixes issue #198 password will require only 3 chars now for login form
448 - fixes issue #199 wrong redirection for non admin users after creating a repository
464 - fixes issue #199 wrong redirection for non admin users after creating a repository
449 - fixes issues #202, bad db constraint made impossible to attach same group
465 - fixes issues #202, bad db constraint made impossible to attach same group
450 more than one time. Affects only mysql/postgres
466 more than one time. Affects only mysql/postgres
451 - fixes #218 os.kill patch for windows was missing sig param
467 - fixes #218 os.kill patch for windows was missing sig param
452 - improved rendering of dag (they are not trimmed anymore when number of
468 - improved rendering of dag (they are not trimmed anymore when number of
453 heads exceeds 5)
469 heads exceeds 5)
454
470
455 1.1.8 (**2011-04-12**)
471 1.1.8 (**2011-04-12**)
456 ----------------------
472 ----------------------
457
473
458 news
474 news
459 ++++
475 ++++
460
476
461 - improved windows support
477 - improved windows support
462
478
463 fixes
479 fixes
464 +++++
480 +++++
465
481
466 - fixed #140 freeze of python dateutil library, since new version is python2.x
482 - fixed #140 freeze of python dateutil library, since new version is python2.x
467 incompatible
483 incompatible
468 - setup-app will check for write permission in given path
484 - setup-app will check for write permission in given path
469 - cleaned up license info issue #149
485 - cleaned up license info issue #149
470 - fixes for issues #137,#116 and problems with unicode and accented characters.
486 - fixes for issues #137,#116 and problems with unicode and accented characters.
471 - fixes crashes on gravatar, when passed in email as unicode
487 - fixes crashes on gravatar, when passed in email as unicode
472 - fixed tooltip flickering problems
488 - fixed tooltip flickering problems
473 - fixed came_from redirection on windows
489 - fixed came_from redirection on windows
474 - fixed logging modules, and sql formatters
490 - fixed logging modules, and sql formatters
475 - windows fixes for os.kill issue #133
491 - windows fixes for os.kill issue #133
476 - fixes path splitting for windows issues #148
492 - fixes path splitting for windows issues #148
477 - fixed issue #143 wrong import on migration to 1.1.X
493 - fixed issue #143 wrong import on migration to 1.1.X
478 - fixed problems with displaying binary files, thanks to Thomas Waldmann
494 - fixed problems with displaying binary files, thanks to Thomas Waldmann
479 - removed name from archive files since it's breaking ui for long repo names
495 - removed name from archive files since it's breaking ui for long repo names
480 - fixed issue with archive headers sent to browser, thanks to Thomas Waldmann
496 - fixed issue with archive headers sent to browser, thanks to Thomas Waldmann
481 - fixed compatibility for 1024px displays, and larger dpi settings, thanks to
497 - fixed compatibility for 1024px displays, and larger dpi settings, thanks to
482 Thomas Waldmann
498 Thomas Waldmann
483 - fixed issue #166 summary pager was skipping 10 revisions on second page
499 - fixed issue #166 summary pager was skipping 10 revisions on second page
484
500
485
501
486 1.1.7 (**2011-03-23**)
502 1.1.7 (**2011-03-23**)
487 ----------------------
503 ----------------------
488
504
489 news
505 news
490 ++++
506 ++++
491
507
492 fixes
508 fixes
493 +++++
509 +++++
494
510
495 - fixed (again) #136 installation support for FreeBSD
511 - fixed (again) #136 installation support for FreeBSD
496
512
497
513
498 1.1.6 (**2011-03-21**)
514 1.1.6 (**2011-03-21**)
499 ----------------------
515 ----------------------
500
516
501 news
517 news
502 ++++
518 ++++
503
519
504 fixes
520 fixes
505 +++++
521 +++++
506
522
507 - fixed #136 installation support for FreeBSD
523 - fixed #136 installation support for FreeBSD
508 - RhodeCode will check for python version during installation
524 - RhodeCode will check for python version during installation
509
525
510 1.1.5 (**2011-03-17**)
526 1.1.5 (**2011-03-17**)
511 ----------------------
527 ----------------------
512
528
513 news
529 news
514 ++++
530 ++++
515
531
516 - basic windows support, by exchanging pybcrypt into sha256 for windows only
532 - basic windows support, by exchanging pybcrypt into sha256 for windows only
517 highly inspired by idea of mantis406
533 highly inspired by idea of mantis406
518
534
519 fixes
535 fixes
520 +++++
536 +++++
521
537
522 - fixed sorting by author in main page
538 - fixed sorting by author in main page
523 - fixed crashes with diffs on binary files
539 - fixed crashes with diffs on binary files
524 - fixed #131 problem with boolean values for LDAP
540 - fixed #131 problem with boolean values for LDAP
525 - fixed #122 mysql problems thanks to striker69
541 - fixed #122 mysql problems thanks to striker69
526 - fixed problem with errors on calling raw/raw_files/annotate functions
542 - fixed problem with errors on calling raw/raw_files/annotate functions
527 with unknown revisions
543 with unknown revisions
528 - fixed returned rawfiles attachment names with international character
544 - fixed returned rawfiles attachment names with international character
529 - cleaned out docs, big thanks to Jason Harris
545 - cleaned out docs, big thanks to Jason Harris
530
546
531 1.1.4 (**2011-02-19**)
547 1.1.4 (**2011-02-19**)
532 ----------------------
548 ----------------------
533
549
534 news
550 news
535 ++++
551 ++++
536
552
537 fixes
553 fixes
538 +++++
554 +++++
539
555
540 - fixed formencode import problem on settings page, that caused server crash
556 - fixed formencode import problem on settings page, that caused server crash
541 when that page was accessed as first after server start
557 when that page was accessed as first after server start
542 - journal fixes
558 - journal fixes
543 - fixed option to access repository just by entering http://server/<repo_name>
559 - fixed option to access repository just by entering http://server/<repo_name>
544
560
545 1.1.3 (**2011-02-16**)
561 1.1.3 (**2011-02-16**)
546 ----------------------
562 ----------------------
547
563
548 news
564 news
549 ++++
565 ++++
550
566
551 - implemented #102 allowing the '.' character in username
567 - implemented #102 allowing the '.' character in username
552 - added option to access repository just by entering http://server/<repo_name>
568 - added option to access repository just by entering http://server/<repo_name>
553 - celery task ignores result for better performance
569 - celery task ignores result for better performance
554
570
555 fixes
571 fixes
556 +++++
572 +++++
557
573
558 - fixed ehlo command and non auth mail servers on smtp_lib. Thanks to
574 - fixed ehlo command and non auth mail servers on smtp_lib. Thanks to
559 apollo13 and Johan Walles
575 apollo13 and Johan Walles
560 - small fixes in journal
576 - small fixes in journal
561 - fixed problems with getting setting for celery from .ini files
577 - fixed problems with getting setting for celery from .ini files
562 - registration, password reset and login boxes share the same title as main
578 - registration, password reset and login boxes share the same title as main
563 application now
579 application now
564 - fixed #113: to high permissions to fork repository
580 - fixed #113: to high permissions to fork repository
565 - fixed problem with '[' chars in commit messages in journal
581 - fixed problem with '[' chars in commit messages in journal
566 - removed issue with space inside renamed repository after deletion
582 - removed issue with space inside renamed repository after deletion
567 - db transaction fixes when filesystem repository creation failed
583 - db transaction fixes when filesystem repository creation failed
568 - fixed #106 relation issues on databases different than sqlite
584 - fixed #106 relation issues on databases different than sqlite
569 - fixed static files paths links to use of url() method
585 - fixed static files paths links to use of url() method
570
586
571 1.1.2 (**2011-01-12**)
587 1.1.2 (**2011-01-12**)
572 ----------------------
588 ----------------------
573
589
574 news
590 news
575 ++++
591 ++++
576
592
577
593
578 fixes
594 fixes
579 +++++
595 +++++
580
596
581 - fixes #98 protection against float division of percentage stats
597 - fixes #98 protection against float division of percentage stats
582 - fixed graph bug
598 - fixed graph bug
583 - forced webhelpers version since it was making troubles during installation
599 - forced webhelpers version since it was making troubles during installation
584
600
585 1.1.1 (**2011-01-06**)
601 1.1.1 (**2011-01-06**)
586 ----------------------
602 ----------------------
587
603
588 news
604 news
589 ++++
605 ++++
590
606
591 - added force https option into ini files for easier https usage (no need to
607 - added force https option into ini files for easier https usage (no need to
592 set server headers with this options)
608 set server headers with this options)
593 - small css updates
609 - small css updates
594
610
595 fixes
611 fixes
596 +++++
612 +++++
597
613
598 - fixed #96 redirect loop on files view on repositories without changesets
614 - fixed #96 redirect loop on files view on repositories without changesets
599 - fixed #97 unicode string passed into server header in special cases (mod_wsgi)
615 - fixed #97 unicode string passed into server header in special cases (mod_wsgi)
600 and server crashed with errors
616 and server crashed with errors
601 - fixed large tooltips problems on main page
617 - fixed large tooltips problems on main page
602 - fixed #92 whoosh indexer is more error proof
618 - fixed #92 whoosh indexer is more error proof
603
619
604 1.1.0 (**2010-12-18**)
620 1.1.0 (**2010-12-18**)
605 ----------------------
621 ----------------------
606
622
607 news
623 news
608 ++++
624 ++++
609
625
610 - rewrite of internals for vcs >=0.1.10
626 - rewrite of internals for vcs >=0.1.10
611 - uses mercurial 1.7 with dotencode disabled for maintaining compatibility
627 - uses mercurial 1.7 with dotencode disabled for maintaining compatibility
612 with older clients
628 with older clients
613 - anonymous access, authentication via ldap
629 - anonymous access, authentication via ldap
614 - performance upgrade for cached repos list - each repository has its own
630 - performance upgrade for cached repos list - each repository has its own
615 cache that's invalidated when needed.
631 cache that's invalidated when needed.
616 - performance upgrades on repositories with large amount of commits (20K+)
632 - performance upgrades on repositories with large amount of commits (20K+)
617 - main page quick filter for filtering repositories
633 - main page quick filter for filtering repositories
618 - user dashboards with ability to follow chosen repositories actions
634 - user dashboards with ability to follow chosen repositories actions
619 - sends email to admin on new user registration
635 - sends email to admin on new user registration
620 - added cache/statistics reset options into repository settings
636 - added cache/statistics reset options into repository settings
621 - more detailed action logger (based on hooks) with pushed changesets lists
637 - more detailed action logger (based on hooks) with pushed changesets lists
622 and options to disable those hooks from admin panel
638 and options to disable those hooks from admin panel
623 - introduced new enhanced changelog for merges that shows more accurate results
639 - introduced new enhanced changelog for merges that shows more accurate results
624 - new improved and faster code stats (based on pygments lexers mapping tables,
640 - new improved and faster code stats (based on pygments lexers mapping tables,
625 showing up to 10 trending sources for each repository. Additionally stats
641 showing up to 10 trending sources for each repository. Additionally stats
626 can be disabled in repository settings.
642 can be disabled in repository settings.
627 - gui optimizations, fixed application width to 1024px
643 - gui optimizations, fixed application width to 1024px
628 - added cut off (for large files/changesets) limit into config files
644 - added cut off (for large files/changesets) limit into config files
629 - whoosh, celeryd, upgrade moved to paster command
645 - whoosh, celeryd, upgrade moved to paster command
630 - other than sqlite database backends can be used
646 - other than sqlite database backends can be used
631
647
632 fixes
648 fixes
633 +++++
649 +++++
634
650
635 - fixes #61 forked repo was showing only after cache expired
651 - fixes #61 forked repo was showing only after cache expired
636 - fixes #76 no confirmation on user deletes
652 - fixes #76 no confirmation on user deletes
637 - fixes #66 Name field misspelled
653 - fixes #66 Name field misspelled
638 - fixes #72 block user removal when he owns repositories
654 - fixes #72 block user removal when he owns repositories
639 - fixes #69 added password confirmation fields
655 - fixes #69 added password confirmation fields
640 - fixes #87 RhodeCode crashes occasionally on updating repository owner
656 - fixes #87 RhodeCode crashes occasionally on updating repository owner
641 - fixes #82 broken annotations on files with more than 1 blank line at the end
657 - fixes #82 broken annotations on files with more than 1 blank line at the end
642 - a lot of fixes and tweaks for file browser
658 - a lot of fixes and tweaks for file browser
643 - fixed detached session issues
659 - fixed detached session issues
644 - fixed when user had no repos he would see all repos listed in my account
660 - fixed when user had no repos he would see all repos listed in my account
645 - fixed ui() instance bug when global hgrc settings was loaded for server
661 - fixed ui() instance bug when global hgrc settings was loaded for server
646 instance and all hgrc options were merged with our db ui() object
662 instance and all hgrc options were merged with our db ui() object
647 - numerous small bugfixes
663 - numerous small bugfixes
648
664
649 (special thanks for TkSoh for detailed feedback)
665 (special thanks for TkSoh for detailed feedback)
650
666
651
667
652 1.0.2 (**2010-11-12**)
668 1.0.2 (**2010-11-12**)
653 ----------------------
669 ----------------------
654
670
655 news
671 news
656 ++++
672 ++++
657
673
658 - tested under python2.7
674 - tested under python2.7
659 - bumped sqlalchemy and celery versions
675 - bumped sqlalchemy and celery versions
660
676
661 fixes
677 fixes
662 +++++
678 +++++
663
679
664 - fixed #59 missing graph.js
680 - fixed #59 missing graph.js
665 - fixed repo_size crash when repository had broken symlinks
681 - fixed repo_size crash when repository had broken symlinks
666 - fixed python2.5 crashes.
682 - fixed python2.5 crashes.
667
683
668
684
669 1.0.1 (**2010-11-10**)
685 1.0.1 (**2010-11-10**)
670 ----------------------
686 ----------------------
671
687
672 news
688 news
673 ++++
689 ++++
674
690
675 - small css updated
691 - small css updated
676
692
677 fixes
693 fixes
678 +++++
694 +++++
679
695
680 - fixed #53 python2.5 incompatible enumerate calls
696 - fixed #53 python2.5 incompatible enumerate calls
681 - fixed #52 disable mercurial extension for web
697 - fixed #52 disable mercurial extension for web
682 - fixed #51 deleting repositories don't delete it's dependent objects
698 - fixed #51 deleting repositories don't delete it's dependent objects
683
699
684
700
685 1.0.0 (**2010-11-02**)
701 1.0.0 (**2010-11-02**)
686 ----------------------
702 ----------------------
687
703
688 - security bugfix simplehg wasn't checking for permissions on commands
704 - security bugfix simplehg wasn't checking for permissions on commands
689 other than pull or push.
705 other than pull or push.
690 - fixed doubled messages after push or pull in admin journal
706 - fixed doubled messages after push or pull in admin journal
691 - templating and css corrections, fixed repo switcher on chrome, updated titles
707 - templating and css corrections, fixed repo switcher on chrome, updated titles
692 - admin menu accessible from options menu on repository view
708 - admin menu accessible from options menu on repository view
693 - permissions cached queries
709 - permissions cached queries
694
710
695 1.0.0rc4 (**2010-10-12**)
711 1.0.0rc4 (**2010-10-12**)
696 --------------------------
712 --------------------------
697
713
698 - fixed python2.5 missing simplejson imports (thanks to Jens BΓ€ckman)
714 - fixed python2.5 missing simplejson imports (thanks to Jens BΓ€ckman)
699 - removed cache_manager settings from sqlalchemy meta
715 - removed cache_manager settings from sqlalchemy meta
700 - added sqlalchemy cache settings to ini files
716 - added sqlalchemy cache settings to ini files
701 - validated password length and added second try of failure on paster setup-app
717 - validated password length and added second try of failure on paster setup-app
702 - fixed setup database destroy prompt even when there was no db
718 - fixed setup database destroy prompt even when there was no db
703
719
704
720
705 1.0.0rc3 (**2010-10-11**)
721 1.0.0rc3 (**2010-10-11**)
706 -------------------------
722 -------------------------
707
723
708 - fixed i18n during installation.
724 - fixed i18n during installation.
709
725
710 1.0.0rc2 (**2010-10-11**)
726 1.0.0rc2 (**2010-10-11**)
711 -------------------------
727 -------------------------
712
728
713 - Disabled dirsize in file browser, it's causing nasty bug when dir renames
729 - Disabled dirsize in file browser, it's causing nasty bug when dir renames
714 occure. After vcs is fixed it'll be put back again.
730 occure. After vcs is fixed it'll be put back again.
715 - templating/css rewrites, optimized css. No newline at end of file
731 - templating/css rewrites, optimized css.
@@ -1,443 +1,447 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.changeset
3 rhodecode.controllers.changeset
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 changeset controller for pylons showoing changes beetween
6 changeset controller for pylons showoing changes beetween
7 revisions
7 revisions
8
8
9 :created_on: Apr 25, 2010
9 :created_on: Apr 25, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 import logging
26 import logging
27 import traceback
27 import traceback
28 from collections import defaultdict
28 from collections import defaultdict
29 from webob.exc import HTTPForbidden
29 from webob.exc import HTTPForbidden
30
30
31 from pylons import tmpl_context as c, url, request, response
31 from pylons import tmpl_context as c, url, request, response
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.decorators import jsonify
34 from pylons.decorators import jsonify
35
35
36 from rhodecode.lib.vcs.exceptions import RepositoryError, ChangesetError, \
36 from rhodecode.lib.vcs.exceptions import RepositoryError, ChangesetError, \
37 ChangesetDoesNotExistError
37 ChangesetDoesNotExistError
38 from rhodecode.lib.vcs.nodes import FileNode
38 from rhodecode.lib.vcs.nodes import FileNode
39
39
40 import rhodecode.lib.helpers as h
40 import rhodecode.lib.helpers as h
41 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
41 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
42 from rhodecode.lib.base import BaseRepoController, render
42 from rhodecode.lib.base import BaseRepoController, render
43 from rhodecode.lib.utils import action_logger
43 from rhodecode.lib.utils import action_logger
44 from rhodecode.lib.compat import OrderedDict
44 from rhodecode.lib.compat import OrderedDict
45 from rhodecode.lib import diffs
45 from rhodecode.lib import diffs
46 from rhodecode.model.db import ChangesetComment, ChangesetStatus
46 from rhodecode.model.db import ChangesetComment, ChangesetStatus
47 from rhodecode.model.comment import ChangesetCommentsModel
47 from rhodecode.model.comment import ChangesetCommentsModel
48 from rhodecode.model.changeset_status import ChangesetStatusModel
48 from rhodecode.model.changeset_status import ChangesetStatusModel
49 from rhodecode.model.meta import Session
49 from rhodecode.model.meta import Session
50 from rhodecode.lib.diffs import wrapped_diff
50 from rhodecode.lib.diffs import wrapped_diff
51 from rhodecode.model.repo import RepoModel
51 from rhodecode.model.repo import RepoModel
52 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
52 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
53 from rhodecode.lib.vcs.backends.base import EmptyChangeset
53 from rhodecode.lib.vcs.backends.base import EmptyChangeset
54
54
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57
57
58 def _update_with_GET(params, GET):
58 def _update_with_GET(params, GET):
59 for k in ['diff1', 'diff2', 'diff']:
59 for k in ['diff1', 'diff2', 'diff']:
60 params[k] += GET.getall(k)
60 params[k] += GET.getall(k)
61
61
62
62
63 def anchor_url(revision, path, GET):
63 def anchor_url(revision, path, GET):
64 fid = h.FID(revision, path)
64 fid = h.FID(revision, path)
65 return h.url.current(anchor=fid, **dict(GET))
65 return h.url.current(anchor=fid, **dict(GET))
66
66
67
67
68 def get_ignore_ws(fid, GET):
68 def get_ignore_ws(fid, GET):
69 ig_ws_global = GET.get('ignorews')
69 ig_ws_global = GET.get('ignorews')
70 ig_ws = filter(lambda k: k.startswith('WS'), GET.getall(fid))
70 ig_ws = filter(lambda k: k.startswith('WS'), GET.getall(fid))
71 if ig_ws:
71 if ig_ws:
72 try:
72 try:
73 return int(ig_ws[0].split(':')[-1])
73 return int(ig_ws[0].split(':')[-1])
74 except:
74 except:
75 pass
75 pass
76 return ig_ws_global
76 return ig_ws_global
77
77
78
78
79 def _ignorews_url(GET, fileid=None):
79 def _ignorews_url(GET, fileid=None):
80 fileid = str(fileid) if fileid else None
80 fileid = str(fileid) if fileid else None
81 params = defaultdict(list)
81 params = defaultdict(list)
82 _update_with_GET(params, GET)
82 _update_with_GET(params, GET)
83 lbl = _('show white space')
83 lbl = _('show white space')
84 ig_ws = get_ignore_ws(fileid, GET)
84 ig_ws = get_ignore_ws(fileid, GET)
85 ln_ctx = get_line_ctx(fileid, GET)
85 ln_ctx = get_line_ctx(fileid, GET)
86 # global option
86 # global option
87 if fileid is None:
87 if fileid is None:
88 if ig_ws is None:
88 if ig_ws is None:
89 params['ignorews'] += [1]
89 params['ignorews'] += [1]
90 lbl = _('ignore white space')
90 lbl = _('ignore white space')
91 ctx_key = 'context'
91 ctx_key = 'context'
92 ctx_val = ln_ctx
92 ctx_val = ln_ctx
93 # per file options
93 # per file options
94 else:
94 else:
95 if ig_ws is None:
95 if ig_ws is None:
96 params[fileid] += ['WS:1']
96 params[fileid] += ['WS:1']
97 lbl = _('ignore white space')
97 lbl = _('ignore white space')
98
98
99 ctx_key = fileid
99 ctx_key = fileid
100 ctx_val = 'C:%s' % ln_ctx
100 ctx_val = 'C:%s' % ln_ctx
101 # if we have passed in ln_ctx pass it along to our params
101 # if we have passed in ln_ctx pass it along to our params
102 if ln_ctx:
102 if ln_ctx:
103 params[ctx_key] += [ctx_val]
103 params[ctx_key] += [ctx_val]
104
104
105 params['anchor'] = fileid
105 params['anchor'] = fileid
106 img = h.image(h.url('/images/icons/text_strikethrough.png'), lbl, class_='icon')
106 img = h.image(h.url('/images/icons/text_strikethrough.png'), lbl, class_='icon')
107 return h.link_to(img, h.url.current(**params), title=lbl, class_='tooltip')
107 return h.link_to(img, h.url.current(**params), title=lbl, class_='tooltip')
108
108
109
109
110 def get_line_ctx(fid, GET):
110 def get_line_ctx(fid, GET):
111 ln_ctx_global = GET.get('context')
111 ln_ctx_global = GET.get('context')
112 ln_ctx = filter(lambda k: k.startswith('C'), GET.getall(fid))
112 ln_ctx = filter(lambda k: k.startswith('C'), GET.getall(fid))
113
113
114 if ln_ctx:
114 if ln_ctx:
115 retval = ln_ctx[0].split(':')[-1]
115 retval = ln_ctx[0].split(':')[-1]
116 else:
116 else:
117 retval = ln_ctx_global
117 retval = ln_ctx_global
118
118
119 try:
119 try:
120 return int(retval)
120 return int(retval)
121 except:
121 except:
122 return
122 return
123
123
124
124
125 def _context_url(GET, fileid=None):
125 def _context_url(GET, fileid=None):
126 """
126 """
127 Generates url for context lines
127 Generates url for context lines
128
128
129 :param fileid:
129 :param fileid:
130 """
130 """
131
131
132 fileid = str(fileid) if fileid else None
132 fileid = str(fileid) if fileid else None
133 ig_ws = get_ignore_ws(fileid, GET)
133 ig_ws = get_ignore_ws(fileid, GET)
134 ln_ctx = (get_line_ctx(fileid, GET) or 3) * 2
134 ln_ctx = (get_line_ctx(fileid, GET) or 3) * 2
135
135
136 params = defaultdict(list)
136 params = defaultdict(list)
137 _update_with_GET(params, GET)
137 _update_with_GET(params, GET)
138
138
139 # global option
139 # global option
140 if fileid is None:
140 if fileid is None:
141 if ln_ctx > 0:
141 if ln_ctx > 0:
142 params['context'] += [ln_ctx]
142 params['context'] += [ln_ctx]
143
143
144 if ig_ws:
144 if ig_ws:
145 ig_ws_key = 'ignorews'
145 ig_ws_key = 'ignorews'
146 ig_ws_val = 1
146 ig_ws_val = 1
147
147
148 # per file option
148 # per file option
149 else:
149 else:
150 params[fileid] += ['C:%s' % ln_ctx]
150 params[fileid] += ['C:%s' % ln_ctx]
151 ig_ws_key = fileid
151 ig_ws_key = fileid
152 ig_ws_val = 'WS:%s' % 1
152 ig_ws_val = 'WS:%s' % 1
153
153
154 if ig_ws:
154 if ig_ws:
155 params[ig_ws_key] += [ig_ws_val]
155 params[ig_ws_key] += [ig_ws_val]
156
156
157 lbl = _('%s line context') % ln_ctx
157 lbl = _('%s line context') % ln_ctx
158
158
159 params['anchor'] = fileid
159 params['anchor'] = fileid
160 img = h.image(h.url('/images/icons/table_add.png'), lbl, class_='icon')
160 img = h.image(h.url('/images/icons/table_add.png'), lbl, class_='icon')
161 return h.link_to(img, h.url.current(**params), title=lbl, class_='tooltip')
161 return h.link_to(img, h.url.current(**params), title=lbl, class_='tooltip')
162
162
163
163
164 class ChangesetController(BaseRepoController):
164 class ChangesetController(BaseRepoController):
165
165
166 @LoginRequired()
166 @LoginRequired()
167 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
167 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
168 'repository.admin')
168 'repository.admin')
169 def __before__(self):
169 def __before__(self):
170 super(ChangesetController, self).__before__()
170 super(ChangesetController, self).__before__()
171 c.affected_files_cut_off = 60
171 c.affected_files_cut_off = 60
172 repo_model = RepoModel()
172 repo_model = RepoModel()
173 c.users_array = repo_model.get_users_js()
173 c.users_array = repo_model.get_users_js()
174 c.users_groups_array = repo_model.get_users_groups_js()
174 c.users_groups_array = repo_model.get_users_groups_js()
175
175
176 def index(self, revision):
176 def index(self, revision):
177
177
178 c.anchor_url = anchor_url
178 c.anchor_url = anchor_url
179 c.ignorews_url = _ignorews_url
179 c.ignorews_url = _ignorews_url
180 c.context_url = _context_url
180 c.context_url = _context_url
181 limit_off = request.GET.get('fulldiff')
181 limit_off = request.GET.get('fulldiff')
182 #get ranges of revisions if preset
182 #get ranges of revisions if preset
183 rev_range = revision.split('...')[:2]
183 rev_range = revision.split('...')[:2]
184 enable_comments = True
184 enable_comments = True
185 try:
185 try:
186 if len(rev_range) == 2:
186 if len(rev_range) == 2:
187 enable_comments = False
187 enable_comments = False
188 rev_start = rev_range[0]
188 rev_start = rev_range[0]
189 rev_end = rev_range[1]
189 rev_end = rev_range[1]
190 rev_ranges = c.rhodecode_repo.get_changesets(start=rev_start,
190 rev_ranges = c.rhodecode_repo.get_changesets(start=rev_start,
191 end=rev_end)
191 end=rev_end)
192 else:
192 else:
193 rev_ranges = [c.rhodecode_repo.get_changeset(revision)]
193 rev_ranges = [c.rhodecode_repo.get_changeset(revision)]
194
194
195 c.cs_ranges = list(rev_ranges)
195 c.cs_ranges = list(rev_ranges)
196 if not c.cs_ranges:
196 if not c.cs_ranges:
197 raise RepositoryError('Changeset range returned empty result')
197 raise RepositoryError('Changeset range returned empty result')
198
198
199 except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
199 except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
200 log.error(traceback.format_exc())
200 log.error(traceback.format_exc())
201 h.flash(str(e), category='warning')
201 h.flash(str(e), category='warning')
202 return redirect(url('home'))
202 return redirect(url('home'))
203
203
204 c.changes = OrderedDict()
204 c.changes = OrderedDict()
205
205
206 c.lines_added = 0 # count of lines added
206 c.lines_added = 0 # count of lines added
207 c.lines_deleted = 0 # count of lines removes
207 c.lines_deleted = 0 # count of lines removes
208
208
209 cumulative_diff = 0
209 cumulative_diff = 0
210 c.cut_off = False # defines if cut off limit is reached
210 c.cut_off = False # defines if cut off limit is reached
211 c.changeset_statuses = ChangesetStatus.STATUSES
211 c.changeset_statuses = ChangesetStatus.STATUSES
212 c.comments = []
212 c.comments = []
213 c.statuses = []
213 c.statuses = []
214 c.inline_comments = []
214 c.inline_comments = []
215 c.inline_cnt = 0
215 c.inline_cnt = 0
216 # Iterate over ranges (default changeset view is always one changeset)
216 # Iterate over ranges (default changeset view is always one changeset)
217 for changeset in c.cs_ranges:
217 for changeset in c.cs_ranges:
218
218
219 c.statuses.extend([ChangesetStatusModel()\
219 c.statuses.extend([ChangesetStatusModel()\
220 .get_status(c.rhodecode_db_repo.repo_id,
220 .get_status(c.rhodecode_db_repo.repo_id,
221 changeset.raw_id)])
221 changeset.raw_id)])
222
222
223 c.comments.extend(ChangesetCommentsModel()\
223 c.comments.extend(ChangesetCommentsModel()\
224 .get_comments(c.rhodecode_db_repo.repo_id,
224 .get_comments(c.rhodecode_db_repo.repo_id,
225 revision=changeset.raw_id))
225 revision=changeset.raw_id))
226 inlines = ChangesetCommentsModel()\
226 inlines = ChangesetCommentsModel()\
227 .get_inline_comments(c.rhodecode_db_repo.repo_id,
227 .get_inline_comments(c.rhodecode_db_repo.repo_id,
228 revision=changeset.raw_id)
228 revision=changeset.raw_id)
229 c.inline_comments.extend(inlines)
229 c.inline_comments.extend(inlines)
230 c.changes[changeset.raw_id] = []
230 c.changes[changeset.raw_id] = []
231 try:
231 try:
232 changeset_parent = changeset.parents[0]
232 changeset_parent = changeset.parents[0]
233 except IndexError:
233 except IndexError:
234 changeset_parent = None
234 changeset_parent = None
235
235
236 #==================================================================
236 #==================================================================
237 # ADDED FILES
237 # ADDED FILES
238 #==================================================================
238 #==================================================================
239 for node in changeset.added:
239 for node in changeset.added:
240 fid = h.FID(revision, node.path)
240 fid = h.FID(revision, node.path)
241 line_context_lcl = get_line_ctx(fid, request.GET)
241 line_context_lcl = get_line_ctx(fid, request.GET)
242 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
242 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
243 lim = self.cut_off_limit
243 lim = self.cut_off_limit
244 if cumulative_diff > self.cut_off_limit:
244 if cumulative_diff > self.cut_off_limit:
245 lim = -1 if limit_off is None else None
245 lim = -1 if limit_off is None else None
246 size, cs1, cs2, diff, st = wrapped_diff(
246 size, cs1, cs2, diff, st = wrapped_diff(
247 filenode_old=None,
247 filenode_old=None,
248 filenode_new=node,
248 filenode_new=node,
249 cut_off_limit=lim,
249 cut_off_limit=lim,
250 ignore_whitespace=ign_whitespace_lcl,
250 ignore_whitespace=ign_whitespace_lcl,
251 line_context=line_context_lcl,
251 line_context=line_context_lcl,
252 enable_comments=enable_comments
252 enable_comments=enable_comments
253 )
253 )
254 cumulative_diff += size
254 cumulative_diff += size
255 c.lines_added += st[0]
255 c.lines_added += st[0]
256 c.lines_deleted += st[1]
256 c.lines_deleted += st[1]
257 c.changes[changeset.raw_id].append(
257 c.changes[changeset.raw_id].append(
258 ('added', node, diff, cs1, cs2, st)
258 ('added', node, diff, cs1, cs2, st)
259 )
259 )
260
260
261 #==================================================================
261 #==================================================================
262 # CHANGED FILES
262 # CHANGED FILES
263 #==================================================================
263 #==================================================================
264 for node in changeset.changed:
264 for node in changeset.changed:
265 try:
265 try:
266 filenode_old = changeset_parent.get_node(node.path)
266 filenode_old = changeset_parent.get_node(node.path)
267 except ChangesetError:
267 except ChangesetError:
268 log.warning('Unable to fetch parent node for diff')
268 log.warning('Unable to fetch parent node for diff')
269 filenode_old = FileNode(node.path, '', EmptyChangeset())
269 filenode_old = FileNode(node.path, '', EmptyChangeset())
270
270
271 fid = h.FID(revision, node.path)
271 fid = h.FID(revision, node.path)
272 line_context_lcl = get_line_ctx(fid, request.GET)
272 line_context_lcl = get_line_ctx(fid, request.GET)
273 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
273 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
274 lim = self.cut_off_limit
274 lim = self.cut_off_limit
275 if cumulative_diff > self.cut_off_limit:
275 if cumulative_diff > self.cut_off_limit:
276 lim = -1 if limit_off is None else None
276 lim = -1 if limit_off is None else None
277 size, cs1, cs2, diff, st = wrapped_diff(
277 size, cs1, cs2, diff, st = wrapped_diff(
278 filenode_old=filenode_old,
278 filenode_old=filenode_old,
279 filenode_new=node,
279 filenode_new=node,
280 cut_off_limit=lim,
280 cut_off_limit=lim,
281 ignore_whitespace=ign_whitespace_lcl,
281 ignore_whitespace=ign_whitespace_lcl,
282 line_context=line_context_lcl,
282 line_context=line_context_lcl,
283 enable_comments=enable_comments
283 enable_comments=enable_comments
284 )
284 )
285 cumulative_diff += size
285 cumulative_diff += size
286 c.lines_added += st[0]
286 c.lines_added += st[0]
287 c.lines_deleted += st[1]
287 c.lines_deleted += st[1]
288 c.changes[changeset.raw_id].append(
288 c.changes[changeset.raw_id].append(
289 ('changed', node, diff, cs1, cs2, st)
289 ('changed', node, diff, cs1, cs2, st)
290 )
290 )
291 #==================================================================
291 #==================================================================
292 # REMOVED FILES
292 # REMOVED FILES
293 #==================================================================
293 #==================================================================
294 for node in changeset.removed:
294 for node in changeset.removed:
295 c.changes[changeset.raw_id].append(
295 c.changes[changeset.raw_id].append(
296 ('removed', node, None, None, None, (0, 0))
296 ('removed', node, None, None, None, (0, 0))
297 )
297 )
298
298
299 # count inline comments
299 # count inline comments
300 for __, lines in c.inline_comments:
300 for __, lines in c.inline_comments:
301 for comments in lines.values():
301 for comments in lines.values():
302 c.inline_cnt += len(comments)
302 c.inline_cnt += len(comments)
303
303
304 if len(c.cs_ranges) == 1:
304 if len(c.cs_ranges) == 1:
305 c.changeset = c.cs_ranges[0]
305 c.changeset = c.cs_ranges[0]
306 c.changes = c.changes[c.changeset.raw_id]
306 c.changes = c.changes[c.changeset.raw_id]
307
307
308 return render('changeset/changeset.html')
308 return render('changeset/changeset.html')
309 else:
309 else:
310 return render('changeset/changeset_range.html')
310 return render('changeset/changeset_range.html')
311
311
312 def raw_changeset(self, revision):
312 def raw_changeset(self, revision):
313
313
314 method = request.GET.get('diff', 'show')
314 method = request.GET.get('diff', 'show')
315 ignore_whitespace = request.GET.get('ignorews') == '1'
315 ignore_whitespace = request.GET.get('ignorews') == '1'
316 line_context = request.GET.get('context', 3)
316 line_context = request.GET.get('context', 3)
317 try:
317 try:
318 c.scm_type = c.rhodecode_repo.alias
318 c.scm_type = c.rhodecode_repo.alias
319 c.changeset = c.rhodecode_repo.get_changeset(revision)
319 c.changeset = c.rhodecode_repo.get_changeset(revision)
320 except RepositoryError:
320 except RepositoryError:
321 log.error(traceback.format_exc())
321 log.error(traceback.format_exc())
322 return redirect(url('home'))
322 return redirect(url('home'))
323 else:
323 else:
324 try:
324 try:
325 c.changeset_parent = c.changeset.parents[0]
325 c.changeset_parent = c.changeset.parents[0]
326 except IndexError:
326 except IndexError:
327 c.changeset_parent = None
327 c.changeset_parent = None
328 c.changes = []
328 c.changes = []
329
329
330 for node in c.changeset.added:
330 for node in c.changeset.added:
331 filenode_old = FileNode(node.path, '')
331 filenode_old = FileNode(node.path, '')
332 if filenode_old.is_binary or node.is_binary:
332 if filenode_old.is_binary or node.is_binary:
333 diff = _('binary file') + '\n'
333 diff = _('binary file') + '\n'
334 else:
334 else:
335 f_gitdiff = diffs.get_gitdiff(filenode_old, node,
335 f_gitdiff = diffs.get_gitdiff(filenode_old, node,
336 ignore_whitespace=ignore_whitespace,
336 ignore_whitespace=ignore_whitespace,
337 context=line_context)
337 context=line_context)
338 diff = diffs.DiffProcessor(f_gitdiff,
338 diff = diffs.DiffProcessor(f_gitdiff,
339 format='gitdiff').raw_diff()
339 format='gitdiff').raw_diff()
340
340
341 cs1 = None
341 cs1 = None
342 cs2 = node.changeset.raw_id
342 cs2 = node.changeset.raw_id
343 c.changes.append(('added', node, diff, cs1, cs2))
343 c.changes.append(('added', node, diff, cs1, cs2))
344
344
345 for node in c.changeset.changed:
345 for node in c.changeset.changed:
346 filenode_old = c.changeset_parent.get_node(node.path)
346 filenode_old = c.changeset_parent.get_node(node.path)
347 if filenode_old.is_binary or node.is_binary:
347 if filenode_old.is_binary or node.is_binary:
348 diff = _('binary file')
348 diff = _('binary file')
349 else:
349 else:
350 f_gitdiff = diffs.get_gitdiff(filenode_old, node,
350 f_gitdiff = diffs.get_gitdiff(filenode_old, node,
351 ignore_whitespace=ignore_whitespace,
351 ignore_whitespace=ignore_whitespace,
352 context=line_context)
352 context=line_context)
353 diff = diffs.DiffProcessor(f_gitdiff,
353 diff = diffs.DiffProcessor(f_gitdiff,
354 format='gitdiff').raw_diff()
354 format='gitdiff').raw_diff()
355
355
356 cs1 = filenode_old.changeset.raw_id
356 cs1 = filenode_old.changeset.raw_id
357 cs2 = node.changeset.raw_id
357 cs2 = node.changeset.raw_id
358 c.changes.append(('changed', node, diff, cs1, cs2))
358 c.changes.append(('changed', node, diff, cs1, cs2))
359
359
360 response.content_type = 'text/plain'
360 response.content_type = 'text/plain'
361
361
362 if method == 'download':
362 if method == 'download':
363 response.content_disposition = 'attachment; filename=%s.patch' \
363 response.content_disposition = 'attachment; filename=%s.patch' \
364 % revision
364 % revision
365
365
366 c.parent_tmpl = ''.join(['# Parent %s\n' % x.raw_id
366 c.parent_tmpl = ''.join(['# Parent %s\n' % x.raw_id
367 for x in c.changeset.parents])
367 for x in c.changeset.parents])
368
368
369 c.diffs = ''
369 c.diffs = ''
370 for x in c.changes:
370 for x in c.changes:
371 c.diffs += x[2]
371 c.diffs += x[2]
372
372
373 return render('changeset/raw_changeset.html')
373 return render('changeset/raw_changeset.html')
374
374
375 @jsonify
375 @jsonify
376 def comment(self, repo_name, revision):
376 def comment(self, repo_name, revision):
377 status = request.POST.get('changeset_status')
377 status = request.POST.get('changeset_status')
378 change_status = request.POST.get('change_changeset_status')
378 change_status = request.POST.get('change_changeset_status')
379 text = request.POST.get('text')
380 if status and change_status:
381 text = text or (_('Status change -> %s')
382 % ChangesetStatus.get_status_lbl(status))
379
383
380 comm = ChangesetCommentsModel().create(
384 comm = ChangesetCommentsModel().create(
381 text=request.POST.get('text'),
385 text=text,
382 repo=c.rhodecode_db_repo.repo_id,
386 repo=c.rhodecode_db_repo.repo_id,
383 user=c.rhodecode_user.user_id,
387 user=c.rhodecode_user.user_id,
384 revision=revision,
388 revision=revision,
385 f_path=request.POST.get('f_path'),
389 f_path=request.POST.get('f_path'),
386 line_no=request.POST.get('line'),
390 line_no=request.POST.get('line'),
387 status_change=(ChangesetStatus.get_status_lbl(status)
391 status_change=(ChangesetStatus.get_status_lbl(status)
388 if status and change_status else None)
392 if status and change_status else None)
389 )
393 )
390
394
391 # get status if set !
395 # get status if set !
392 if status and change_status:
396 if status and change_status:
393 # if latest status was from pull request and it's closed
397 # if latest status was from pull request and it's closed
394 # disallow changing status !
398 # disallow changing status !
395 # dont_allow_on_closed_pull_request = True !
399 # dont_allow_on_closed_pull_request = True !
396
400
397 try:
401 try:
398 ChangesetStatusModel().set_status(
402 ChangesetStatusModel().set_status(
399 c.rhodecode_db_repo.repo_id,
403 c.rhodecode_db_repo.repo_id,
400 status,
404 status,
401 c.rhodecode_user.user_id,
405 c.rhodecode_user.user_id,
402 comm,
406 comm,
403 revision=revision,
407 revision=revision,
404 dont_allow_on_closed_pull_request=True
408 dont_allow_on_closed_pull_request=True
405 )
409 )
406 except StatusChangeOnClosedPullRequestError:
410 except StatusChangeOnClosedPullRequestError:
407 log.error(traceback.format_exc())
411 log.error(traceback.format_exc())
408 msg = _('Changing status on a changeset associated with'
412 msg = _('Changing status on a changeset associated with'
409 'a closed pull request is not allowed')
413 'a closed pull request is not allowed')
410 h.flash(msg, category='warning')
414 h.flash(msg, category='warning')
411 return redirect(h.url('changeset_home', repo_name=repo_name,
415 return redirect(h.url('changeset_home', repo_name=repo_name,
412 revision=revision))
416 revision=revision))
413 action_logger(self.rhodecode_user,
417 action_logger(self.rhodecode_user,
414 'user_commented_revision:%s' % revision,
418 'user_commented_revision:%s' % revision,
415 c.rhodecode_db_repo, self.ip_addr, self.sa)
419 c.rhodecode_db_repo, self.ip_addr, self.sa)
416
420
417 Session().commit()
421 Session().commit()
418
422
419 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
423 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
420 return redirect(h.url('changeset_home', repo_name=repo_name,
424 return redirect(h.url('changeset_home', repo_name=repo_name,
421 revision=revision))
425 revision=revision))
422
426
423 data = {
427 data = {
424 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
428 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
425 }
429 }
426 if comm:
430 if comm:
427 c.co = comm
431 c.co = comm
428 data.update(comm.get_dict())
432 data.update(comm.get_dict())
429 data.update({'rendered_text':
433 data.update({'rendered_text':
430 render('changeset/changeset_comment_block.html')})
434 render('changeset/changeset_comment_block.html')})
431
435
432 return data
436 return data
433
437
434 @jsonify
438 @jsonify
435 def delete_comment(self, repo_name, comment_id):
439 def delete_comment(self, repo_name, comment_id):
436 co = ChangesetComment.get(comment_id)
440 co = ChangesetComment.get(comment_id)
437 owner = lambda: co.author.user_id == c.rhodecode_user.user_id
441 owner = lambda: co.author.user_id == c.rhodecode_user.user_id
438 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
442 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
439 ChangesetCommentsModel().delete(comment=co)
443 ChangesetCommentsModel().delete(comment=co)
440 Session().commit()
444 Session().commit()
441 return True
445 return True
442 else:
446 else:
443 raise HTTPForbidden()
447 raise HTTPForbidden()
@@ -1,404 +1,407 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.pullrequests
3 rhodecode.controllers.pullrequests
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 pull requests controller for rhodecode for initializing pull requests
6 pull requests controller for rhodecode for initializing pull requests
7
7
8 :created_on: May 7, 2012
8 :created_on: May 7, 2012
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import logging
25 import logging
26 import traceback
26 import traceback
27 import formencode
27 import formencode
28
28
29 from webob.exc import HTTPNotFound, HTTPForbidden
29 from webob.exc import HTTPNotFound, HTTPForbidden
30 from collections import defaultdict
30 from collections import defaultdict
31 from itertools import groupby
31 from itertools import groupby
32
32
33 from pylons import request, response, session, tmpl_context as c, url
33 from pylons import request, response, session, tmpl_context as c, url
34 from pylons.controllers.util import abort, redirect
34 from pylons.controllers.util import abort, redirect
35 from pylons.i18n.translation import _
35 from pylons.i18n.translation import _
36 from pylons.decorators import jsonify
36 from pylons.decorators import jsonify
37
37
38 from rhodecode.lib.compat import json
38 from rhodecode.lib.compat import json
39 from rhodecode.lib.base import BaseRepoController, render
39 from rhodecode.lib.base import BaseRepoController, render
40 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
40 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
41 NotAnonymous
41 NotAnonymous
42 from rhodecode.lib import helpers as h
42 from rhodecode.lib import helpers as h
43 from rhodecode.lib import diffs
43 from rhodecode.lib import diffs
44 from rhodecode.lib.utils import action_logger
44 from rhodecode.lib.utils import action_logger
45 from rhodecode.model.db import User, PullRequest, ChangesetStatus,\
45 from rhodecode.model.db import User, PullRequest, ChangesetStatus,\
46 ChangesetComment
46 ChangesetComment
47 from rhodecode.model.pull_request import PullRequestModel
47 from rhodecode.model.pull_request import PullRequestModel
48 from rhodecode.model.meta import Session
48 from rhodecode.model.meta import Session
49 from rhodecode.model.repo import RepoModel
49 from rhodecode.model.repo import RepoModel
50 from rhodecode.model.comment import ChangesetCommentsModel
50 from rhodecode.model.comment import ChangesetCommentsModel
51 from rhodecode.model.changeset_status import ChangesetStatusModel
51 from rhodecode.model.changeset_status import ChangesetStatusModel
52 from rhodecode.model.forms import PullRequestForm
52 from rhodecode.model.forms import PullRequestForm
53
53
54 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
55
55
56
56
57 class PullrequestsController(BaseRepoController):
57 class PullrequestsController(BaseRepoController):
58
58
59 @LoginRequired()
59 @LoginRequired()
60 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
60 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
61 'repository.admin')
61 'repository.admin')
62 def __before__(self):
62 def __before__(self):
63 super(PullrequestsController, self).__before__()
63 super(PullrequestsController, self).__before__()
64 repo_model = RepoModel()
64 repo_model = RepoModel()
65 c.users_array = repo_model.get_users_js()
65 c.users_array = repo_model.get_users_js()
66 c.users_groups_array = repo_model.get_users_groups_js()
66 c.users_groups_array = repo_model.get_users_groups_js()
67
67
68 def _get_repo_refs(self, repo):
68 def _get_repo_refs(self, repo):
69 hist_l = []
69 hist_l = []
70
70
71 branches_group = ([('branch:%s:%s' % (k, v), k) for
71 branches_group = ([('branch:%s:%s' % (k, v), k) for
72 k, v in repo.branches.iteritems()], _("Branches"))
72 k, v in repo.branches.iteritems()], _("Branches"))
73 bookmarks_group = ([('book:%s:%s' % (k, v), k) for
73 bookmarks_group = ([('book:%s:%s' % (k, v), k) for
74 k, v in repo.bookmarks.iteritems()], _("Bookmarks"))
74 k, v in repo.bookmarks.iteritems()], _("Bookmarks"))
75 tags_group = ([('tag:%s:%s' % (k, v), k) for
75 tags_group = ([('tag:%s:%s' % (k, v), k) for
76 k, v in repo.tags.iteritems()], _("Tags"))
76 k, v in repo.tags.iteritems()], _("Tags"))
77
77
78 hist_l.append(bookmarks_group)
78 hist_l.append(bookmarks_group)
79 hist_l.append(branches_group)
79 hist_l.append(branches_group)
80 hist_l.append(tags_group)
80 hist_l.append(tags_group)
81
81
82 return hist_l
82 return hist_l
83
83
84 def show_all(self, repo_name):
84 def show_all(self, repo_name):
85 c.pull_requests = PullRequestModel().get_all(repo_name)
85 c.pull_requests = PullRequestModel().get_all(repo_name)
86 c.repo_name = repo_name
86 c.repo_name = repo_name
87 return render('/pullrequests/pullrequest_show_all.html')
87 return render('/pullrequests/pullrequest_show_all.html')
88
88
89 @NotAnonymous()
89 @NotAnonymous()
90 def index(self):
90 def index(self):
91 org_repo = c.rhodecode_db_repo
91 org_repo = c.rhodecode_db_repo
92
92
93 if org_repo.scm_instance.alias != 'hg':
93 if org_repo.scm_instance.alias != 'hg':
94 log.error('Review not available for GIT REPOS')
94 log.error('Review not available for GIT REPOS')
95 raise HTTPNotFound
95 raise HTTPNotFound
96
96
97 other_repos_info = {}
97 other_repos_info = {}
98
98
99 c.org_refs = self._get_repo_refs(c.rhodecode_repo)
99 c.org_refs = self._get_repo_refs(c.rhodecode_repo)
100 c.org_repos = []
100 c.org_repos = []
101 c.other_repos = []
101 c.other_repos = []
102 c.org_repos.append((org_repo.repo_name, '%s/%s' % (
102 c.org_repos.append((org_repo.repo_name, '%s/%s' % (
103 org_repo.user.username, c.repo_name))
103 org_repo.user.username, c.repo_name))
104 )
104 )
105
105
106 # add org repo to other so we can open pull request agains itself
106 # add org repo to other so we can open pull request agains itself
107 c.other_repos.extend(c.org_repos)
107 c.other_repos.extend(c.org_repos)
108
108
109 c.default_pull_request = org_repo.repo_name
109 c.default_pull_request = org_repo.repo_name
110 c.default_revs = self._get_repo_refs(org_repo.scm_instance)
110 c.default_revs = self._get_repo_refs(org_repo.scm_instance)
111 #add orginal repo
111 #add orginal repo
112 other_repos_info[org_repo.repo_name] = {
112 other_repos_info[org_repo.repo_name] = {
113 'gravatar': h.gravatar_url(org_repo.user.email, 24),
113 'gravatar': h.gravatar_url(org_repo.user.email, 24),
114 'description': org_repo.description,
114 'description': org_repo.description,
115 'revs': h.select('other_ref', '', c.default_revs, class_='refs')
115 'revs': h.select('other_ref', '', c.default_revs, class_='refs')
116 }
116 }
117
117
118 #gather forks and add to this list
118 #gather forks and add to this list
119 for fork in org_repo.forks:
119 for fork in org_repo.forks:
120 c.other_repos.append((fork.repo_name, '%s/%s' % (
120 c.other_repos.append((fork.repo_name, '%s/%s' % (
121 fork.user.username, fork.repo_name))
121 fork.user.username, fork.repo_name))
122 )
122 )
123 other_repos_info[fork.repo_name] = {
123 other_repos_info[fork.repo_name] = {
124 'gravatar': h.gravatar_url(fork.user.email, 24),
124 'gravatar': h.gravatar_url(fork.user.email, 24),
125 'description': fork.description,
125 'description': fork.description,
126 'revs': h.select('other_ref', '',
126 'revs': h.select('other_ref', '',
127 self._get_repo_refs(fork.scm_instance),
127 self._get_repo_refs(fork.scm_instance),
128 class_='refs')
128 class_='refs')
129 }
129 }
130 #add parents of this fork also
130 #add parents of this fork also
131 if org_repo.parent:
131 if org_repo.parent:
132 c.default_pull_request = org_repo.parent.repo_name
132 c.default_pull_request = org_repo.parent.repo_name
133 c.other_repos.append((org_repo.parent.repo_name, '%s/%s' % (
133 c.other_repos.append((org_repo.parent.repo_name, '%s/%s' % (
134 org_repo.parent.user.username,
134 org_repo.parent.user.username,
135 org_repo.parent.repo_name))
135 org_repo.parent.repo_name))
136 )
136 )
137 other_repos_info[org_repo.parent.repo_name] = {
137 other_repos_info[org_repo.parent.repo_name] = {
138 'gravatar': h.gravatar_url(org_repo.parent.user.email, 24),
138 'gravatar': h.gravatar_url(org_repo.parent.user.email, 24),
139 'description': org_repo.parent.description,
139 'description': org_repo.parent.description,
140 'revs': h.select('other_ref', '',
140 'revs': h.select('other_ref', '',
141 self._get_repo_refs(org_repo.parent.scm_instance),
141 self._get_repo_refs(org_repo.parent.scm_instance),
142 class_='refs')
142 class_='refs')
143 }
143 }
144
144
145 c.other_repos_info = json.dumps(other_repos_info)
145 c.other_repos_info = json.dumps(other_repos_info)
146 c.review_members = [org_repo.user]
146 c.review_members = [org_repo.user]
147 return render('/pullrequests/pullrequest.html')
147 return render('/pullrequests/pullrequest.html')
148
148
149 @NotAnonymous()
149 @NotAnonymous()
150 def create(self, repo_name):
150 def create(self, repo_name):
151 try:
151 try:
152 _form = PullRequestForm()().to_python(request.POST)
152 _form = PullRequestForm()().to_python(request.POST)
153 except formencode.Invalid, errors:
153 except formencode.Invalid, errors:
154 log.error(traceback.format_exc())
154 log.error(traceback.format_exc())
155 if errors.error_dict.get('revisions'):
155 if errors.error_dict.get('revisions'):
156 msg = 'Revisions: %s' % errors.error_dict['revisions']
156 msg = 'Revisions: %s' % errors.error_dict['revisions']
157 elif errors.error_dict.get('pullrequest_title'):
157 elif errors.error_dict.get('pullrequest_title'):
158 msg = _('Pull request requires a title with min. 3 chars')
158 msg = _('Pull request requires a title with min. 3 chars')
159 else:
159 else:
160 msg = _('error during creation of pull request')
160 msg = _('error during creation of pull request')
161
161
162 h.flash(msg, 'error')
162 h.flash(msg, 'error')
163 return redirect(url('pullrequest_home', repo_name=repo_name))
163 return redirect(url('pullrequest_home', repo_name=repo_name))
164
164
165 org_repo = _form['org_repo']
165 org_repo = _form['org_repo']
166 org_ref = _form['org_ref']
166 org_ref = _form['org_ref']
167 other_repo = _form['other_repo']
167 other_repo = _form['other_repo']
168 other_ref = _form['other_ref']
168 other_ref = _form['other_ref']
169 revisions = _form['revisions']
169 revisions = _form['revisions']
170 reviewers = _form['review_members']
170 reviewers = _form['review_members']
171
171
172 title = _form['pullrequest_title']
172 title = _form['pullrequest_title']
173 description = _form['pullrequest_desc']
173 description = _form['pullrequest_desc']
174
174
175 try:
175 try:
176 pull_request = PullRequestModel().create(
176 pull_request = PullRequestModel().create(
177 self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
177 self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
178 other_ref, revisions, reviewers, title, description
178 other_ref, revisions, reviewers, title, description
179 )
179 )
180 Session().commit()
180 Session().commit()
181 h.flash(_('Successfully opened new pull request'),
181 h.flash(_('Successfully opened new pull request'),
182 category='success')
182 category='success')
183 except Exception:
183 except Exception:
184 h.flash(_('Error occurred during sending pull request'),
184 h.flash(_('Error occurred during sending pull request'),
185 category='error')
185 category='error')
186 log.error(traceback.format_exc())
186 log.error(traceback.format_exc())
187 return redirect(url('pullrequest_home', repo_name=repo_name))
187 return redirect(url('pullrequest_home', repo_name=repo_name))
188
188
189 return redirect(url('pullrequest_show', repo_name=other_repo,
189 return redirect(url('pullrequest_show', repo_name=other_repo,
190 pull_request_id=pull_request.pull_request_id))
190 pull_request_id=pull_request.pull_request_id))
191
191
192 @NotAnonymous()
192 @NotAnonymous()
193 @jsonify
193 @jsonify
194 def update(self, repo_name, pull_request_id):
194 def update(self, repo_name, pull_request_id):
195 pull_request = PullRequest.get_or_404(pull_request_id)
195 pull_request = PullRequest.get_or_404(pull_request_id)
196 if pull_request.is_closed():
196 if pull_request.is_closed():
197 raise HTTPForbidden()
197 raise HTTPForbidden()
198 #only owner or admin can update it
198 #only owner or admin can update it
199 owner = pull_request.author.user_id == c.rhodecode_user.user_id
199 owner = pull_request.author.user_id == c.rhodecode_user.user_id
200 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
200 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
201 reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
201 reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
202 request.POST.get('reviewers_ids', '').split(',')))
202 request.POST.get('reviewers_ids', '').split(',')))
203
203
204 PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
204 PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
205 Session.commit()
205 Session.commit()
206 return True
206 return True
207 raise HTTPForbidden()
207 raise HTTPForbidden()
208
208
209 @NotAnonymous()
209 @NotAnonymous()
210 @jsonify
210 @jsonify
211 def delete(self, repo_name, pull_request_id):
211 def delete(self, repo_name, pull_request_id):
212 pull_request = PullRequest.get_or_404(pull_request_id)
212 pull_request = PullRequest.get_or_404(pull_request_id)
213 #only owner can delete it !
213 #only owner can delete it !
214 if pull_request.author.user_id == c.rhodecode_user.user_id:
214 if pull_request.author.user_id == c.rhodecode_user.user_id:
215 PullRequestModel().delete(pull_request)
215 PullRequestModel().delete(pull_request)
216 Session().commit()
216 Session().commit()
217 h.flash(_('Successfully deleted pull request'),
217 h.flash(_('Successfully deleted pull request'),
218 category='success')
218 category='success')
219 return redirect(url('admin_settings_my_account'))
219 return redirect(url('admin_settings_my_account'))
220 raise HTTPForbidden()
220 raise HTTPForbidden()
221
221
222 def _load_compare_data(self, pull_request, enable_comments=True):
222 def _load_compare_data(self, pull_request, enable_comments=True):
223 """
223 """
224 Load context data needed for generating compare diff
224 Load context data needed for generating compare diff
225
225
226 :param pull_request:
226 :param pull_request:
227 :type pull_request:
227 :type pull_request:
228 """
228 """
229
229
230 org_repo = pull_request.org_repo
230 org_repo = pull_request.org_repo
231 (org_ref_type,
231 (org_ref_type,
232 org_ref_name,
232 org_ref_name,
233 org_ref_rev) = pull_request.org_ref.split(':')
233 org_ref_rev) = pull_request.org_ref.split(':')
234
234
235 other_repo = pull_request.other_repo
235 other_repo = pull_request.other_repo
236 (other_ref_type,
236 (other_ref_type,
237 other_ref_name,
237 other_ref_name,
238 other_ref_rev) = pull_request.other_ref.split(':')
238 other_ref_rev) = pull_request.other_ref.split(':')
239
239
240 # despite opening revisions for bookmarks/branches/tags, we always
240 # despite opening revisions for bookmarks/branches/tags, we always
241 # convert this to rev to prevent changes after book or branch change
241 # convert this to rev to prevent changes after book or branch change
242 org_ref = ('rev', org_ref_rev)
242 org_ref = ('rev', org_ref_rev)
243 other_ref = ('rev', other_ref_rev)
243 other_ref = ('rev', other_ref_rev)
244
244
245 c.org_repo = org_repo
245 c.org_repo = org_repo
246 c.other_repo = other_repo
246 c.other_repo = other_repo
247
247
248 c.cs_ranges, discovery_data = PullRequestModel().get_compare_data(
248 c.cs_ranges, discovery_data = PullRequestModel().get_compare_data(
249 org_repo, org_ref, other_repo, other_ref
249 org_repo, org_ref, other_repo, other_ref
250 )
250 )
251
251
252 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
252 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
253 c.cs_ranges])
253 c.cs_ranges])
254 # defines that we need hidden inputs with changesets
254 # defines that we need hidden inputs with changesets
255 c.as_form = request.GET.get('as_form', False)
255 c.as_form = request.GET.get('as_form', False)
256
256
257 c.org_ref = org_ref[1]
257 c.org_ref = org_ref[1]
258 c.other_ref = other_ref[1]
258 c.other_ref = other_ref[1]
259 # diff needs to have swapped org with other to generate proper diff
259 # diff needs to have swapped org with other to generate proper diff
260 _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref,
260 _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref,
261 discovery_data)
261 discovery_data)
262 diff_processor = diffs.DiffProcessor(_diff, format='gitdiff')
262 diff_processor = diffs.DiffProcessor(_diff, format='gitdiff')
263 _parsed = diff_processor.prepare()
263 _parsed = diff_processor.prepare()
264
264
265 c.files = []
265 c.files = []
266 c.changes = {}
266 c.changes = {}
267
267
268 for f in _parsed:
268 for f in _parsed:
269 fid = h.FID('', f['filename'])
269 fid = h.FID('', f['filename'])
270 c.files.append([fid, f['operation'], f['filename'], f['stats']])
270 c.files.append([fid, f['operation'], f['filename'], f['stats']])
271 diff = diff_processor.as_html(enable_comments=enable_comments,
271 diff = diff_processor.as_html(enable_comments=enable_comments,
272 diff_lines=[f])
272 diff_lines=[f])
273 c.changes[fid] = [f['operation'], f['filename'], diff]
273 c.changes[fid] = [f['operation'], f['filename'], diff]
274
274
275 def show(self, repo_name, pull_request_id):
275 def show(self, repo_name, pull_request_id):
276 repo_model = RepoModel()
276 repo_model = RepoModel()
277 c.users_array = repo_model.get_users_js()
277 c.users_array = repo_model.get_users_js()
278 c.users_groups_array = repo_model.get_users_groups_js()
278 c.users_groups_array = repo_model.get_users_groups_js()
279 c.pull_request = PullRequest.get_or_404(pull_request_id)
279 c.pull_request = PullRequest.get_or_404(pull_request_id)
280
280
281 cc_model = ChangesetCommentsModel()
281 cc_model = ChangesetCommentsModel()
282 cs_model = ChangesetStatusModel()
282 cs_model = ChangesetStatusModel()
283 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
283 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
284 pull_request=c.pull_request,
284 pull_request=c.pull_request,
285 with_revisions=True)
285 with_revisions=True)
286
286
287 cs_statuses = defaultdict(list)
287 cs_statuses = defaultdict(list)
288 for st in _cs_statuses:
288 for st in _cs_statuses:
289 cs_statuses[st.author.username] += [st]
289 cs_statuses[st.author.username] += [st]
290
290
291 c.pull_request_reviewers = []
291 c.pull_request_reviewers = []
292 c.pull_request_pending_reviewers = []
292 c.pull_request_pending_reviewers = []
293 for o in c.pull_request.reviewers:
293 for o in c.pull_request.reviewers:
294 st = cs_statuses.get(o.user.username, None)
294 st = cs_statuses.get(o.user.username, None)
295 if st:
295 if st:
296 sorter = lambda k: k.version
296 sorter = lambda k: k.version
297 st = [(x, list(y)[0])
297 st = [(x, list(y)[0])
298 for x, y in (groupby(sorted(st, key=sorter), sorter))]
298 for x, y in (groupby(sorted(st, key=sorter), sorter))]
299 else:
299 else:
300 c.pull_request_pending_reviewers.append(o.user)
300 c.pull_request_pending_reviewers.append(o.user)
301 c.pull_request_reviewers.append([o.user, st])
301 c.pull_request_reviewers.append([o.user, st])
302
302
303 # pull_requests repo_name we opened it against
303 # pull_requests repo_name we opened it against
304 # ie. other_repo must match
304 # ie. other_repo must match
305 if repo_name != c.pull_request.other_repo.repo_name:
305 if repo_name != c.pull_request.other_repo.repo_name:
306 raise HTTPNotFound
306 raise HTTPNotFound
307
307
308 # load compare data into template context
308 # load compare data into template context
309 enable_comments = not c.pull_request.is_closed()
309 enable_comments = not c.pull_request.is_closed()
310 self._load_compare_data(c.pull_request, enable_comments=enable_comments)
310 self._load_compare_data(c.pull_request, enable_comments=enable_comments)
311
311
312 # inline comments
312 # inline comments
313 c.inline_cnt = 0
313 c.inline_cnt = 0
314 c.inline_comments = cc_model.get_inline_comments(
314 c.inline_comments = cc_model.get_inline_comments(
315 c.rhodecode_db_repo.repo_id,
315 c.rhodecode_db_repo.repo_id,
316 pull_request=pull_request_id)
316 pull_request=pull_request_id)
317 # count inline comments
317 # count inline comments
318 for __, lines in c.inline_comments:
318 for __, lines in c.inline_comments:
319 for comments in lines.values():
319 for comments in lines.values():
320 c.inline_cnt += len(comments)
320 c.inline_cnt += len(comments)
321 # comments
321 # comments
322 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
322 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
323 pull_request=pull_request_id)
323 pull_request=pull_request_id)
324
324
325 # changeset(pull-request) status
325 # changeset(pull-request) status
326 c.current_changeset_status = cs_model.calculate_status(
326 c.current_changeset_status = cs_model.calculate_status(
327 c.pull_request_reviewers
327 c.pull_request_reviewers
328 )
328 )
329 c.changeset_statuses = ChangesetStatus.STATUSES
329 c.changeset_statuses = ChangesetStatus.STATUSES
330 c.target_repo = c.pull_request.org_repo.repo_name
330 c.target_repo = c.pull_request.org_repo.repo_name
331 return render('/pullrequests/pullrequest_show.html')
331 return render('/pullrequests/pullrequest_show.html')
332
332
333 @NotAnonymous()
333 @NotAnonymous()
334 @jsonify
334 @jsonify
335 def comment(self, repo_name, pull_request_id):
335 def comment(self, repo_name, pull_request_id):
336 pull_request = PullRequest.get_or_404(pull_request_id)
336 pull_request = PullRequest.get_or_404(pull_request_id)
337 if pull_request.is_closed():
337 if pull_request.is_closed():
338 raise HTTPForbidden()
338 raise HTTPForbidden()
339
339
340 status = request.POST.get('changeset_status')
340 status = request.POST.get('changeset_status')
341 change_status = request.POST.get('change_changeset_status')
341 change_status = request.POST.get('change_changeset_status')
342
342 text = request.POST.get('text')
343 if status and change_status:
344 text = text or (_('Status change -> %s')
345 % ChangesetStatus.get_status_lbl(status))
343 comm = ChangesetCommentsModel().create(
346 comm = ChangesetCommentsModel().create(
344 text=request.POST.get('text'),
347 text=text,
345 repo=c.rhodecode_db_repo.repo_id,
348 repo=c.rhodecode_db_repo.repo_id,
346 user=c.rhodecode_user.user_id,
349 user=c.rhodecode_user.user_id,
347 pull_request=pull_request_id,
350 pull_request=pull_request_id,
348 f_path=request.POST.get('f_path'),
351 f_path=request.POST.get('f_path'),
349 line_no=request.POST.get('line'),
352 line_no=request.POST.get('line'),
350 status_change=(ChangesetStatus.get_status_lbl(status)
353 status_change=(ChangesetStatus.get_status_lbl(status)
351 if status and change_status else None)
354 if status and change_status else None)
352 )
355 )
353
356
354 # get status if set !
357 # get status if set !
355 if status and change_status:
358 if status and change_status:
356 ChangesetStatusModel().set_status(
359 ChangesetStatusModel().set_status(
357 c.rhodecode_db_repo.repo_id,
360 c.rhodecode_db_repo.repo_id,
358 status,
361 status,
359 c.rhodecode_user.user_id,
362 c.rhodecode_user.user_id,
360 comm,
363 comm,
361 pull_request=pull_request_id
364 pull_request=pull_request_id
362 )
365 )
363 action_logger(self.rhodecode_user,
366 action_logger(self.rhodecode_user,
364 'user_commented_pull_request:%s' % pull_request_id,
367 'user_commented_pull_request:%s' % pull_request_id,
365 c.rhodecode_db_repo, self.ip_addr, self.sa)
368 c.rhodecode_db_repo, self.ip_addr, self.sa)
366
369
367 if request.POST.get('save_close'):
370 if request.POST.get('save_close'):
368 PullRequestModel().close_pull_request(pull_request_id)
371 PullRequestModel().close_pull_request(pull_request_id)
369 action_logger(self.rhodecode_user,
372 action_logger(self.rhodecode_user,
370 'user_closed_pull_request:%s' % pull_request_id,
373 'user_closed_pull_request:%s' % pull_request_id,
371 c.rhodecode_db_repo, self.ip_addr, self.sa)
374 c.rhodecode_db_repo, self.ip_addr, self.sa)
372
375
373 Session().commit()
376 Session().commit()
374
377
375 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
378 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
376 return redirect(h.url('pullrequest_show', repo_name=repo_name,
379 return redirect(h.url('pullrequest_show', repo_name=repo_name,
377 pull_request_id=pull_request_id))
380 pull_request_id=pull_request_id))
378
381
379 data = {
382 data = {
380 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
383 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
381 }
384 }
382 if comm:
385 if comm:
383 c.co = comm
386 c.co = comm
384 data.update(comm.get_dict())
387 data.update(comm.get_dict())
385 data.update({'rendered_text':
388 data.update({'rendered_text':
386 render('changeset/changeset_comment_block.html')})
389 render('changeset/changeset_comment_block.html')})
387
390
388 return data
391 return data
389
392
390 @NotAnonymous()
393 @NotAnonymous()
391 @jsonify
394 @jsonify
392 def delete_comment(self, repo_name, comment_id):
395 def delete_comment(self, repo_name, comment_id):
393 co = ChangesetComment.get(comment_id)
396 co = ChangesetComment.get(comment_id)
394 if co.pull_request.is_closed():
397 if co.pull_request.is_closed():
395 #don't allow deleting comments on closed pull request
398 #don't allow deleting comments on closed pull request
396 raise HTTPForbidden()
399 raise HTTPForbidden()
397
400
398 owner = lambda: co.author.user_id == c.rhodecode_user.user_id
401 owner = lambda: co.author.user_id == c.rhodecode_user.user_id
399 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
402 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
400 ChangesetCommentsModel().delete(comment=co)
403 ChangesetCommentsModel().delete(comment=co)
401 Session().commit()
404 Session().commit()
402 return True
405 return True
403 else:
406 else:
404 raise HTTPForbidden()
407 raise HTTPForbidden()
General Comments 0
You need to be logged in to leave comments. Login now