Merge lp:~oddbloke/pqm/merge-directives into lp:pqm
- merge-directives
- Merge into trunk
Status: | Work in progress |
---|---|
Proposed branch: | lp:~oddbloke/pqm/merge-directives |
Merge into: | lp:pqm |
Prerequisite: | lp:~oddbloke/pqm/fix-tests |
Diff against target: |
793 lines (+488/-20) (has conflicts) 7 files modified
.bzrignore (+1/-0) pqm/__init__.py (+79/-0) pqm/script.py (+164/-8) pqm/tests/test_pqm.py (+201/-4) tests/Makefile.am (+2/-1) tests/bzr-merge-2.sh (+20/-0) tests/test-framework (+21/-7) Text conflict in pqm/__init__.py Text conflict in pqm/script.py Text conflict in pqm/tests/test_pqm.py |
To merge this branch: | bzr merge lp:~oddbloke/pqm/merge-directives |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Robert Collins | Needs Resubmitting | ||
Review via email: mp+833@code.launchpad.net |
This proposal supersedes a proposal from 2008-07-09.
Commit message
Description of the change
Robert Collins (lifeless) wrote : Posted in a previous version of this proposal | # |
Dan Watkins (oddbloke) wrote : | # |
To address Robert's issues:
> - uses more shell tests rather than python unit tests
I'm not sure what the Python tests are missing. If you could give me some suggestions, I'd be happy to put them into practice.
> - the new Command class won't render correctly in the web GUI
Fixed.
> - I don't like the merge_callback lambda usage, its unobvious and doesn't seem necessary.
I agree. Fixed. In the process, replay merge became its own Command (rather than being a part of CommandRunner) to stop my head exploding because of the broken merge abstraction we had.
Robert Collins (lifeless) wrote : | # |
On Sun, 2008-08-24 at 03:29 +0000, Daniel Watkins wrote:
> To address Robert's issues:
>
> > - uses more shell tests rather than python unit tests
> I'm not sure what the Python tests are missing. If you could give me some suggestions, I'd be happy to put them into practice.
Well, there are new shell tests; either they are redundant (in which
case, remove em :)) or they test something not tested in the python
tests (in which case move them into python tests).
> > - the new Command class won't render correctly in the web GUI
> Fixed.
>
> > - I don't like the merge_callback lambda usage, its unobvious and doesn't seem necessary.
> I agree. Fixed. In the process, replay merge became its own Command (rather than being a part of CommandRunner) to stop my head exploding because of the broken merge abstraction we had.
Cool - thanks.
-Rob
--
GPG key available at: <http://
Tim Penhey (thumper) wrote : | # |
Were you going to remove the shell tests?
Dan Watkins (oddbloke) wrote : | # |
On Wed, 2008-11-26 at 06:40 +0000, Tim Penhey wrote:
> Were you going to remove the shell tests?
I believe the intent is to replace them with Python unit tests, in time.
Tim Penhey (thumper) wrote : | # |
On Wed, 26 Nov 2008 22:51:31 Daniel Watkins wrote:
> On Wed, 2008-11-26 at 06:40 +0000, Tim Penhey wrote:
> > Were you going to remove the shell tests?
>
> I believe the intent is to replace them with Python unit tests, in
> time.
OK, I'll look to include this in my integration branch shortly (not
tonight).
Robert Collins (lifeless) wrote : | # |
Needs conflicts fixing at minimum.
Unmerged revisions
- 181. By Dan Watkins
-
Readded replay merge as ReplayMergeCommand.
- 180. By Dan Watkins
-
Removed use of merge_callback.
- 179. By Dan Watkins
-
Refactored merge code from Command to MergeCommand.
- 178. By Dan Watkins
-
Removed replay legacy command.
- 177. By Dan Watkins
-
ExtendedMergeCo
mmands now work with the UI correctly. - 176. By Dan Watkins
-
Merged pqm.dev r183.
- 175. By Dan Watkins
-
Fixed failing tests.
- 174. By Dan Watkins
-
Merged pqm.dev r182, without fixing tests.
- 173. By Dan Watkins
-
Fixed issues with PGP headers and footers in merge directive mail.
- 172. By Dan Watkins
-
Modified test to step around issues with detecting which VCS a branch uses.
Updating diff...
An updated diff will be available in a few minutes. Reload to see the changes.
Preview Diff
1 | === modified file '.bzrignore' |
2 | --- .bzrignore 2009-08-30 02:09:42 +0000 |
3 | +++ .bzrignore 2010-05-04 16:39:23 +0000 |
4 | @@ -8,6 +8,7 @@ |
5 | configure |
6 | autotools/* |
7 | tests/.gnupg/* |
8 | +./bzr |
9 | twistd.log* |
10 | tests/workdir |
11 | bzr |
12 | |
13 | === modified file 'pqm/__init__.py' |
14 | --- pqm/__init__.py 2010-04-20 12:11:40 +0000 |
15 | +++ pqm/__init__.py 2010-05-04 16:39:23 +0000 |
16 | @@ -25,9 +25,21 @@ |
17 | import re |
18 | import stat |
19 | import string |
20 | +from StringIO import StringIO |
21 | import sys |
22 | |
23 | +<<<<<<< TREE |
24 | from pqm.core import do_mkdir |
25 | +======= |
26 | +from bzrlib import ( |
27 | + errors, |
28 | + merge_directive, |
29 | + testament, |
30 | + ) |
31 | + |
32 | +from PQMConfigParser import ConfigParser |
33 | + |
34 | +>>>>>>> MERGE-SOURCE |
35 | from pqm.errors import PQMException, PQMTlaFailure |
36 | from pqm.script import Script |
37 | |
38 | @@ -420,20 +432,85 @@ |
39 | return output |
40 | |
41 | def do_star_merge(self, sender, from_branch, local_dir): |
42 | +<<<<<<< TREE |
43 | import bzrlib.errors |
44 | +======= |
45 | + return self._do_bzr_merge(sender, None, None, local_dir, from_branch, |
46 | + None) |
47 | + |
48 | + def do_bzr_merge(self, sender, revision_id, testament_sha1, |
49 | + local_dir, from_branch=None, bundle=None): |
50 | + if None in (revision_id, testament_sha1): |
51 | + raise PQMTlaFailure(sender, ['required argument missing']) |
52 | + return self._do_bzr_merge(sender, revision_id, testament_sha1, |
53 | + local_dir, from_branch, bundle) |
54 | + |
55 | + def _do_bzr_merge(self, sender, revision_id, testament_sha1, local_dir, |
56 | + from_branch, bundle): |
57 | + from bzrlib.branch import Branch |
58 | + from bzrlib.bundle.apply_bundle import install_bundle |
59 | + from bzrlib.bundle import serializer as bundle_serializer |
60 | + from bzrlib.merge import Merger, Merge3Merger |
61 | +>>>>>>> MERGE-SOURCE |
62 | from bzrlib.workingtree import WorkingTree |
63 | tree = WorkingTree.open(local_dir) |
64 | tree.lock_write() |
65 | try: |
66 | +<<<<<<< TREE |
67 | try: |
68 | return self._do_star_merge(sender, from_branch, local_dir, tree) |
69 | except bzrlib.errors.UnrelatedBranches: |
70 | raise PQMTlaFailure(sender, |
71 | ['Branches have no common ancestor, and no merge base' |
72 | ' revision was specified.']) |
73 | +======= |
74 | + merger = Merger(tree.branch, this_tree=tree) |
75 | + merger.check_basis(True) |
76 | + if from_branch is None and bundle is None: |
77 | + raise PQMTlaFailure(sender, |
78 | + ['Either a branch or a bundle must be supplied']) |
79 | + |
80 | + if bundle is not None: |
81 | + other_branch = tree.branch |
82 | + reader = bundle_serializer.read_bundle(StringIO(bundle)) |
83 | + install_bundle(other_branch.repository, reader) |
84 | + else: |
85 | + other_branch = Branch.open(from_branch) |
86 | + if testament_sha1 is not None: |
87 | + my_testament = testament.StrictTestament3.from_revision( |
88 | + other_branch.repository, revision_id) |
89 | + if my_testament.as_sha1() != testament_sha1: |
90 | + raise PQMTlaFailure(sender, |
91 | + ['Revision testament mismatch: "%s" is not %s.' % |
92 | + (my_testament.as_sha1(), testament_sha1)]) |
93 | + if revision_id is None: |
94 | + revision_id = other_branch.last_revision() |
95 | + merger.set_other_revision(revision_id, other_branch) |
96 | + merger.find_base() |
97 | + if merger.base_rev_id == merger.other_rev_id: |
98 | + raise PQMTlaFailure(sender, ['Nothing to merge.']) |
99 | + merger.backup_files = False |
100 | + merger.merge_type = Merge3Merger |
101 | + merger.set_interesting_files(None) |
102 | + merger.show_base = False |
103 | + merger.reprocess = False |
104 | + conflicts = merger.do_merge() |
105 | + merger.set_pending() |
106 | + if conflicts: |
107 | + error_lines = ['Conflicts during merge: %s' % conflicts] |
108 | + tree.lock_read() |
109 | + try: |
110 | + for conflict in tree.conflicts(): |
111 | + error_lines.append(str(conflict)) |
112 | + finally: |
113 | + tree.unlock() |
114 | + raise PQMTlaFailure(sender, error_lines) |
115 | + return ["merge successful"] |
116 | +>>>>>>> MERGE-SOURCE |
117 | finally: |
118 | tree.unlock() |
119 | |
120 | +<<<<<<< TREE |
121 | def _do_star_merge(self, sender, from_branch, local_dir, tree): |
122 | from bzrlib.merge import Merger, Merge3Merger |
123 | from bzrlib.tag import _merge_tags_if_possible |
124 | @@ -462,6 +539,8 @@ |
125 | _merge_tags_if_possible(merger.other_branch, tree.branch) |
126 | return ["merge successful"] |
127 | |
128 | +======= |
129 | +>>>>>>> MERGE-SOURCE |
130 | def make_local_dir(self, sender, branch_spec, output_dir): |
131 | from bzrlib.branch import Branch, BranchReferenceFormat |
132 | import bzrlib.bzrdir as bzrdir |
133 | |
134 | === modified file 'pqm/script.py' |
135 | --- pqm/script.py 2010-04-21 22:38:26 +0000 |
136 | +++ pqm/script.py 2010-05-04 16:39:23 +0000 |
137 | @@ -36,6 +36,8 @@ |
138 | import config_manager |
139 | import subunit.test_results |
140 | |
141 | +from bzrlib import merge_directive |
142 | +import bzrlib.errors |
143 | from pqm.errors import PQMCmdFailure, PQMException, PQMTlaFailure |
144 | |
145 | |
146 | @@ -192,6 +194,7 @@ |
147 | pgp_re = re.compile('^-----BEGIN PGP.*MESSAGE') |
148 | pgp_end_re = re.compile('^-----BEGIN PGP SIG') |
149 | # parser for merge recognition |
150 | + replay_re = re.compile('^replay (\S+/\S+)\s+(\S+/\S+)\s*$') |
151 | star_re = re.compile('^star-merge (\S+/\S+)\s+(\S+/\S+)\s*$') |
152 | # parse matcher for the debug command |
153 | debug_re = re.compile('^debug') |
154 | @@ -293,18 +296,59 @@ |
155 | result.append(line) |
156 | return result |
157 | |
158 | + def getRawLines(self): |
159 | + if not self.readComplete: |
160 | + self._read() |
161 | + return self.msg.get_payload().splitlines(True) |
162 | + |
163 | def getCommands(self): |
164 | """Get the actual command lines from the script.""" |
165 | self.logger.info("parsing commands") |
166 | result = [] |
167 | legacy_lines = [] |
168 | + try: |
169 | + md_lines = [] |
170 | + to_skip = 0 |
171 | + for line in self.getRawLines(): |
172 | + if to_skip: |
173 | + to_skip -= 1 |
174 | + continue |
175 | + if self.pgp_re.match(line): |
176 | + to_skip = 2 |
177 | + continue |
178 | + if self.pgp_end_re.match(line): |
179 | + break |
180 | + md_lines.append(line) |
181 | + directive = merge_directive.MergeDirective.from_lines(md_lines) |
182 | + except bzrlib.errors.NotAMergeDirective: |
183 | + pass |
184 | + else: |
185 | + result.append(ExtendedMergeCommand(self, |
186 | + self._branch_spec_handler, |
187 | + self._configp, |
188 | + directive.revision_id, |
189 | + directive.testament_sha1, |
190 | + directive.message, |
191 | + directive.target_branch, |
192 | + directive.source_branch, |
193 | + directive.get_raw_bundle(), |
194 | + )) |
195 | + return result |
196 | + |
197 | for line in self.getLines(): |
198 | if not self.isCommand(line): |
199 | continue |
200 | # identify and construct commands |
201 | +<<<<<<< TREE |
202 | star_match = EmailScript.star_re.match(line) |
203 | debug_match = EmailScript.debug_re.match(line) |
204 | any_match = star_match or debug_match |
205 | +======= |
206 | + debug_match = Script.debug_re.match(line) |
207 | + replay_match = Script.replay_re.match(line) |
208 | + star_match = Script.star_re.match(line) |
209 | + any_match = debug_match or replay_match or star_match |
210 | +>>>>>>> MERGE-SOURCE |
211 | if any_match and legacy_lines: |
212 | result.append(CommandRunner(self, |
213 | self._branch_spec_handler, |
214 | @@ -323,8 +367,18 @@ |
215 | elif debug_match: |
216 | result.append(DebugCommand(self, |
217 | self._branch_spec_handler, |
218 | +<<<<<<< TREE |
219 | self._configp, |
220 | self.manager)) |
221 | +======= |
222 | + self._configp)) |
223 | + elif replay_match: |
224 | + result.append(ReplayMergeCommand(self, |
225 | + self._branch_spec_handler, |
226 | + self._configp, |
227 | + replay_match.group(1), |
228 | + replay_match.group(2))) |
229 | +>>>>>>> MERGE-SOURCE |
230 | else: |
231 | legacy_lines.append(line) |
232 | if legacy_lines: |
233 | @@ -621,6 +675,7 @@ |
234 | for top in os.listdir(possible_dir): |
235 | self.rm_rf(os.path.join(possible_dir, top)) |
236 | |
237 | +<<<<<<< TREE |
238 | def do_merge(self, from_repo_revision, to_repo_revision, merge_name, |
239 | merge_method, line): |
240 | # Record the start time for elapsed time calculation. |
241 | @@ -688,6 +743,8 @@ |
242 | # Launchpad notices merges |
243 | self.reported = True |
244 | |
245 | +======= |
246 | +>>>>>>> MERGE-SOURCE |
247 | def get_arch_impl(self): |
248 | # TODO: Tim Penhey, 2008-05-28 |
249 | # Get rid of the imports from pqm module itself. |
250 | @@ -946,6 +1003,7 @@ |
251 | def run(self): |
252 | super(MergeCommand, self).run() |
253 | self.cleanup_wd() |
254 | +<<<<<<< TREE |
255 | self.lp_mp = None |
256 | if self.launchpad is None: |
257 | self.from_branch_lp = None |
258 | @@ -988,6 +1046,112 @@ |
259 | self.manager.logger.error("Exception handling merge", exc_info=1) |
260 | self.fail_operation("Exception processing merge: %s" % e) |
261 | return CommandResult(self.successful, self.unrecognized, self.output, not self.reported) |
262 | +======= |
263 | + self.do_merge(from_repo_revision=self.from_branch, |
264 | + to_repo_revision=self.to_branch, |
265 | + merge_name='star-merge', |
266 | + line='merge %s %s' % (self.from_branch, self.to_branch)) |
267 | + return self.successful, self.unrecognized, self.output |
268 | +>>>>>>> MERGE-SOURCE |
269 | + |
270 | + def _do_merge(self, sender, dir): |
271 | + return self.get_vcs().do_star_merge(sender, self.from_branch, dir) |
272 | + |
273 | + def do_merge(self, from_repo_revision, to_repo_revision, merge_name, |
274 | + line): |
275 | + sender = self.script.getSender() |
276 | + # Star-merge |
277 | + self.check_target(to_repo_revision, line) |
278 | + to_repo_revision, config = self.getBranchConfig(to_repo_revision) |
279 | + self.set_current_vcs(from_repo_revision, to_repo_revision) |
280 | + self.validate_revision(from_repo_revision) |
281 | + self.validate_revision(to_repo_revision) |
282 | + self.check_commit_regex(to_repo_revision, config) |
283 | + self.script.logger.info("current cwd is %s", os.getcwd()) |
284 | + self.script.logger.info("getting working dir for %s", to_repo_revision) |
285 | + dir = self.get_wd(sender, to_repo_revision, config) |
286 | + origdir = os.getcwd() |
287 | + merge_line = 'Executing %s %s at %s' % (merge_name, |
288 | + from_repo_revision, |
289 | + time.strftime('%c')) |
290 | + self.log_with_status(self.script.logger, merge_line) |
291 | + self.output += [ |
292 | + '\n', |
293 | + merge_line, |
294 | + '\n', |
295 | + ] |
296 | + self.wrap_command(self._do_merge, line, sender, dir) |
297 | + self.run_precommit(to_repo_revision, config, line, dir) |
298 | + os.chdir(origdir) |
299 | + self.log_with_status(self.script.logger, "success: %s", line) |
300 | + self.successful.append(line) |
301 | + self.output += ['\n', '%s succeeded at %s' % (merge_name, time.strftime('%c')), '\n'] |
302 | + self.get_vcs().commit(sender, dir, self.commitmsg, to_repo_revision, config) |
303 | + |
304 | + |
305 | +class ExtendedMergeCommand(MergeCommand): |
306 | + |
307 | + def __init__(self, script, branch_spec_handler, configp, revision_id, |
308 | + testament_sha1, message, to_branch, from_branch=None, |
309 | + bundle=None): |
310 | + |
311 | + MergeCommand.__init__(self, script, branch_spec_handler, configp, |
312 | + from_branch, to_branch) |
313 | + self.revision_id = revision_id |
314 | + self.testament_sha1 = testament_sha1 |
315 | + self.message = message |
316 | + self.from_branch = from_branch |
317 | + self.bundle = bundle |
318 | + |
319 | + def asHTML(self): |
320 | + print self.from_branch |
321 | + if self.from_branch is not None: |
322 | + s = " (%s)" % (str(self.from_branch),) |
323 | + else: |
324 | + s = "" |
325 | + return cgi.escape("Merge from bundle%s" % (s,)) |
326 | + |
327 | + def run(self): |
328 | + super(MergeCommand, self).run() |
329 | + self.cleanup_wd() |
330 | + if self.message is not None: |
331 | + self.commitmsg = self.message |
332 | + line = 'merge %s %s' % (self.from_branch, self.to_branch) |
333 | + self.do_merge(self.from_branch, self.to_branch, 'bzr-merge', line) |
334 | + return self.successful, self.unrecognized, self.output |
335 | + |
336 | + def _do_merge(self, sender, dir): |
337 | + return self.get_vcs().do_bzr_merge(sender, self.revision_id, |
338 | + self.testament_sha1, dir, |
339 | + self.from_branch, self.bundle) |
340 | + |
341 | + def __eq__(self, other): |
342 | + return (super(ExtendedMergeCommand, self).__eq__(other) and |
343 | + isinstance(other, ExtendedMergeCommand) and |
344 | + self.revision_id == other.revision_id and |
345 | + self.testament_sha1 == other.testament_sha1 and |
346 | + self.message == other.message and |
347 | + self.bundle == other.bundle) |
348 | + |
349 | + |
350 | +class ReplayMergeCommand(MergeCommand): |
351 | + |
352 | + def asHTML(self): |
353 | + return cgi.escape("Replay %s %s" % (self.from_branch, |
354 | + self.to_branch)) |
355 | + |
356 | + def run(self): |
357 | + super(MergeCommand, self).run() |
358 | + self.cleanup_wd() |
359 | + self.do_merge(from_repo_revision=self.from_branch, |
360 | + to_repo_revision=self.to_branch, |
361 | + merge_name='replay', |
362 | + line='replay %s %s' % (self.from_branch, |
363 | + self.to_branch)) |
364 | + return self.successful, self.unrecognized, self.output |
365 | + |
366 | + def _do_merge(self, sender, dir): |
367 | + return self.get_vcs().do_replay(sender, self.from_branch, dir) |
368 | |
369 | |
370 | class PrecommitCommand(Command): |
371 | @@ -1121,7 +1285,6 @@ |
372 | |
373 | class CommandRunner(Command): |
374 | """This runs commands as lines one at a time.""" |
375 | - replay_re = re.compile('^replay (\S+/\S+)\s+(\S+/\S+)\s*$') |
376 | repo_cache_re = re.compile('^repo-cache-revision (\S+/\S+)\s*$') |
377 | repo_uncache_re = re.compile('^repo-uncache-revision (\S+/\S+)\s*$') |
378 | tag_re = re.compile('^tag (\S+/\S+)\s+(\S+/\S+)\s*$') |
379 | @@ -1204,7 +1367,6 @@ |
380 | # its a command of some sort |
381 | start_time = datetime.now() |
382 | patch_match = self.patch_re.match(line) |
383 | - replay_match = self.replay_re.match(line) |
384 | repo_cache_match=self.repo_cache_re.match(line) |
385 | repo_uncache_match=self.repo_uncache_re.match(line) |
386 | tag_match=self.tag_re.match(line) |
387 | @@ -1221,12 +1383,6 @@ |
388 | self.accumulating_patch = True |
389 | elif self.accumulating_patch: |
390 | self.patch_content.append(line) |
391 | - elif replay_match: |
392 | - self.do_merge(from_repo_revision=replay_match.group(1), |
393 | - to_repo_revision=replay_match.group(2), |
394 | - merge_name='replay', |
395 | - merge_method="do_replay", |
396 | - line=line) |
397 | elif repo_cache_match: |
398 | # Cache a revision |
399 | repo_revision = repo_cache_match.group(1) |
400 | |
401 | === modified file 'pqm/tests/test_pqm.py' |
402 | --- pqm/tests/test_pqm.py 2010-04-19 02:40:26 +0000 |
403 | +++ pqm/tests/test_pqm.py 2010-05-04 16:39:23 +0000 |
404 | @@ -12,9 +12,18 @@ |
405 | from pqm.core import PatchQueueManager |
406 | from pqm.errors import PQMCmdFailure, PQMException |
407 | from pqm.PQMConfigParser import ConfigParser |
408 | +<<<<<<< TREE |
409 | from pqm.script import ( |
410 | Command, CommandRunner, DebugCommand, EmailScript, MergeCommand) |
411 | |
412 | +======= |
413 | +from pqm.script import (Command, |
414 | + CommandRunner, |
415 | + DebugCommand, |
416 | + ExtendedMergeCommand, |
417 | + MergeCommand, |
418 | + ) |
419 | +>>>>>>> MERGE-SOURCE |
420 | |
421 | sample_message = dedent("""\ |
422 | From: John.Citizen@example.com |
423 | @@ -117,6 +126,51 @@ |
424 | workdir=test-workdir |
425 | """) |
426 | |
427 | +sample_merge_directive = """\ |
428 | +From John.Citizen@example.com Sun Mar 11 20:19:38 2007 |
429 | +Return-Path: <John.Citizen@example.com> |
430 | +X-Original-To: abentley |
431 | +Delivered-To: abentley@localhost.localdomain |
432 | +Received: from localhost.localdomain (localhost.localdomain [127.0.0.1]) |
433 | + by localhost.localdomain (Postfix) with ESMTP id 6E01032E9F5 |
434 | + for <abentley>; Sun, 11 Mar 2007 20:19:38 -0400 (EDT) |
435 | +To: abentley@localhost.localdomain |
436 | +From: John Citizen <John.Citizen@example.com> |
437 | +Subject: This is my second commit message |
438 | +Message-Id: <20070312001938.6E01032E9F5@localhost.localdomain> |
439 | +Date: Sun, 11 Mar 2007 20:19:38 -0400 (EDT) |
440 | + |
441 | +# Bazaar merge directive format 1 |
442 | +# revision_id: john.citizen@example.com-20070312001559-\\ |
443 | +# 9jdb99jgb8dn1k5b |
444 | +# message: Please merge this commit |
445 | +# target_branch: http://example.com/target |
446 | +# source_branch: http://example.com/source |
447 | +# testament_sha1: 5cd628553ec98c056bd7eff4b8b72fce9961c53b |
448 | +# timestamp: 2007-03-11 20:19:37 -0400 |
449 | +# |
450 | +# Bazaar revision bundle v0.9 |
451 | +# |
452 | +# message: |
453 | +# This is my second commit message |
454 | +# committer: John Citizen <John.Citizen@example.com> |
455 | +# date: Sun 2007-03-11 20:15:59.841000080 -0400 |
456 | + |
457 | +=== modified directory // last-changed:john.citizen@example.com-20070312001559 |
458 | +... -9jdb99jgb8dn1k5b |
459 | +# revision id: john.citizen@example.com-20070312001559-9jdb99jgb8dn1k5b |
460 | +# sha1: 5cd628553ec98c056bd7eff4b8b72fce9961c53b |
461 | +# inventory sha1: 191c819cc7943e5e4140744cc04a7fe088897f35 |
462 | +# parent ids: |
463 | +# john.citizen@example.com-20070312001321-qbz0l8mrp0csql1e |
464 | +# base id: john.citizen@example.com-20070312001321-qbz0l8mrp0csql1e |
465 | +# properties: |
466 | +# branch-nick: example |
467 | + |
468 | + |
469 | +""" |
470 | +sample_bundle = ''.join(sample_merge_directive.splitlines(True)[22:]) |
471 | + |
472 | |
473 | class QueueSetup(object): |
474 | """Setup a queue with mock messages in it.""" |
475 | @@ -198,6 +252,14 @@ |
476 | return EmailScript(self.scriptname, logging, False, 54, handler, |
477 | configp, self.queue.manager) |
478 | |
479 | + |
480 | +class TestQueueDirectory(TestWithQueueDirectory): |
481 | + |
482 | + def testName(self): |
483 | + patch = pqm.Script('foo.script', logging, False, 0, None, None) |
484 | + self.assertEqual(patch.filename, 'foo.script') |
485 | + self.scriptname = 'fpp' |
486 | + |
487 | def testFields(self): |
488 | script = self.getScript(sample_message) |
489 | self.assertEqual(script.getSender(), "John.Citizen@example.com") |
490 | @@ -216,6 +278,26 @@ |
491 | self.queue.manager)], |
492 | script.getCommands()) |
493 | |
494 | + def testMergeDirectiveFields(self): |
495 | + script = self.getScript(sample_merge_directive) |
496 | + self.assertEqual(script.getSender(), |
497 | + "John Citizen <John.Citizen@example.com>") |
498 | + self.assertEqual(script.getSubject(), |
499 | + "This is my second commit message") |
500 | + commands = script.getCommands() |
501 | + self.assertEqual([ExtendedMergeCommand( |
502 | + None, |
503 | + None, |
504 | + None, |
505 | + 'john.citizen@example.com-20070312001559-9jdb99jgb8dn1k5b', |
506 | + '5cd628553ec98c056bd7eff4b8b72fce9961c53b', |
507 | + 'Please merge this commit', |
508 | + 'http://example.com/target', |
509 | + 'http://example.com/source', |
510 | + sample_bundle, |
511 | + )], |
512 | + script.getCommands()) |
513 | + |
514 | def testGPGFields(self): |
515 | script = self.getScript(sample_signed_message) |
516 | self.assertEqual(script.getSender(), "whee@bar.com (Matthew Thomas)") |
517 | @@ -273,7 +355,6 @@ |
518 | self.assertEqual('bar\nfoo\n', content) |
519 | |
520 | |
521 | - |
522 | # NOTDONEYET: move command recognition from CommandRunner to Script |
523 | # and commands should follow the command pattern rather than being strings. |
524 | # |
525 | @@ -427,9 +508,10 @@ |
526 | self.assertTrue(script.debug) |
527 | |
528 | |
529 | -class FunctionalTestCommandRunner(unittest.TestCase): |
530 | +class FunctionalTestCommandRunner(TestCaseWithTransport): |
531 | |
532 | def setUp(self): |
533 | + super(FunctionalTestCommandRunner, self).setUp() |
534 | from bzrlib.bzrdir import BzrDir |
535 | from bzrlib.plugin import load_plugins |
536 | load_plugins() |
537 | @@ -491,14 +573,18 @@ |
538 | load_plugins() |
539 | tree = self.make_branch_and_tree("bzrbranch") |
540 | tree.smart_add(["bzrbranch"]) |
541 | - tree.commit("start branch.", verbose=False) |
542 | + tree.commit("start branch.", verbose=False, rev_id='revision-0') |
543 | tree.branch.bzrdir.sprout("bzrbranch-parent") |
544 | tree.branch.bzrdir.sprout("bzrbranch-public") |
545 | tree.branch.bzrdir.sprout("branch-contributor") |
546 | contrib_tree = BzrDir.open("branch-contributor").open_workingtree() |
547 | + self.build_tree_contents([("branch-contributor/NEWS", |
548 | + "This is brand-new\n")]) |
549 | + contrib_tree.add(["NEWS"]) |
550 | + contrib_tree.commit("add NEWS", rev_id='revision-1') |
551 | self.build_tree_contents([("branch-contributor/README", "Boo!\n")]) |
552 | contrib_tree.add(["README"]) |
553 | - contrib_tree.commit("add README") |
554 | + contrib_tree.commit("add README", rev_id='revision-2') |
555 | |
556 | def tearDown(self): |
557 | shutil.rmtree("bzrbranch") |
558 | @@ -507,6 +593,9 @@ |
559 | shutil.rmtree("bzrbranch-public") |
560 | super(BzrHandlerTestCase, self).tearDown() |
561 | |
562 | + |
563 | +class TestBzrHandler(BzrHandlerTestCase): |
564 | + |
565 | def test_commit(self): |
566 | from bzrlib.branch import Branch |
567 | branch = Branch.open("bzrbranch") |
568 | @@ -569,6 +658,66 @@ |
569 | contrib_branch.last_revision()).message, |
570 | 'add README') |
571 | self.failUnless(os.path.exists("bzrbranch/README")) |
572 | + |
573 | + def test_merge_revision(self): |
574 | + from bzrlib.branch import Branch |
575 | + from bzrlib.testament import StrictTestament3 |
576 | + branch = Branch.open("bzrbranch") |
577 | + self.assertEqual( |
578 | + branch.repository.get_revision(branch.last_revision()).message, |
579 | + 'start branch.') |
580 | + contrib_branch = Branch.open("branch-contributor") |
581 | + self.assertEqual(contrib_branch.repository.get_revision( |
582 | + contrib_branch.last_revision()).message, |
583 | + 'add README') |
584 | + self.failIf(os.path.exists("bzrbranch/README")) |
585 | + self.failIf(os.path.exists("bzrbranch/NEWS")) |
586 | + handler = pqm.Bazaar2Handler() |
587 | + t = StrictTestament3.from_revision(contrib_branch.repository, |
588 | + 'revision-2') |
589 | + self.assertRaises(pqm.PQMTlaFailure, handler.do_bzr_merge, "me", |
590 | + 'revision-1', t.as_sha1(), "bzrbranch", |
591 | + "branch-contributor",) |
592 | + t = StrictTestament3.from_revision(contrib_branch.repository, |
593 | + 'revision-1') |
594 | + self.assertRaises(pqm.PQMTlaFailure, handler.do_bzr_merge, "me", |
595 | + 'revision-1', t.as_sha1(), "bzrbranch") |
596 | + result = handler.do_bzr_merge("me", 'revision-1', t.as_sha1(), |
597 | + "bzrbranch", 'branch-contributor') |
598 | + self.assertEqual(result, ["merge successful"]) |
599 | + branch = Branch.open("bzrbranch") |
600 | + self.assertEqual( |
601 | + branch.repository.get_revision(branch.last_revision()).message, |
602 | + 'start branch.') |
603 | + contrib_branch = Branch.open("branch-contributor") |
604 | + self.assertEqual(contrib_branch.repository.get_revision( |
605 | + contrib_branch.last_revision()).message, |
606 | + 'add README') |
607 | + self.failIf(os.path.exists("bzrbranch/README")) |
608 | + self.failUnless(os.path.exists("bzrbranch/NEWS")) |
609 | + |
610 | + def test_merge_bundle(self): |
611 | + from bzrlib.branch import Branch |
612 | + from bzrlib.bundle.serializer import write_bundle |
613 | + from bzrlib.testament import StrictTestament3 |
614 | + branch = Branch.open("bzrbranch") |
615 | + self.assertEqual( |
616 | + branch.repository.get_revision(branch.last_revision()).message, |
617 | + 'start branch.') |
618 | + contrib_branch = Branch.open("branch-contributor") |
619 | + self.assertEqual(contrib_branch.repository.get_revision( |
620 | + contrib_branch.last_revision()).message, |
621 | + 'add README') |
622 | + self.failIf(os.path.exists("bzrbranch/README")) |
623 | + self.failIf(os.path.exists("bzrbranch/NEWS")) |
624 | + t = StrictTestament3.from_revision(contrib_branch.repository, |
625 | + 'revision-1') |
626 | + bundle = StringIO() |
627 | + write_bundle(contrib_branch.repository, 'revision-2', 'revision-0', |
628 | + bundle) |
629 | + handler = pqm.Bazaar2Handler() |
630 | + result = handler.do_bzr_merge("me", 'revision-1', t.as_sha1(), |
631 | + "bzrbranch", bundle=bundle.getvalue()) |
632 | |
633 | def test_merge_conflicts(self): |
634 | from bzrlib.workingtree import WorkingTree |
635 | @@ -608,10 +757,17 @@ |
636 | try: |
637 | handler.do_star_merge("me", "unrelated", "bzrbranch") |
638 | except pqm.PQMTlaFailure, e: |
639 | +<<<<<<< TREE |
640 | self.assertEqual( |
641 | ["Branches have no common ancestor, and no merge base" |
642 | " revision was specified."], |
643 | e.output) |
644 | +======= |
645 | + self.assertEqual(["Conflicts during merge: 1", |
646 | + "Text conflict in README", |
647 | + ], |
648 | + e.output) |
649 | +>>>>>>> MERGE-SOURCE |
650 | return |
651 | self.fail("Merge base error not raised.") |
652 | |
653 | @@ -622,6 +778,47 @@ |
654 | self.assertEqual('start branch.', message) |
655 | |
656 | |
657 | +class TestBlackbox(BzrHandlerTestCase, TestWithQueueDirectory): |
658 | + |
659 | + def setUp(self): |
660 | + BzrHandlerTestCase.setUp(self) |
661 | + TestWithQueueDirectory.setUp(self) |
662 | + |
663 | + def test_blackbox(self): |
664 | + from bzrlib.branch import Branch |
665 | + from bzrlib.bundle.serializer import write_bundle |
666 | + from bzrlib.merge_directive import MergeDirective |
667 | + from bzrlib.testament import StrictTestament3 |
668 | + branch = Branch.open("bzrbranch") |
669 | + self.assertEqual( |
670 | + branch.repository.get_revision(branch.last_revision()).message, |
671 | + 'start branch.') |
672 | + contrib_branch = Branch.open("branch-contributor") |
673 | + self.assertEqual(contrib_branch.repository.get_revision( |
674 | + contrib_branch.last_revision()).message, |
675 | + 'add README') |
676 | + repo = contrib_branch.repository |
677 | + repo.lock_write() |
678 | + try: |
679 | + directive = MergeDirective.from_objects(repo, |
680 | + contrib_branch.last_revision(), 0, 0, |
681 | + branch.bzrdir.root_transport.base) |
682 | + script = self.getScript(''.join(directive.to_lines())) |
683 | + self.failIf(os.path.exists("bzrbranch/README")) |
684 | + self.failIf(os.path.exists("bzrbranch/NEWS")) |
685 | + t = StrictTestament3.from_revision(contrib_branch.repository, |
686 | + 'revision-1') |
687 | + bundle = StringIO() |
688 | + write_bundle(contrib_branch.repository, 'revision-2', |
689 | + 'revision-0', bundle) |
690 | + handler = pqm.Bazaar2Handler() |
691 | + result = handler.do_bzr_merge("me", 'revision-1', t.as_sha1(), |
692 | + "bzrbranch", |
693 | + bundle=bundle.getvalue()) |
694 | + finally: |
695 | + repo.unlock() |
696 | + |
697 | + |
698 | class TestConfig(unittest.TestCase): |
699 | |
700 | def test_location_overrides(self): |
701 | |
702 | === modified file 'tests/Makefile.am' |
703 | --- tests/Makefile.am 2005-12-19 08:02:19 +0000 |
704 | +++ tests/Makefile.am 2010-05-04 16:39:23 +0000 |
705 | @@ -13,7 +13,8 @@ |
706 | $(srcdir)/simple-merge-2.sh \ |
707 | $(srcdir)/simple-merge-3.sh \ |
708 | $(srcdir)/simple-merge-4.sh \ |
709 | - $(srcdir)/bzr-merge.sh |
710 | + $(srcdir)/bzr-merge.sh \ |
711 | + $(srcdir)/bzr-merge-2.sh |
712 | |
713 | EXTRA_DIST = test-framework pqm-tests.conf pqm-tests-2.conf \ |
714 | pqm-tests-3.conf pqm-tests-4.conf \ |
715 | |
716 | === added file 'tests/bzr-merge-2.sh' |
717 | --- tests/bzr-merge-2.sh 1970-01-01 00:00:00 +0000 |
718 | +++ tests/bzr-merge-2.sh 2010-05-04 16:39:23 +0000 |
719 | @@ -0,0 +1,20 @@ |
720 | +#!/bin/sh |
721 | +# -*- mode: sh; coding: utf-8 -*- |
722 | +# Copyright © 2005 Canonical Limited |
723 | +# Authors: Robert Collins <robert@canonical.com> |
724 | +# |
725 | +# See the file "COPYING" for further information about |
726 | +# the copyright and warranty status of this work. |
727 | + |
728 | +set -e |
729 | + |
730 | +srcdir=$(cd ${srcdir} && pwd) |
731 | + |
732 | +. ${srcdir}/test-framework |
733 | + |
734 | +PQM_CONFIG_FILE="${srcdir}/pqm-tests-2.conf" |
735 | +initial_bzr_setup_with_plain_merge_directive |
736 | +run_queue |
737 | +bzr_archive_has_revision_with_summary "hello-world/mainline/1.0" 2 'minor rename' |
738 | + |
739 | +clean_workdir |
740 | |
741 | === modified file 'tests/test-framework' |
742 | --- tests/test-framework 2010-04-13 07:40:54 +0000 |
743 | +++ tests/test-framework 2010-05-04 16:39:23 +0000 |
744 | @@ -268,13 +268,13 @@ |
745 | (echo "From: ${USERID}"; echo "Subject: ${subj}"; echo) | pqm --no-verify --read "$@" |
746 | else |
747 | if test ${type} = "verify"; then |
748 | - tmpf="${WORKDIR}/sig.$$" |
749 | - HOME=${srcdir} gpg --batch --clearsign --passphrase-fd 2 2</dev/null > "${tmpf}" |
750 | - (echo "From: ${USERID}"; echo "Subject: ${subj}"; echo; cat < "${tmpf}") | pqm --read "$@" |
751 | - rm -f "${tmpf}" |
752 | + tmpf="${WORKDIR}/sig.$$" |
753 | + HOME=${srcdir} gpg --batch --clearsign --passphrase-fd 2 2</dev/null > "${tmpf}" |
754 | + (echo "From: ${USERID}"; echo "Subject: ${subj}"; echo; cat < "${tmpf}") | pqm --read "$@" |
755 | + rm -f "${tmpf}" |
756 | else |
757 | - echo "Unknown verification type ${type}!" 1>&2 |
758 | - exit 1 |
759 | + echo "Unknown verification type ${type}!" 1>&2 |
760 | + exit 1 |
761 | fi |
762 | fi |
763 | endverbose |
764 | @@ -486,6 +486,20 @@ |
765 | endverbose |
766 | } |
767 | |
768 | + |
769 | +initial_bzr_setup_with_plain_merge_directive () |
770 | +{ |
771 | + initial_bzr_setup |
772 | + identity_user1 |
773 | + verbose 'doing simple submission' |
774 | + cd ${WORKDIR}/hello-world |
775 | + pqm_bzr rename hello-world.c hello_world.c |
776 | + pqm_bzr commit -m 'rename hello-world => hello_world.c' |
777 | + pqm_bzr merge-directive --plain ${PQM_ARCHIVE_DIR}/hello-world/mainline/1.0 `pwd` | submit_merge 'minor rename' "verify" |
778 | + endverbose |
779 | +} |
780 | + |
781 | + |
782 | import_bzr_hello_world () { |
783 | branch=$1 |
784 | shift |
785 | @@ -506,7 +520,7 @@ |
786 | archive=$1 |
787 | revision=$2 |
788 | summary=$3 |
789 | - verbose "checking for revision \"$1\" in archive \"$2\"" |
790 | + verbose "checking for revision \"$2\" in archive \"$1\"" |
791 | output=$(pqm_bzr log --message "$summary" ${PQM_ARCHIVE_DIR}/${archive} | grep "revno") |
792 | test "$output" = "revno: $revision [merge]" |
793 | endverbose |
overall good.
A few issues:
- uses more shell tests rather than python unit tests
- the new Command class won't render correctly in the web GUI
- I don't like the merge_callback lambda usage, its unobvious and doesn't seem necessary.
overall - resubmit