Merge lp:~jelmer/brz/bundle-commitfromnews into lp:brz
- bundle-commitfromnews
- Merge into trunk
Proposed by
Jelmer Vernooij
Status: | Merged |
---|---|
Merged at revision: | 6739 |
Proposed branch: | lp:~jelmer/brz/bundle-commitfromnews |
Merge into: | lp:brz |
Diff against target: |
457 lines (+420/-0) 6 files modified
breezy/plugins/commitfromnews/__init__.py (+86/-0) breezy/plugins/commitfromnews/committemplate.py (+110/-0) breezy/plugins/commitfromnews/tests/__init__.py (+29/-0) breezy/plugins/commitfromnews/tests/test_committemplate.py (+155/-0) breezy/plugins/commitfromnews/tests/test_msgeditor.py (+36/-0) doc/en/release-notes/brz-3.0.txt (+4/-0) |
To merge this branch: | bzr merge lp:~jelmer/brz/bundle-commitfromnews |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Martin Packman | Approve | ||
Review via email: mp+327941@code.launchpad.net |
Commit message
Bundle the bzr-commitfromnews plugin.
Description of the change
Bundle the bzr-commitfromnews plugin.
I've changed the behaviour slightly so you have to set the "commit.
To use with bzr:
echo "commit.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added directory 'breezy/plugins/commitfromnews' |
2 | === added file 'breezy/plugins/commitfromnews/__init__.py' |
3 | --- breezy/plugins/commitfromnews/__init__.py 1970-01-01 00:00:00 +0000 |
4 | +++ breezy/plugins/commitfromnews/__init__.py 2017-07-23 22:07:30 +0000 |
5 | @@ -0,0 +1,86 @@ |
6 | +# Copyright (C) 2010 Canonical Ltd |
7 | +# |
8 | +# This program is free software; you can redistribute it and/or modify |
9 | +# it under the terms of the GNU General Public License as published by |
10 | +# the Free Software Foundation; either version 2 of the License, or |
11 | +# (at your option) any later version. |
12 | +# |
13 | +# This program is distributed in the hope that it will be useful, |
14 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | +# GNU General Public License for more details. |
17 | +# |
18 | +# You should have received a copy of the GNU General Public License |
19 | +# along with this program; if not, write to the Free Software |
20 | +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
21 | + |
22 | +"""bzr-commitfromnews - make commit messages from the changes in a NEWS file. |
23 | + |
24 | +commitfromnews is enabled by default when installed. |
25 | + |
26 | +To use, set the ``commit.template_from_files`` setting to a path and |
27 | +just do a commit where the NEWS file for your project has a new section |
28 | +added without providing a message to commit. |
29 | + |
30 | +E.g.:: |
31 | + $ echo "commit.template_from_files = NEWS" >> .bzr/branch/branch.conf |
32 | + $ echo "\n* new thing\n" >> NEWS |
33 | + $ bzr commit |
34 | + # editor pops open to let you tweak the message, and it starts with |
35 | + "* new thing" as the message to edit. |
36 | + |
37 | +commitfromnews attempts to create a sensible default commit message by |
38 | +including sections from a NEWS or ChangeLog file. |
39 | +""" |
40 | + |
41 | +from __future__ import absolute_import |
42 | + |
43 | +from ... import hooks |
44 | +from ...config import ( |
45 | + option_registry, |
46 | + ListOption, |
47 | + ) |
48 | + |
49 | +option_registry.register( |
50 | + ListOption('commit.template_from_files', default=[], help="""\ |
51 | +List of fnmatch(2)-style shell file patterns to use when creating commit |
52 | +templates. |
53 | +""")) |
54 | + |
55 | + |
56 | +def commit_template(commit, message): |
57 | + """Create a commit message for commit based on changes in the tree.""" |
58 | + config_stack = commit.work_tree.get_config_stack() |
59 | + filespec = config_stack.get('commit.template_from_files') |
60 | + if filespec: |
61 | + from .committemplate import CommitTemplate |
62 | + template = CommitTemplate(commit, message, filespec) |
63 | + return template.make() |
64 | + return message |
65 | + |
66 | + |
67 | +def load_tests(loader, basic_tests, pattern): |
68 | + testmod_names = [ |
69 | + 'tests', |
70 | + ] |
71 | + basic_tests.addTest(loader.loadTestsFromModuleNames( |
72 | + ["%s.%s" % (__name__, tmn) for tmn in testmod_names])) |
73 | + return basic_tests |
74 | + |
75 | + |
76 | +_registered = False |
77 | + |
78 | + |
79 | +def register(): |
80 | + """Register the plugin.""" |
81 | + global _registered |
82 | + # Does not check registered because only tests call this, and they are |
83 | + # isolated. |
84 | + _registered = True |
85 | + hooks.install_lazy_named_hook( |
86 | + 'breezy.msgeditor', 'hooks', |
87 | + 'commit_message_template', |
88 | + commit_template, 'commitfromnews template') |
89 | + |
90 | + |
91 | +register() |
92 | |
93 | === added file 'breezy/plugins/commitfromnews/committemplate.py' |
94 | --- breezy/plugins/commitfromnews/committemplate.py 1970-01-01 00:00:00 +0000 |
95 | +++ breezy/plugins/commitfromnews/committemplate.py 2017-07-23 22:07:30 +0000 |
96 | @@ -0,0 +1,110 @@ |
97 | +# Copyright (C) 2010 Canonical Ltd |
98 | +# |
99 | +# This program is free software; you can redistribute it and/or modify |
100 | +# it under the terms of the GNU General Public License as published by |
101 | +# the Free Software Foundation; either version 2 of the License, or |
102 | +# (at your option) any later version. |
103 | +# |
104 | +# This program is distributed in the hope that it will be useful, |
105 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
106 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
107 | +# GNU General Public License for more details. |
108 | +# |
109 | +# You should have received a copy of the GNU General Public License |
110 | +# along with this program; if not, write to the Free Software |
111 | +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
112 | + |
113 | +"""Logic to create commit templates.""" |
114 | + |
115 | +from __future__ import absolute_import |
116 | + |
117 | +from ... import bugtracker, osutils, patiencediff |
118 | +import re |
119 | + |
120 | +_BUG_MATCH = re.compile(r'lp:(\d+)') |
121 | + |
122 | + |
123 | +class CommitTemplate(object): |
124 | + |
125 | + def __init__(self, commit, message, file_matches): |
126 | + """Create a commit template for commit with initial message message. |
127 | + |
128 | + :param commit: A Commit object for the in progress commit. |
129 | + :param message: The current message (which may be None). |
130 | + :param file_matches: Check whether file matches |
131 | + """ |
132 | + self.commit = commit |
133 | + self.message = message |
134 | + self.file_matches = file_matches |
135 | + |
136 | + def make(self): |
137 | + """Make the template. |
138 | + |
139 | + If NEWS is missing or not not modified, the original template is |
140 | + returned unaltered. Otherwise the changes from NEWS are concatenated |
141 | + with whatever message was provided to __init__. |
142 | + """ |
143 | + delta = self.commit.builder.get_basis_delta() |
144 | + found_old_path = None |
145 | + found_entry = None |
146 | + for old_path, new_path, fileid, entry in delta: |
147 | + if self.file_matches(new_path): |
148 | + found_entry = entry |
149 | + found_old_path = old_path |
150 | + break |
151 | + if not found_entry: |
152 | + return self.message |
153 | + if found_old_path is None: |
154 | + # New file |
155 | + _, new_chunks = list( |
156 | + self.commit.builder.repository.iter_files_bytes( |
157 | + [(found_entry.file_id, found_entry.revision, None)]))[0] |
158 | + content = ''.join(new_chunks) |
159 | + return self.merge_message(content) |
160 | + else: |
161 | + # Get a diff. XXX Is this hookable? I thought it was, can't find it |
162 | + # though.... add DiffTree.diff_factories. Sadly thats not at the |
163 | + # right level: we want to identify the changed lines, not have the |
164 | + # final diff: because we want to grab the sections for regions |
165 | + # changed in new version of the file. So for now a direct diff |
166 | + # using patiencediff is done. |
167 | + old_revision = self.commit.basis_tree.get_file_revision( |
168 | + found_entry.file_id) |
169 | + needed = [(found_entry.file_id, found_entry.revision, 'new'), |
170 | + (found_entry.file_id, old_revision, 'old')] |
171 | + contents = self.commit.builder.repository.iter_files_bytes(needed) |
172 | + lines = {} |
173 | + for name, chunks in contents: |
174 | + lines[name] = osutils.chunks_to_lines(chunks) |
175 | + new = lines['new'] |
176 | + sequence_matcher = patiencediff.PatienceSequenceMatcher( |
177 | + None, lines['old'], new) |
178 | + new_lines = [] |
179 | + for group in sequence_matcher.get_opcodes(): |
180 | + tag, i1, i2, j1, j2 = group |
181 | + if tag == 'equal': |
182 | + continue |
183 | + if tag == 'delete': |
184 | + continue |
185 | + new_lines.extend(new[j1:j2]) |
186 | + if not self.commit.revprops.get('bugs'): |
187 | + # TODO: Allow the user to configure the bug tracker to use |
188 | + # rather than hardcoding Launchpad. |
189 | + bt = bugtracker.tracker_registry.get('launchpad') |
190 | + bugids = [] |
191 | + for line in new_lines: |
192 | + bugids.extend(_BUG_MATCH.findall(line)) |
193 | + self.commit.revprops['bugs'] = \ |
194 | + bugtracker.encode_fixes_bug_urls( |
195 | + [bt.get_bug_url(bugid) for bugid in bugids]) |
196 | + return self.merge_message(''.join(new_lines)) |
197 | + |
198 | + def merge_message(self, new_message): |
199 | + """Merge new_message with self.message. |
200 | + |
201 | + :param new_message: A string message to merge with self.message. |
202 | + :return: A string with the merged messages. |
203 | + """ |
204 | + if self.message is None: |
205 | + return new_message |
206 | + return self.message + new_message |
207 | |
208 | === added directory 'breezy/plugins/commitfromnews/tests' |
209 | === added file 'breezy/plugins/commitfromnews/tests/__init__.py' |
210 | --- breezy/plugins/commitfromnews/tests/__init__.py 1970-01-01 00:00:00 +0000 |
211 | +++ breezy/plugins/commitfromnews/tests/__init__.py 2017-07-23 22:07:30 +0000 |
212 | @@ -0,0 +1,29 @@ |
213 | +# Copyright (C) 2010 Canonical Ltd |
214 | +# |
215 | +# This program is free software; you can redistribute it and/or modify |
216 | +# it under the terms of the GNU General Public License as published by |
217 | +# the Free Software Foundation; either version 2 of the License, or |
218 | +# (at your option) any later version. |
219 | +# |
220 | +# This program is distributed in the hope that it will be useful, |
221 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
222 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
223 | +# GNU General Public License for more details. |
224 | +# |
225 | +# You should have received a copy of the GNU General Public License |
226 | +# along with this program; if not, write to the Free Software |
227 | +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
228 | + |
229 | +"""Tests for commitfromnews.""" |
230 | + |
231 | +from __future__ import absolute_import |
232 | + |
233 | +def load_tests(loader, basic_tests, pattern): |
234 | + testmod_names = [ |
235 | + 'test_committemplate', |
236 | + 'test_msgeditor', |
237 | + ] |
238 | + basic_tests.addTest(loader.loadTestsFromModuleNames( |
239 | + ["%s.%s" % (__name__, tmn) for tmn in testmod_names])) |
240 | + return basic_tests |
241 | + |
242 | |
243 | === added file 'breezy/plugins/commitfromnews/tests/test_committemplate.py' |
244 | --- breezy/plugins/commitfromnews/tests/test_committemplate.py 1970-01-01 00:00:00 +0000 |
245 | +++ breezy/plugins/commitfromnews/tests/test_committemplate.py 2017-07-23 22:07:30 +0000 |
246 | @@ -0,0 +1,155 @@ |
247 | +# Copyright (C) 2010 Canonical Ltd |
248 | +# |
249 | +# This program is free software; you can redistribute it and/or modify |
250 | +# it under the terms of the GNU General Public License as published by |
251 | +# the Free Software Foundation; either version 2 of the License, or |
252 | +# (at your option) any later version. |
253 | +# |
254 | +# This program is distributed in the hope that it will be useful, |
255 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
256 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
257 | +# GNU General Public License for more details. |
258 | +# |
259 | +# You should have received a copy of the GNU General Public License |
260 | +# along with this program; if not, write to the Free Software |
261 | +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
262 | + |
263 | +"""Tests for the commit template creation.""" |
264 | + |
265 | +from __future__ import absolute_import |
266 | + |
267 | +from ... import commitfromnews |
268 | +from .... import msgeditor |
269 | +from ....tests import TestCaseWithTransport |
270 | + |
271 | +INITIAL_NEWS_CONTENT = """---------------------------- |
272 | +commitfromnews release notes |
273 | +---------------------------- |
274 | + |
275 | +NEXT (In development) |
276 | +--------------------- |
277 | + |
278 | +IMPROVEMENTS |
279 | +~~~~~~~~~~~~ |
280 | + |
281 | +* Created plugin, basic functionality of looking for NEWS and including the |
282 | + NEWS diff. |
283 | +""" |
284 | + |
285 | + |
286 | +class TestCommitTemplate(TestCaseWithTransport): |
287 | + |
288 | + def capture_template(self, commit, message): |
289 | + self.commits.append(commit) |
290 | + self.messages.append(message) |
291 | + if message is None: |
292 | + message = 'let this commit succeed I command thee.' |
293 | + return message |
294 | + |
295 | + def setup_capture(self): |
296 | + commitfromnews.register() |
297 | + msgeditor.hooks.install_named_hook('commit_message_template', |
298 | + self.capture_template, 'commitfromnews test template') |
299 | + self.messages = [] |
300 | + self.commits = [] |
301 | + |
302 | + def test_initial(self): |
303 | + self.setup_capture() |
304 | + builder = self.make_branch_builder('test') |
305 | + builder.start_series() |
306 | + builder.build_snapshot('BASE-id', None, |
307 | + [('add', ('', None, 'directory', None)), |
308 | + ('add', ('foo', 'foo-id', 'file', 'a\nb\nc\nd\ne\n')), |
309 | + ], |
310 | + message_callback=msgeditor.generate_commit_message_template) |
311 | + builder.finish_series() |
312 | + self.assertEqual([None], self.messages) |
313 | + |
314 | + def test_added_NEWS(self): |
315 | + self.setup_capture() |
316 | + builder = self.make_branch_builder('test') |
317 | + builder.start_series() |
318 | + content = INITIAL_NEWS_CONTENT |
319 | + builder.build_snapshot('BASE-id', None, |
320 | + [('add', ('', None, 'directory', None)), |
321 | + ('add', ('NEWS', 'foo-id', 'file', content)), |
322 | + ], |
323 | + message_callback=msgeditor.generate_commit_message_template) |
324 | + builder.finish_series() |
325 | + self.assertEqual([content], self.messages) |
326 | + |
327 | + def test_changed_NEWS(self): |
328 | + self.setup_capture() |
329 | + builder = self.make_branch_builder('test') |
330 | + builder.start_series() |
331 | + orig_content = INITIAL_NEWS_CONTENT |
332 | + mod_content = """---------------------------- |
333 | +commitfromnews release notes |
334 | +---------------------------- |
335 | + |
336 | +NEXT (In development) |
337 | +--------------------- |
338 | + |
339 | +IMPROVEMENTS |
340 | +~~~~~~~~~~~~ |
341 | + |
342 | +* Added a new change to the system. |
343 | + |
344 | +* Created plugin, basic functionality of looking for NEWS and including the |
345 | + NEWS diff. |
346 | +""" |
347 | + change_content = """* Added a new change to the system. |
348 | + |
349 | +""" |
350 | + builder.build_snapshot('BASE-id', None, |
351 | + [('add', ('', None, 'directory', None)), |
352 | + ('add', ('NEWS', 'foo-id', 'file', orig_content)), |
353 | + ]) |
354 | + builder.build_snapshot(None, None, |
355 | + [('modify', ('foo-id', mod_content)), |
356 | + ], |
357 | + message_callback=msgeditor.generate_commit_message_template) |
358 | + builder.finish_series() |
359 | + self.assertEqual([change_content], self.messages) |
360 | + |
361 | + def test_fix_bug(self): |
362 | + self.setup_capture() |
363 | + builder = self.make_branch_builder('test') |
364 | + builder.start_series() |
365 | + orig_content = INITIAL_NEWS_CONTENT |
366 | + mod_content = """---------------------------- |
367 | +commitfromnews release notes |
368 | +---------------------------- |
369 | + |
370 | +NEXT (In development) |
371 | +--------------------- |
372 | + |
373 | +IMPROVEMENTS |
374 | +~~~~~~~~~~~~ |
375 | + |
376 | +* Created plugin, basic functionality of looking for NEWS and including the |
377 | + NEWS diff. |
378 | + |
379 | +* Fixed a horrible bug. (lp:523423) |
380 | + |
381 | +""" |
382 | + change_content = """ |
383 | +* Fixed a horrible bug. (lp:523423) |
384 | + |
385 | +""" |
386 | + builder.build_snapshot('BASE-id', None, |
387 | + [('add', ('', None, 'directory', None)), |
388 | + ('add', ('NEWS', 'foo-id', 'file', orig_content)), |
389 | + ]) |
390 | + builder.build_snapshot(None, None, |
391 | + [('modify', ('foo-id', mod_content)), |
392 | + ], |
393 | + message_callback=msgeditor.generate_commit_message_template) |
394 | + builder.finish_series() |
395 | + self.assertEqual([change_content], self.messages) |
396 | + self.assertEqual(1, len(self.commits)) |
397 | + self.assertEquals('https://launchpad.net/bugs/523423 fixed', |
398 | + self.commits[0].revprops['bugs']) |
399 | + |
400 | + def _todo_test_passes_messages_through(self): |
401 | + pass |
402 | |
403 | === added file 'breezy/plugins/commitfromnews/tests/test_msgeditor.py' |
404 | --- breezy/plugins/commitfromnews/tests/test_msgeditor.py 1970-01-01 00:00:00 +0000 |
405 | +++ breezy/plugins/commitfromnews/tests/test_msgeditor.py 2017-07-23 22:07:30 +0000 |
406 | @@ -0,0 +1,36 @@ |
407 | +# Copyright (C) 2010 Canonical Ltd |
408 | +# |
409 | +# This program is free software; you can redistribute it and/or modify |
410 | +# it under the terms of the GNU General Public License as published by |
411 | +# the Free Software Foundation; either version 2 of the License, or |
412 | +# (at your option) any later version. |
413 | +# |
414 | +# This program is distributed in the hope that it will be useful, |
415 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
416 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
417 | +# GNU General Public License for more details. |
418 | +# |
419 | +# You should have received a copy of the GNU General Public License |
420 | +# along with this program; if not, write to the Free Software |
421 | +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
422 | + |
423 | +"""Tests for msg editor interactions..""" |
424 | + |
425 | +from __future__ import absolute_import |
426 | + |
427 | +from ... import commitfromnews |
428 | +from .... import msgeditor |
429 | +from ....tests import TestCaseWithTransport |
430 | + |
431 | + |
432 | +class TestRegisters(TestCaseWithTransport): |
433 | + |
434 | + def test_registered_at_import(self): |
435 | + self.assertTrue(commitfromnews._registered) |
436 | + |
437 | + def test_register_registers_for_commit_message_template(self): |
438 | + commitfromnews._registered = False |
439 | + # Registers only within the plugin |
440 | + commitfromnews.register() |
441 | + self.assertLength(1, msgeditor.hooks['commit_message_template']) |
442 | + self.assertTrue(commitfromnews._registered) |
443 | |
444 | === modified file 'doc/en/release-notes/brz-3.0.txt' |
445 | --- doc/en/release-notes/brz-3.0.txt 2017-07-20 00:00:04 +0000 |
446 | +++ doc/en/release-notes/brz-3.0.txt 2017-07-23 22:07:30 +0000 |
447 | @@ -86,6 +86,10 @@ |
448 | * The 'fetch-ghosts' command is now bundled with brz. |
449 | Imported from bzrtools by Aaron Bentley. (Jelmer Vernooij) |
450 | |
451 | + * The 'commitfromnews' plugin is now bundled and |
452 | + can be enabled by setting ``commit.template_from_files = NEWS``. |
453 | + (Jelmer Vernooij) |
454 | + |
455 | * The functionality from ``bzr-guess`` is now merged into Breezy. |
456 | It will provide suggestions if the user typoes a command. |
457 | (Jelmer Vernooij) |
Bundling seems fine, couple of issues inline.