Merge lp:~vila/bzr/per-file-merge-hook into lp:bzr
- per-file-merge-hook
- Merge into bzr.dev
Proposed by
Vincent Ladeuil
Status: | Merged |
---|---|
Approved by: | John A Meinel |
Approved revision: | not available |
Merged at revision: | not available |
Proposed branch: | lp:~vila/bzr/per-file-merge-hook |
Merge into: | lp:bzr |
Prerequisite: | lp:~spiv/bzr/per-file-merge-hook-491711 |
Diff against target: |
262 lines (+115/-19) 8 files modified
bzrlib/decorators.py (+2/-1) bzrlib/merge.py (+2/-2) bzrlib/plugins/news_merge/README (+1/-3) bzrlib/plugins/news_merge/__init__.py (+28/-5) bzrlib/plugins/news_merge/news_merge.py (+0/-1) bzrlib/plugins/news_merge/tests/__init__.py (+23/-0) bzrlib/plugins/news_merge/tests/test_news_merge.py (+51/-0) bzrlib/tests/per_merger.py (+8/-7) |
To merge this branch: | bzr merge lp:~vila/bzr/per-file-merge-hook |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
bzr-core | Pending | ||
Review via email: mp+17754@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Vincent Ladeuil (vila) wrote : | # |
Revision history for this message
John A Meinel (jameinel) wrote : | # |
looks reasonable to me
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'bzrlib/decorators.py' | |||
2 | --- bzrlib/decorators.py 2010-01-20 16:12:23 +0000 | |||
3 | +++ bzrlib/decorators.py 2010-01-20 16:12:25 +0000 | |||
4 | @@ -256,7 +256,8 @@ | |||
5 | 256 | 256 | ||
6 | 257 | 257 | ||
7 | 258 | # This implementation of cachedproperty is copied from Launchpad's | 258 | # This implementation of cachedproperty is copied from Launchpad's |
9 | 259 | # canonical.launchpad.cachedproperty module. | 259 | # canonical.launchpad.cachedproperty module (with permission from flacoste) |
10 | 260 | # -- spiv & vila 100120 | ||
11 | 260 | def cachedproperty(attrname_or_fn): | 261 | def cachedproperty(attrname_or_fn): |
12 | 261 | """A decorator for methods that makes them properties with their return | 262 | """A decorator for methods that makes them properties with their return |
13 | 262 | value cached. | 263 | value cached. |
14 | 263 | 264 | ||
15 | === modified file 'bzrlib/merge.py' | |||
16 | --- bzrlib/merge.py 2010-01-20 16:12:23 +0000 | |||
17 | +++ bzrlib/merge.py 2010-01-20 16:12:25 +0000 | |||
18 | @@ -89,11 +89,11 @@ | |||
19 | 89 | self.this_kind = this_kind | 89 | self.this_kind = this_kind |
20 | 90 | self.other_kind = other_kind | 90 | self.other_kind = other_kind |
21 | 91 | self.winner = winner | 91 | self.winner = winner |
23 | 92 | 92 | ||
24 | 93 | def is_file_merge(self): | 93 | def is_file_merge(self): |
25 | 94 | """True if this_kind and other_kind are both 'file'.""" | 94 | """True if this_kind and other_kind are both 'file'.""" |
26 | 95 | return self.this_kind == 'file' and self.other_kind == 'file' | 95 | return self.this_kind == 'file' and self.other_kind == 'file' |
28 | 96 | 96 | ||
29 | 97 | @decorators.cachedproperty | 97 | @decorators.cachedproperty |
30 | 98 | def base_lines(self): | 98 | def base_lines(self): |
31 | 99 | """The lines of the 'base' version of the file.""" | 99 | """The lines of the 'base' version of the file.""" |
32 | 100 | 100 | ||
33 | === modified file 'bzrlib/plugins/news_merge/README' | |||
34 | --- bzrlib/plugins/news_merge/README 2010-01-20 16:12:23 +0000 | |||
35 | +++ bzrlib/plugins/news_merge/README 2010-01-20 16:12:25 +0000 | |||
36 | @@ -1,8 +1,6 @@ | |||
37 | 1 | A plugin for merging bzr's NEWS file. | 1 | A plugin for merging bzr's NEWS file. |
38 | 2 | 2 | ||
42 | 3 | Install as usual (e.g. symlink this directory into ~/.bazaar/plugins), and any | 3 | This plugin is activated via configuration variables, see 'bzr help news_merge'. |
40 | 4 | file with a path of NEWS that is in bzr's NEWS format will be merged with this | ||
41 | 5 | hook. | ||
43 | 6 | 4 | ||
44 | 7 | This hook can resolve conflicts where both sides add entries at the same place. | 5 | This hook can resolve conflicts where both sides add entries at the same place. |
45 | 8 | If it encounters a more difficult conflict it gives up and bzr will fallback to | 6 | If it encounters a more difficult conflict it gives up and bzr will fallback to |
46 | 9 | 7 | ||
47 | === modified file 'bzrlib/plugins/news_merge/__init__.py' | |||
48 | --- bzrlib/plugins/news_merge/__init__.py 2010-01-20 16:12:23 +0000 | |||
49 | +++ bzrlib/plugins/news_merge/__init__.py 2010-01-20 16:12:25 +0000 | |||
50 | @@ -32,6 +32,9 @@ | |||
51 | 32 | how to resolve that, so bzr will fallback to the default line-based merge. | 32 | how to resolve that, so bzr will fallback to the default line-based merge. |
52 | 33 | """ | 33 | """ |
53 | 34 | 34 | ||
54 | 35 | # Since we are a built-in plugin we share the bzrlib version | ||
55 | 36 | from bzrlib import version_info | ||
56 | 37 | |||
57 | 35 | # Put most of the code in a separate module that we lazy-import to keep the | 38 | # Put most of the code in a separate module that we lazy-import to keep the |
58 | 36 | # overhead of this plugin as minimal as possible. | 39 | # overhead of this plugin as minimal as possible. |
59 | 37 | from bzrlib.lazy_import import lazy_import | 40 | from bzrlib.lazy_import import lazy_import |
60 | @@ -59,15 +62,35 @@ | |||
61 | 59 | 62 | ||
62 | 60 | 63 | ||
63 | 61 | def filename_matches_config(params): | 64 | def filename_matches_config(params): |
66 | 62 | config = params.merger.this_branch.get_config() | 65 | affected_files = getattr(params, '_news_merge_affected_files', None) |
67 | 63 | affected_files = config.get_user_option_as_list('news_merge_files') | 66 | if affected_files is None: |
68 | 67 | config = params.merger.this_tree.branch.get_config() | ||
69 | 68 | # Until bzr provides a better policy for caching the config, we just | ||
70 | 69 | # add the part we're interested in to the params to avoid reading the | ||
71 | 70 | # config files repeatedly (bazaar.conf, location.conf, branch.conf). | ||
72 | 71 | affected_files = config.get_user_option_as_list('news_merge_files') | ||
73 | 72 | if affected_files is None: | ||
74 | 73 | # If nothing was specified in the config, we have nothing to do, | ||
75 | 74 | # but we use None in the params to start the caching. | ||
76 | 75 | affected_files = [] | ||
77 | 76 | params._news_merge_affected_files = affected_files | ||
78 | 64 | if affected_files: | 77 | if affected_files: |
79 | 65 | filename = params.merger.this_tree.id2path(params.file_id) | 78 | filename = params.merger.this_tree.id2path(params.file_id) |
80 | 66 | if filename in affected_files: | 79 | if filename in affected_files: |
81 | 67 | return True | 80 | return True |
82 | 68 | return False | 81 | return False |
83 | 69 | 82 | ||
87 | 70 | 83 | def install_hook(): | |
88 | 71 | Merger.hooks.install_named_hook( | 84 | Merger.hooks.install_named_hook( |
89 | 72 | 'merge_file_content', news_merge_hook, 'NEWS file merge') | 85 | 'merge_file_content', news_merge_hook, 'NEWS file merge') |
90 | 86 | install_hook() | ||
91 | 87 | |||
92 | 88 | |||
93 | 89 | def load_tests(basic_tests, module, loader): | ||
94 | 90 | testmod_names = [ | ||
95 | 91 | 'tests', | ||
96 | 92 | ] | ||
97 | 93 | basic_tests.addTest(loader.loadTestsFromModuleNames( | ||
98 | 94 | ["%s.%s" % (__name__, tmn) for tmn in testmod_names])) | ||
99 | 95 | return basic_tests | ||
100 | 73 | 96 | ||
101 | 74 | 97 | ||
102 | === modified file 'bzrlib/plugins/news_merge/news_merge.py' | |||
103 | --- bzrlib/plugins/news_merge/news_merge.py 2010-01-20 16:12:23 +0000 | |||
104 | +++ bzrlib/plugins/news_merge/news_merge.py 2010-01-20 16:12:25 +0000 | |||
105 | @@ -85,4 +85,3 @@ | |||
106 | 85 | 85 | ||
107 | 86 | def sort_key(s): | 86 | def sort_key(s): |
108 | 87 | return s.replace('`', '').lower() | 87 | return s.replace('`', '').lower() |
109 | 88 | |||
110 | 89 | 88 | ||
111 | === added directory 'bzrlib/plugins/news_merge/tests' | |||
112 | === added file 'bzrlib/plugins/news_merge/tests/__init__.py' | |||
113 | --- bzrlib/plugins/news_merge/tests/__init__.py 1970-01-01 00:00:00 +0000 | |||
114 | +++ bzrlib/plugins/news_merge/tests/__init__.py 2010-01-20 16:12:25 +0000 | |||
115 | @@ -0,0 +1,23 @@ | |||
116 | 1 | # Copyright (C) 2010 by Canonical Ltd | ||
117 | 2 | # | ||
118 | 3 | # This program is free software; you can redistribute it and/or modify | ||
119 | 4 | # it under the terms of the GNU General Public License as published by | ||
120 | 5 | # the Free Software Foundation; either version 2 of the License, or | ||
121 | 6 | # (at your option) any later version. | ||
122 | 7 | # | ||
123 | 8 | # This program is distributed in the hope that it will be useful, | ||
124 | 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
125 | 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
126 | 11 | # GNU General Public License for more details. | ||
127 | 12 | # | ||
128 | 13 | # You should have received a copy of the GNU General Public License | ||
129 | 14 | # along with this program; if not, write to the Free Software | ||
130 | 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
131 | 16 | |||
132 | 17 | def load_tests(basic_tests, module, loader): | ||
133 | 18 | testmod_names = [ | ||
134 | 19 | 'test_news_merge', | ||
135 | 20 | ] | ||
136 | 21 | basic_tests.addTest(loader.loadTestsFromModuleNames( | ||
137 | 22 | ["%s.%s" % (__name__, tmn) for tmn in testmod_names])) | ||
138 | 23 | return basic_tests | ||
139 | 0 | 24 | ||
140 | === added file 'bzrlib/plugins/news_merge/tests/test_news_merge.py' | |||
141 | --- bzrlib/plugins/news_merge/tests/test_news_merge.py 1970-01-01 00:00:00 +0000 | |||
142 | +++ bzrlib/plugins/news_merge/tests/test_news_merge.py 2010-01-20 16:12:25 +0000 | |||
143 | @@ -0,0 +1,51 @@ | |||
144 | 1 | # Copyright (C) 2010 by Canonical Ltd | ||
145 | 2 | # | ||
146 | 3 | # This program is free software; you can redistribute it and/or modify | ||
147 | 4 | # it under the terms of the GNU General Public License as published by | ||
148 | 5 | # the Free Software Foundation; either version 2 of the License, or | ||
149 | 6 | # (at your option) any later version. | ||
150 | 7 | # | ||
151 | 8 | # This program is distributed in the hope that it will be useful, | ||
152 | 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
153 | 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
154 | 11 | # GNU General Public License for more details. | ||
155 | 12 | # | ||
156 | 13 | # You should have received a copy of the GNU General Public License | ||
157 | 14 | # along with this program; if not, write to the Free Software | ||
158 | 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
159 | 16 | |||
160 | 17 | # FIXME: This is totally incomplete but I'm only the patch pilot :-) | ||
161 | 18 | # -- vila 100120 | ||
162 | 19 | |||
163 | 20 | from bzrlib import ( | ||
164 | 21 | option, | ||
165 | 22 | tests, | ||
166 | 23 | ) | ||
167 | 24 | from bzrlib.plugins import news_merge | ||
168 | 25 | from bzrlib.tests import test_merge_core | ||
169 | 26 | |||
170 | 27 | |||
171 | 28 | class TestFilenameMatchesConfig(tests.TestCaseWithTransport): | ||
172 | 29 | |||
173 | 30 | def test_affected_files_cached(self): | ||
174 | 31 | """Ensures that the config variable is cached""" | ||
175 | 32 | news_merge.install_hook() | ||
176 | 33 | self.affected_files = None | ||
177 | 34 | orig = news_merge.filename_matches_config | ||
178 | 35 | def wrapper(params): | ||
179 | 36 | ret = orig(params) | ||
180 | 37 | # Capture the value set by the hook | ||
181 | 38 | self.affected_files = params._news_merge_affected_files | ||
182 | 39 | return ret | ||
183 | 40 | def restore(): | ||
184 | 41 | news_merge.filename_matches_config = orig | ||
185 | 42 | self.addCleanup(restore) | ||
186 | 43 | news_merge.filename_matches_config = wrapper | ||
187 | 44 | |||
188 | 45 | builder = test_merge_core.MergeBuilder(self.test_base_dir) | ||
189 | 46 | self.addCleanup(builder.cleanup) | ||
190 | 47 | builder.add_file('NEWS', builder.tree_root, 'name1', 'text1', True) | ||
191 | 48 | builder.change_contents('NEWS', other='text4', this='text3') | ||
192 | 49 | conflicts = builder.merge() | ||
193 | 50 | # The hook should set the variable | ||
194 | 51 | self.assertIsNot(None, self.affected_files) | ||
195 | 0 | 52 | ||
196 | === modified file 'bzrlib/tests/per_merger.py' | |||
197 | --- bzrlib/tests/per_merger.py 2010-01-20 16:12:23 +0000 | |||
198 | +++ bzrlib/tests/per_merger.py 2010-01-20 16:12:25 +0000 | |||
199 | @@ -142,7 +142,8 @@ | |||
200 | 142 | this_tree = branch.bzrdir.create_workingtree() | 142 | this_tree = branch.bzrdir.create_workingtree() |
201 | 143 | this_tree.lock_write() | 143 | this_tree.lock_write() |
202 | 144 | self.addCleanup(this_tree.unlock) | 144 | self.addCleanup(this_tree.unlock) |
204 | 145 | other_tree = this_tree.bzrdir.sprout('other', 'OTHER-id').open_workingtree() | 145 | other_tree = this_tree.bzrdir.sprout('other', |
205 | 146 | 'OTHER-id').open_workingtree() | ||
206 | 146 | self.do_merge(this_tree, other_tree) | 147 | self.do_merge(this_tree, other_tree) |
207 | 147 | if self.merge_type is _mod_merge.LCAMerger: | 148 | if self.merge_type is _mod_merge.LCAMerger: |
208 | 148 | self.expectFailure("lca merge doesn't track deleted lines", | 149 | self.expectFailure("lca merge doesn't track deleted lines", |
209 | @@ -174,7 +175,7 @@ | |||
210 | 174 | deletiondir = transform._deletiondir | 175 | deletiondir = transform._deletiondir |
211 | 175 | transform.finalize() | 176 | transform.finalize() |
212 | 176 | return (limbodir, deletiondir) | 177 | return (limbodir, deletiondir) |
214 | 177 | 178 | ||
215 | 178 | def test_merge_with_existing_limbo(self): | 179 | def test_merge_with_existing_limbo(self): |
216 | 179 | wt = self.make_branch_and_tree('this') | 180 | wt = self.make_branch_and_tree('this') |
217 | 180 | (limbodir, deletiondir) = self.get_limbodir_deletiondir(wt) | 181 | (limbodir, deletiondir) = self.get_limbodir_deletiondir(wt) |
218 | @@ -196,7 +197,7 @@ | |||
219 | 196 | def setUp(self): | 197 | def setUp(self): |
220 | 197 | TestCaseWithTransport.setUp(self) | 198 | TestCaseWithTransport.setUp(self) |
221 | 198 | self.hook_log = [] | 199 | self.hook_log = [] |
223 | 199 | 200 | ||
224 | 200 | def install_hook_noop(self): | 201 | def install_hook_noop(self): |
225 | 201 | def hook_na(merge_params): | 202 | def hook_na(merge_params): |
226 | 202 | # This hook unconditionally does nothing. | 203 | # This hook unconditionally does nothing. |
227 | @@ -213,7 +214,7 @@ | |||
228 | 213 | return 'not_applicable', None | 214 | return 'not_applicable', None |
229 | 214 | _mod_merge.Merger.hooks.install_named_hook( | 215 | _mod_merge.Merger.hooks.install_named_hook( |
230 | 215 | 'merge_file_content', hook_success, 'test hook (success)') | 216 | 'merge_file_content', hook_success, 'test hook (success)') |
232 | 216 | 217 | ||
233 | 217 | def install_hook_conflict(self): | 218 | def install_hook_conflict(self): |
234 | 218 | def hook_conflict(merge_params): | 219 | def hook_conflict(merge_params): |
235 | 219 | self.hook_log.append(('conflict',)) | 220 | self.hook_log.append(('conflict',)) |
236 | @@ -222,7 +223,7 @@ | |||
237 | 222 | return 'not_applicable', None | 223 | return 'not_applicable', None |
238 | 223 | _mod_merge.Merger.hooks.install_named_hook( | 224 | _mod_merge.Merger.hooks.install_named_hook( |
239 | 224 | 'merge_file_content', hook_conflict, 'test hook (delete)') | 225 | 'merge_file_content', hook_conflict, 'test hook (delete)') |
241 | 225 | 226 | ||
242 | 226 | def install_hook_delete(self): | 227 | def install_hook_delete(self): |
243 | 227 | def hook_delete(merge_params): | 228 | def hook_delete(merge_params): |
244 | 228 | self.hook_log.append(('delete',)) | 229 | self.hook_log.append(('delete',)) |
245 | @@ -231,7 +232,7 @@ | |||
246 | 231 | return 'not_applicable', None | 232 | return 'not_applicable', None |
247 | 232 | _mod_merge.Merger.hooks.install_named_hook( | 233 | _mod_merge.Merger.hooks.install_named_hook( |
248 | 233 | 'merge_file_content', hook_delete, 'test hook (delete)') | 234 | 'merge_file_content', hook_delete, 'test hook (delete)') |
250 | 234 | 235 | ||
251 | 235 | def install_hook_log_lines(self): | 236 | def install_hook_log_lines(self): |
252 | 236 | """Install a hook that saves the get_lines for the this, base and other | 237 | """Install a hook that saves the get_lines for the this, base and other |
253 | 237 | versions of the file. | 238 | versions of the file. |
254 | @@ -255,7 +256,7 @@ | |||
255 | 255 | def create_file_needing_contents_merge(self, builder, file_id): | 256 | def create_file_needing_contents_merge(self, builder, file_id): |
256 | 256 | builder.add_file(file_id, builder.tree_root, "name1", "text1", True) | 257 | builder.add_file(file_id, builder.tree_root, "name1", "text1", True) |
257 | 257 | builder.change_contents(file_id, other="text4", this="text3") | 258 | builder.change_contents(file_id, other="text4", this="text3") |
259 | 258 | 259 | ||
260 | 259 | def test_change_vs_change(self): | 260 | def test_change_vs_change(self): |
261 | 260 | """Hook is used for (changed, changed)""" | 261 | """Hook is used for (changed, changed)""" |
262 | 261 | self.install_hook_success() | 262 | self.install_hook_success() |
Since spiv is on leave, I've finished his lp:~spiv/bzr/per-file-merge-hook-491711
merge proposal based on:
- the reviews,
- some discussion on IRC,
- some more cleanup.
The core modification is about caching the config data to avoid re-reading
the config files for each merged file.
* bzrlib/ tests/per_ merger. py:
Fix line too long and spurious spaces.
* bzrlib/ plugins/ news_merge/ tests/test_ news_merge. py: tchesConfig) : Ensure that the params get updated.
(TestFilenameMa
* bzrlib/ plugins/ news_merge/ __init_ _.py: matches_ config) : Save the relevant config variable in
(filename_
the hook params.
(install_hook): Wrap the hook installation so we can reuse it for
tests.
* bzrlib/ plugins/ news_merge/ README:
Update the instructions by pointing to the plugin help.
* bzrlib/merge.py:
(MergeHookParams): Delete spurious spaces.
* bzrlib/ decorators. py: decorators) : Mention that we get clearance to copy
(use_pretty_
launchpad code here (since canonical has copyrights on both code
bases).
As it's far too much to be considered simple tweaks, I'd like a review on the diff
before (if approved :) landing both.