Merge lp:~jelmer/brz/suggest-commit-message into lp:brz
- suggest-commit-message
- Merge into trunk
Proposed by
Jelmer Vernooij
Status: | Merged |
---|---|
Approved by: | Jelmer Vernooij |
Approved revision: | no longer in the source branch. |
Merge reported by: | The Breezy Bot |
Merged at revision: | not available |
Proposed branch: | lp:~jelmer/brz/suggest-commit-message |
Merge into: | lp:brz |
Diff against target: |
295 lines (+53/-19) 6 files modified
breezy/plugins/propose/cmds.py (+6/-2) breezy/plugins/propose/github.py (+7/-1) breezy/plugins/propose/gitlabs.py (+7/-1) breezy/plugins/propose/launchpad.py (+15/-14) breezy/plugins/propose/propose.py (+14/-1) doc/en/release-notes/brz-3.1.txt (+4/-0) |
To merge this branch: | bzr merge lp:~jelmer/brz/suggest-commit-message |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jelmer Vernooij | Approve | ||
Review via email: mp+366610@code.launchpad.net |
Commit message
Add commit-message option to 'brz propose'.h
Description of the change
Add commit-message option to 'brz propose'.
To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'breezy/plugins/propose/cmds.py' | |||
2 | --- breezy/plugins/propose/cmds.py 2019-01-24 00:56:15 +0000 | |||
3 | +++ breezy/plugins/propose/cmds.py 2019-04-27 16:04:53 +0000 | |||
4 | @@ -143,6 +143,9 @@ | |||
5 | 143 | Option('name', help='Name of the new remote branch.', type=str), | 143 | Option('name', help='Name of the new remote branch.', type=str), |
6 | 144 | Option('description', help='Description of the change.', type=str), | 144 | Option('description', help='Description of the change.', type=str), |
7 | 145 | Option('prerequisite', help='Prerequisite branch.', type=str), | 145 | Option('prerequisite', help='Prerequisite branch.', type=str), |
8 | 146 | Option( | ||
9 | 147 | 'commit-message', | ||
10 | 148 | help='Set commit message for merge, if supported', type=str), | ||
11 | 146 | ListOption('labels', short_name='l', type=text_type, | 149 | ListOption('labels', short_name='l', type=text_type, |
12 | 147 | help='Labels to apply.'), | 150 | help='Labels to apply.'), |
13 | 148 | Option('no-allow-lossy', | 151 | Option('no-allow-lossy', |
14 | @@ -154,7 +157,7 @@ | |||
15 | 154 | 157 | ||
16 | 155 | def run(self, submit_branch=None, directory='.', hoster=None, | 158 | def run(self, submit_branch=None, directory='.', hoster=None, |
17 | 156 | reviewers=None, name=None, no_allow_lossy=False, description=None, | 159 | reviewers=None, name=None, no_allow_lossy=False, description=None, |
19 | 157 | labels=None, prerequisite=None): | 160 | labels=None, prerequisite=None, commit_message=None): |
20 | 158 | tree, branch, relpath = ( | 161 | tree, branch, relpath = ( |
21 | 159 | controldir.ControlDir.open_containing_tree_or_branch(directory)) | 162 | controldir.ControlDir.open_containing_tree_or_branch(directory)) |
22 | 160 | if submit_branch is None: | 163 | if submit_branch is None: |
23 | @@ -191,7 +194,8 @@ | |||
24 | 191 | try: | 194 | try: |
25 | 192 | proposal = proposal_builder.create_proposal( | 195 | proposal = proposal_builder.create_proposal( |
26 | 193 | description=description, reviewers=reviewers, | 196 | description=description, reviewers=reviewers, |
28 | 194 | prerequisite_branch=prerequisite_branch, labels=labels) | 197 | prerequisite_branch=prerequisite_branch, labels=labels, |
29 | 198 | commit_message=commit_message) | ||
30 | 195 | except _mod_propose.MergeProposalExists as e: | 199 | except _mod_propose.MergeProposalExists as e: |
31 | 196 | raise errors.BzrCommandError(gettext( | 200 | raise errors.BzrCommandError(gettext( |
32 | 197 | 'There is already a branch merge proposal: %s') % e.url) | 201 | 'There is already a branch merge proposal: %s') % e.url) |
33 | 198 | 202 | ||
34 | === modified file 'breezy/plugins/propose/github.py' | |||
35 | --- breezy/plugins/propose/github.py 2019-02-14 19:33:19 +0000 | |||
36 | +++ breezy/plugins/propose/github.py 2019-04-27 16:04:53 +0000 | |||
37 | @@ -27,6 +27,7 @@ | |||
38 | 27 | MergeProposalBuilder, | 27 | MergeProposalBuilder, |
39 | 28 | MergeProposalExists, | 28 | MergeProposalExists, |
40 | 29 | PrerequisiteBranchUnsupported, | 29 | PrerequisiteBranchUnsupported, |
41 | 30 | CommitMessageUnsupported, | ||
42 | 30 | UnsupportedHoster, | 31 | UnsupportedHoster, |
43 | 31 | ) | 32 | ) |
44 | 32 | 33 | ||
45 | @@ -122,6 +123,9 @@ | |||
46 | 122 | def get_description(self): | 123 | def get_description(self): |
47 | 123 | return self._pr.body | 124 | return self._pr.body |
48 | 124 | 125 | ||
49 | 126 | def get_commit_message(self): | ||
50 | 127 | return None | ||
51 | 128 | |||
52 | 125 | def set_description(self, description): | 129 | def set_description(self, description): |
53 | 126 | self._pr.edit(body=description, title=determine_title(description)) | 130 | self._pr.edit(body=description, title=determine_title(description)) |
54 | 127 | 131 | ||
55 | @@ -173,6 +177,7 @@ | |||
56 | 173 | name = 'github' | 177 | name = 'github' |
57 | 174 | 178 | ||
58 | 175 | supports_merge_proposal_labels = True | 179 | supports_merge_proposal_labels = True |
59 | 180 | supports_merge_proposal_commit_message = False | ||
60 | 176 | 181 | ||
61 | 177 | def __repr__(self): | 182 | def __repr__(self): |
62 | 178 | return "GitHub()" | 183 | return "GitHub()" |
63 | @@ -346,10 +351,11 @@ | |||
64 | 346 | return None | 351 | return None |
65 | 347 | 352 | ||
66 | 348 | def create_proposal(self, description, reviewers=None, labels=None, | 353 | def create_proposal(self, description, reviewers=None, labels=None, |
68 | 349 | prerequisite_branch=None): | 354 | prerequisite_branch=None, commit_message=None): |
69 | 350 | """Perform the submission.""" | 355 | """Perform the submission.""" |
70 | 351 | if prerequisite_branch is not None: | 356 | if prerequisite_branch is not None: |
71 | 352 | raise PrerequisiteBranchUnsupported(self) | 357 | raise PrerequisiteBranchUnsupported(self) |
72 | 358 | # Note that commit_message is ignored, since github doesn't support it. | ||
73 | 353 | import github | 359 | import github |
74 | 354 | # TODO(jelmer): Probe for right repo name | 360 | # TODO(jelmer): Probe for right repo name |
75 | 355 | if self.target_repo_name.endswith('.git'): | 361 | if self.target_repo_name.endswith('.git'): |
76 | 356 | 362 | ||
77 | === modified file 'breezy/plugins/propose/gitlabs.py' | |||
78 | --- breezy/plugins/propose/gitlabs.py 2019-02-14 19:33:19 +0000 | |||
79 | +++ breezy/plugins/propose/gitlabs.py 2019-04-27 16:04:53 +0000 | |||
80 | @@ -145,6 +145,9 @@ | |||
81 | 145 | self._mr.description = description | 145 | self._mr.description = description |
82 | 146 | self._mr.save() | 146 | self._mr.save() |
83 | 147 | 147 | ||
84 | 148 | def get_commit_message(self): | ||
85 | 149 | return None | ||
86 | 150 | |||
87 | 148 | def _branch_url_from_project(self, project_id, branch_name): | 151 | def _branch_url_from_project(self, project_id, branch_name): |
88 | 149 | project = self._mr.manager.gitlab.projects.get(project_id) | 152 | project = self._mr.manager.gitlab.projects.get(project_id) |
89 | 150 | return gitlab_url_to_bzr_url(project.http_url_to_repo, branch_name) | 153 | return gitlab_url_to_bzr_url(project.http_url_to_repo, branch_name) |
90 | @@ -176,6 +179,7 @@ | |||
91 | 176 | """GitLab hoster implementation.""" | 179 | """GitLab hoster implementation.""" |
92 | 177 | 180 | ||
93 | 178 | supports_merge_proposal_labels = True | 181 | supports_merge_proposal_labels = True |
94 | 182 | supports_merge_proposal_commit_message = False | ||
95 | 179 | 183 | ||
96 | 180 | def __repr__(self): | 184 | def __repr__(self): |
97 | 181 | return "<GitLab(%r)>" % self.gl.url | 185 | return "<GitLab(%r)>" % self.gl.url |
98 | @@ -360,11 +364,13 @@ | |||
99 | 360 | return None | 364 | return None |
100 | 361 | 365 | ||
101 | 362 | def create_proposal(self, description, reviewers=None, labels=None, | 366 | def create_proposal(self, description, reviewers=None, labels=None, |
103 | 363 | prerequisite_branch=None): | 367 | prerequisite_branch=None, commit_message=None): |
104 | 364 | """Perform the submission.""" | 368 | """Perform the submission.""" |
105 | 369 | # https://docs.gitlab.com/ee/api/merge_requests.html#create-mr | ||
106 | 365 | if prerequisite_branch is not None: | 370 | if prerequisite_branch is not None: |
107 | 366 | raise PrerequisiteBranchUnsupported(self) | 371 | raise PrerequisiteBranchUnsupported(self) |
108 | 367 | import gitlab | 372 | import gitlab |
109 | 373 | # Note that commit_message is ignored, since Gitlab doesn't support it. | ||
110 | 368 | # TODO(jelmer): Support reviewers | 374 | # TODO(jelmer): Support reviewers |
111 | 369 | self.gl.auth() | 375 | self.gl.auth() |
112 | 370 | source_project = self.gl.projects.get(self.source_project_name) | 376 | source_project = self.gl.projects.get(self.source_project_name) |
113 | 371 | 377 | ||
114 | === modified file 'breezy/plugins/propose/launchpad.py' | |||
115 | --- breezy/plugins/propose/launchpad.py 2019-03-06 14:03:19 +0000 | |||
116 | +++ breezy/plugins/propose/launchpad.py 2019-04-27 16:04:53 +0000 | |||
117 | @@ -142,6 +142,13 @@ | |||
118 | 142 | self._mp.description = description | 142 | self._mp.description = description |
119 | 143 | self._mp.lp_save() | 143 | self._mp.lp_save() |
120 | 144 | 144 | ||
121 | 145 | def get_commit_message(self): | ||
122 | 146 | return self._mp.commit_message | ||
123 | 147 | |||
124 | 148 | def set_commit_message(self, commit_message): | ||
125 | 149 | self._mp.commit_message = commit_message | ||
126 | 150 | self._mp.lp_save() | ||
127 | 151 | |||
128 | 145 | def close(self): | 152 | def close(self): |
129 | 146 | self._mp.setStatus(status='Rejected') | 153 | self._mp.setStatus(status='Rejected') |
130 | 147 | 154 | ||
131 | @@ -154,6 +161,8 @@ | |||
132 | 154 | # https://bugs.launchpad.net/launchpad/+bug/397676 | 161 | # https://bugs.launchpad.net/launchpad/+bug/397676 |
133 | 155 | supports_merge_proposal_labels = False | 162 | supports_merge_proposal_labels = False |
134 | 156 | 163 | ||
135 | 164 | supports_merge_proposal_commit_message = True | ||
136 | 165 | |||
137 | 157 | def __init__(self, staging=False): | 166 | def __init__(self, staging=False): |
138 | 158 | self._staging = staging | 167 | self._staging = staging |
139 | 159 | if staging: | 168 | if staging: |
140 | @@ -394,13 +403,12 @@ | |||
141 | 394 | 403 | ||
142 | 395 | class LaunchpadBazaarMergeProposalBuilder(MergeProposalBuilder): | 404 | class LaunchpadBazaarMergeProposalBuilder(MergeProposalBuilder): |
143 | 396 | 405 | ||
145 | 397 | def __init__(self, lp_host, source_branch, target_branch, message=None, | 406 | def __init__(self, lp_host, source_branch, target_branch, |
146 | 398 | staging=None, approve=None, fixes=None): | 407 | staging=None, approve=None, fixes=None): |
147 | 399 | """Constructor. | 408 | """Constructor. |
148 | 400 | 409 | ||
149 | 401 | :param source_branch: The branch to propose for merging. | 410 | :param source_branch: The branch to propose for merging. |
150 | 402 | :param target_branch: The branch to merge into. | 411 | :param target_branch: The branch to merge into. |
151 | 403 | :param message: The commit message to use. (May be None.) | ||
152 | 404 | :param staging: If True, propose the merge against staging instead of | 412 | :param staging: If True, propose the merge against staging instead of |
153 | 405 | production. | 413 | production. |
154 | 406 | :param approve: If True, mark the new proposal as approved immediately. | 414 | :param approve: If True, mark the new proposal as approved immediately. |
155 | @@ -421,14 +429,11 @@ | |||
156 | 421 | self.target_branch = target_branch | 429 | self.target_branch = target_branch |
157 | 422 | self.target_branch_lp = self.launchpad.branches.getByUrl( | 430 | self.target_branch_lp = self.launchpad.branches.getByUrl( |
158 | 423 | url=target_branch.user_url) | 431 | url=target_branch.user_url) |
159 | 424 | self.commit_message = message | ||
160 | 425 | self.approve = approve | 432 | self.approve = approve |
161 | 426 | self.fixes = fixes | 433 | self.fixes = fixes |
162 | 427 | 434 | ||
163 | 428 | def get_infotext(self): | 435 | def get_infotext(self): |
164 | 429 | """Determine the initial comment for the merge proposal.""" | 436 | """Determine the initial comment for the merge proposal.""" |
165 | 430 | if self.commit_message is not None: | ||
166 | 431 | return self.commit_message.strip().encode('utf-8') | ||
167 | 432 | info = ["Source: %s\n" % self.source_branch_lp.bzr_identity] | 437 | info = ["Source: %s\n" % self.source_branch_lp.bzr_identity] |
168 | 433 | info.append("Target: %s\n" % self.target_branch_lp.bzr_identity) | 438 | info.append("Target: %s\n" % self.target_branch_lp.bzr_identity) |
169 | 434 | return ''.join(info) | 439 | return ''.join(info) |
170 | @@ -480,7 +485,7 @@ | |||
171 | 480 | revid=self.source_branch.last_revision()) | 485 | revid=self.source_branch.last_revision()) |
172 | 481 | 486 | ||
173 | 482 | def create_proposal(self, description, reviewers=None, labels=None, | 487 | def create_proposal(self, description, reviewers=None, labels=None, |
175 | 483 | prerequisite_branch=None): | 488 | prerequisite_branch=None, commit_message=None): |
176 | 484 | """Perform the submission.""" | 489 | """Perform the submission.""" |
177 | 485 | if labels: | 490 | if labels: |
178 | 486 | raise LabelsUnsupported() | 491 | raise LabelsUnsupported() |
179 | @@ -497,7 +502,7 @@ | |||
180 | 497 | target_branch=self.target_branch_lp, | 502 | target_branch=self.target_branch_lp, |
181 | 498 | prerequisite_branch=prereq, | 503 | prerequisite_branch=prereq, |
182 | 499 | initial_comment=description.strip(), | 504 | initial_comment=description.strip(), |
184 | 500 | commit_message=self.commit_message, | 505 | commit_message=commit_message, |
185 | 501 | reviewers=[self.launchpad.people[reviewer].self_link | 506 | reviewers=[self.launchpad.people[reviewer].self_link |
186 | 502 | for reviewer in reviewers], | 507 | for reviewer in reviewers], |
187 | 503 | review_types=[None for reviewer in reviewers]) | 508 | review_types=[None for reviewer in reviewers]) |
188 | @@ -521,13 +526,12 @@ | |||
189 | 521 | 526 | ||
190 | 522 | class LaunchpadGitMergeProposalBuilder(MergeProposalBuilder): | 527 | class LaunchpadGitMergeProposalBuilder(MergeProposalBuilder): |
191 | 523 | 528 | ||
193 | 524 | def __init__(self, lp_host, source_branch, target_branch, message=None, | 529 | def __init__(self, lp_host, source_branch, target_branch, |
194 | 525 | staging=None, approve=None, fixes=None): | 530 | staging=None, approve=None, fixes=None): |
195 | 526 | """Constructor. | 531 | """Constructor. |
196 | 527 | 532 | ||
197 | 528 | :param source_branch: The branch to propose for merging. | 533 | :param source_branch: The branch to propose for merging. |
198 | 529 | :param target_branch: The branch to merge into. | 534 | :param target_branch: The branch to merge into. |
199 | 530 | :param message: The commit message to use. (May be None.) | ||
200 | 531 | :param staging: If True, propose the merge against staging instead of | 535 | :param staging: If True, propose the merge against staging instead of |
201 | 532 | production. | 536 | production. |
202 | 533 | :param approve: If True, mark the new proposal as approved immediately. | 537 | :param approve: If True, mark the new proposal as approved immediately. |
203 | @@ -549,14 +553,11 @@ | |||
204 | 549 | self.target_branch = target_branch | 553 | self.target_branch = target_branch |
205 | 550 | (self.target_repo_lp, self.target_branch_lp) = ( | 554 | (self.target_repo_lp, self.target_branch_lp) = ( |
206 | 551 | self.lp_host._get_lp_git_ref_from_branch(target_branch)) | 555 | self.lp_host._get_lp_git_ref_from_branch(target_branch)) |
207 | 552 | self.commit_message = message | ||
208 | 553 | self.approve = approve | 556 | self.approve = approve |
209 | 554 | self.fixes = fixes | 557 | self.fixes = fixes |
210 | 555 | 558 | ||
211 | 556 | def get_infotext(self): | 559 | def get_infotext(self): |
212 | 557 | """Determine the initial comment for the merge proposal.""" | 560 | """Determine the initial comment for the merge proposal.""" |
213 | 558 | if self.commit_message is not None: | ||
214 | 559 | return self.commit_message.strip().encode('utf-8') | ||
215 | 560 | info = ["Source: %s\n" % self.source_branch.user_url] | 561 | info = ["Source: %s\n" % self.source_branch.user_url] |
216 | 561 | info.append("Target: %s\n" % self.target_branch.user_url) | 562 | info.append("Target: %s\n" % self.target_branch.user_url) |
217 | 562 | return ''.join(info) | 563 | return ''.join(info) |
218 | @@ -609,7 +610,7 @@ | |||
219 | 609 | revid=self.source_branch.last_revision()) | 610 | revid=self.source_branch.last_revision()) |
220 | 610 | 611 | ||
221 | 611 | def create_proposal(self, description, reviewers=None, labels=None, | 612 | def create_proposal(self, description, reviewers=None, labels=None, |
223 | 612 | prerequisite_branch=None): | 613 | prerequisite_branch=None, commit_message=None): |
224 | 613 | """Perform the submission.""" | 614 | """Perform the submission.""" |
225 | 614 | if labels: | 615 | if labels: |
226 | 615 | raise LabelsUnsupported() | 616 | raise LabelsUnsupported() |
227 | @@ -626,7 +627,7 @@ | |||
228 | 626 | merge_target=self.target_branch_lp, | 627 | merge_target=self.target_branch_lp, |
229 | 627 | merge_prerequisite=prereq_branch_lp, | 628 | merge_prerequisite=prereq_branch_lp, |
230 | 628 | initial_comment=description.strip(), | 629 | initial_comment=description.strip(), |
232 | 629 | commit_message=self.commit_message, | 630 | commit_message=commit_message, |
233 | 630 | needs_review=True, | 631 | needs_review=True, |
234 | 631 | reviewers=[self.launchpad.people[reviewer].self_link | 632 | reviewers=[self.launchpad.people[reviewer].self_link |
235 | 632 | for reviewer in reviewers], | 633 | for reviewer in reviewers], |
236 | 633 | 634 | ||
237 | === modified file 'breezy/plugins/propose/propose.py' | |||
238 | --- breezy/plugins/propose/propose.py 2019-02-15 02:29:10 +0000 | |||
239 | +++ breezy/plugins/propose/propose.py 2019-04-27 16:04:53 +0000 | |||
240 | @@ -111,6 +111,14 @@ | |||
241 | 111 | """Set the description of the merge proposal.""" | 111 | """Set the description of the merge proposal.""" |
242 | 112 | raise NotImplementedError(self.set_description) | 112 | raise NotImplementedError(self.set_description) |
243 | 113 | 113 | ||
244 | 114 | def get_commit_message(self): | ||
245 | 115 | """Get the proposed commit message.""" | ||
246 | 116 | raise NotImplementedError(self.get_commit_message) | ||
247 | 117 | |||
248 | 118 | def set_commit_message(self, commit_message): | ||
249 | 119 | """Set the propose commit message.""" | ||
250 | 120 | raise NotImplementedError(self.set_commit_message) | ||
251 | 121 | |||
252 | 114 | def get_source_branch_url(self): | 122 | def get_source_branch_url(self): |
253 | 115 | """Return the source branch.""" | 123 | """Return the source branch.""" |
254 | 116 | raise NotImplementedError(self.get_source_branch_url) | 124 | raise NotImplementedError(self.get_source_branch_url) |
255 | @@ -154,13 +162,14 @@ | |||
256 | 154 | raise NotImplementedError(self.get_infotext) | 162 | raise NotImplementedError(self.get_infotext) |
257 | 155 | 163 | ||
258 | 156 | def create_proposal(self, description, reviewers=None, labels=None, | 164 | def create_proposal(self, description, reviewers=None, labels=None, |
260 | 157 | prerequisite_branch=None): | 165 | prerequisite_branch=None, commit_message=None): |
261 | 158 | """Create a proposal to merge a branch for merging. | 166 | """Create a proposal to merge a branch for merging. |
262 | 159 | 167 | ||
263 | 160 | :param description: Description for the merge proposal | 168 | :param description: Description for the merge proposal |
264 | 161 | :param reviewers: Optional list of people to ask reviews from | 169 | :param reviewers: Optional list of people to ask reviews from |
265 | 162 | :param labels: Labels to attach to the proposal | 170 | :param labels: Labels to attach to the proposal |
266 | 163 | :param prerequisite_branch: Optional prerequisite branch | 171 | :param prerequisite_branch: Optional prerequisite branch |
267 | 172 | :param commit_message: Optional commit message | ||
268 | 164 | :return: A `MergeProposal` object | 173 | :return: A `MergeProposal` object |
269 | 165 | """ | 174 | """ |
270 | 166 | raise NotImplementedError(self.create_proposal) | 175 | raise NotImplementedError(self.create_proposal) |
271 | @@ -174,6 +183,10 @@ | |||
272 | 174 | # proposals? | 183 | # proposals? |
273 | 175 | supports_merge_proposal_labels = None | 184 | supports_merge_proposal_labels = None |
274 | 176 | 185 | ||
275 | 186 | # Does this hoster support suggesting a commit message in the | ||
276 | 187 | # merge proposal? | ||
277 | 188 | supports_merge_proposal_commit_message = None | ||
278 | 189 | |||
279 | 177 | # The base_url that would be visible to users. I.e. https://github.com/ | 190 | # The base_url that would be visible to users. I.e. https://github.com/ |
280 | 178 | # rather than https://api.github.com/ | 191 | # rather than https://api.github.com/ |
281 | 179 | base_url = None | 192 | base_url = None |
282 | 180 | 193 | ||
283 | === modified file 'doc/en/release-notes/brz-3.1.txt' | |||
284 | --- doc/en/release-notes/brz-3.1.txt 2019-03-06 14:23:50 +0000 | |||
285 | +++ doc/en/release-notes/brz-3.1.txt 2019-04-27 16:04:53 +0000 | |||
286 | @@ -27,6 +27,10 @@ | |||
287 | 27 | .. Improvements to existing commands, especially improved performance | 27 | .. Improvements to existing commands, especially improved performance |
288 | 28 | or memory usage, or better results. | 28 | or memory usage, or better results. |
289 | 29 | 29 | ||
290 | 30 | * A new ``--commit-message`` option has been added to | ||
291 | 31 | ``brz propose``, for hosting sites that support it. | ||
292 | 32 | (Jelmer Vernooij) | ||
293 | 33 | |||
294 | 30 | Bug Fixes | 34 | Bug Fixes |
295 | 31 | ********* | 35 | ********* |
296 | 32 | 36 |