~bzr-pqm/bzr/bzr.dev

1551.12.36 by Aaron Bentley
Fix failing tests
1
# Copyright (C) 2007 Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
18
from StringIO import StringIO
2520.4.105 by Aaron Bentley
Implement patch verification
19
import re
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
20
21
from bzrlib import (
1551.12.5 by Aaron Bentley
Get MergeDirective.from_objects working
22
    branch as _mod_branch,
23
    diff,
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
24
    errors,
1551.12.16 by Aaron Bentley
Enable signing merge directives
25
    gpg,
2520.4.73 by Aaron Bentley
Implement new merge directive format
26
    registry,
1551.12.5 by Aaron Bentley
Get MergeDirective.from_objects working
27
    revision as _mod_revision,
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
28
    rio,
1551.12.5 by Aaron Bentley
Get MergeDirective.from_objects working
29
    testament,
1551.12.30 by Aaron Bentley
Use patch-style dates for timestamps in merge directives
30
    timestamp,
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
31
    )
1551.14.4 by Aaron Bentley
Change bundle reader and merge directive to both be 'mergeables'
32
from bzrlib.bundle import (
33
    serializer as bundle_serializer,
34
    )
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
35
from bzrlib.email_message import EmailMessage
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
36
37
2520.4.73 by Aaron Bentley
Implement new merge directive format
38
class _BaseMergeDirective(object):
39
40
    def __init__(self, revision_id, testament_sha1, time, timezone,
41
                 target_branch, patch=None, source_branch=None, message=None,
42
                 bundle=None):
43
        """Constructor.
44
45
        :param revision_id: The revision to merge
46
        :param testament_sha1: The sha1 of the testament of the revision to
47
            merge.
48
        :param time: The current POSIX timestamp time
49
        :param timezone: The timezone offset
50
        :param target_branch: The branch to apply the merge to
51
        :param patch: The text of a diff or bundle
52
        :param source_branch: A public location to merge the revision from
53
        :param message: The message to use when committing this merge
54
        """
55
        self.revision_id = revision_id
56
        self.testament_sha1 = testament_sha1
57
        self.time = time
58
        self.timezone = timezone
59
        self.target_branch = target_branch
60
        self.patch = patch
61
        self.source_branch = source_branch
62
        self.message = message
63
2520.4.105 by Aaron Bentley
Implement patch verification
64
    def _to_lines(self, base_revision=False):
2520.4.73 by Aaron Bentley
Implement new merge directive format
65
        """Serialize as a list of lines
66
67
        :return: a list of lines
68
        """
69
        time_str = timestamp.format_patch_date(self.time, self.timezone)
70
        stanza = rio.Stanza(revision_id=self.revision_id, timestamp=time_str,
71
                            target_branch=self.target_branch,
72
                            testament_sha1=self.testament_sha1)
73
        for key in ('source_branch', 'message'):
74
            if self.__dict__[key] is not None:
75
                stanza.add(key, self.__dict__[key])
2520.4.105 by Aaron Bentley
Implement patch verification
76
        if base_revision:
77
            stanza.add('base_revision_id', self.base_revision_id)
2520.4.73 by Aaron Bentley
Implement new merge directive format
78
        lines = ['# ' + self._format_string + '\n']
79
        lines.extend(rio.to_patch_lines(stanza))
80
        lines.append('# \n')
81
        return lines
82
83
    @classmethod
84
    def from_objects(klass, repository, revision_id, time, timezone,
85
                 target_branch, patch_type='bundle',
86
                 local_target_branch=None, public_branch=None, message=None):
87
        """Generate a merge directive from various objects
88
89
        :param repository: The repository containing the revision
90
        :param revision_id: The revision to merge
91
        :param time: The POSIX timestamp of the date the request was issued.
92
        :param timezone: The timezone of the request
93
        :param target_branch: The url of the branch to merge into
94
        :param patch_type: 'bundle', 'diff' or None, depending on the type of
95
            patch desired.
96
        :param local_target_branch: a local copy of the target branch
97
        :param public_branch: location of a public branch containing the target
98
            revision.
99
        :param message: Message to use when committing the merge
100
        :return: The merge directive
101
102
        The public branch is always used if supplied.  If the patch_type is
103
        not 'bundle', the public branch must be supplied, and will be verified.
104
105
        If the message is not supplied, the message from revision_id will be
106
        used for the commit.
107
        """
108
        t_revision_id = revision_id
2520.4.86 by Aaron Bentley
Improve locking in _BaseMergeDirective.from_object
109
        if revision_id == _mod_revision.NULL_REVISION:
2520.4.73 by Aaron Bentley
Implement new merge directive format
110
            t_revision_id = None
111
        t = testament.StrictTestament3.from_revision(repository, t_revision_id)
112
        submit_branch = _mod_branch.Branch.open(target_branch)
113
        if submit_branch.get_public_branch() is not None:
114
            target_branch = submit_branch.get_public_branch()
115
        if patch_type is None:
116
            patch = None
117
        else:
118
            submit_revision_id = submit_branch.last_revision()
119
            submit_revision_id = _mod_revision.ensure_null(submit_revision_id)
120
            repository.fetch(submit_branch.repository, submit_revision_id)
121
            graph = repository.get_graph()
122
            ancestor_id = graph.find_unique_lca(revision_id,
123
                                                submit_revision_id)
124
            type_handler = {'bundle': klass._generate_bundle,
125
                            'diff': klass._generate_diff,
126
                            None: lambda x, y, z: None }
127
            patch = type_handler[patch_type](repository, revision_id,
128
                                             ancestor_id)
129
2520.5.4 by Aaron Bentley
Replace 'bundle-revisions' with 'submit' command
130
        if public_branch is not None and patch_type != 'bundle':
131
            public_branch_obj = _mod_branch.Branch.open(public_branch)
132
            if not public_branch_obj.repository.has_revision(revision_id):
133
                raise errors.PublicBranchOutOfDate(public_branch,
134
                                                   revision_id)
2520.4.73 by Aaron Bentley
Implement new merge directive format
135
136
        return klass(revision_id, t.as_sha1(), time, timezone, target_branch,
137
            patch, patch_type, public_branch, message)
138
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
139
    def get_disk_name(self, branch):
140
        """Generate a suitable basename for storing this directive on disk
141
142
        :param branch: The Branch this merge directive was generated fro
143
        :return: A string
144
        """
145
        revno, revision_id = branch.last_revision_info()
146
        if self.revision_id == revision_id:
3251.2.2 by Aaron Bentley
Fix bug in last revno handling
147
            revno = [revno]
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
148
        else:
149
            revno = branch.get_revision_id_to_revno_map().get(self.revision_id,
150
                ['merge'])
151
        return '%s-%s' % (branch.nick, '.'.join(str(n) for n in revno))
152
2520.4.73 by Aaron Bentley
Implement new merge directive format
153
    @staticmethod
154
    def _generate_diff(repository, revision_id, ancestor_id):
155
        tree_1 = repository.revision_tree(ancestor_id)
156
        tree_2 = repository.revision_tree(revision_id)
157
        s = StringIO()
158
        diff.show_diff_trees(tree_1, tree_2, s, old_label='', new_label='')
159
        return s.getvalue()
160
161
    @staticmethod
162
    def _generate_bundle(repository, revision_id, ancestor_id):
163
        s = StringIO()
164
        bundle_serializer.write_bundle(repository, revision_id,
165
                                       ancestor_id, s)
166
        return s.getvalue()
167
2520.4.80 by Aaron Bentley
Improve merge directive tests
168
    def to_signed(self, branch):
169
        """Serialize as a signed string.
170
171
        :param branch: The source branch, to get the signing strategy
172
        :return: a string
173
        """
174
        my_gpg = gpg.GPGStrategy(branch.get_config())
175
        return my_gpg.sign(''.join(self.to_lines()))
176
177
    def to_email(self, mail_to, branch, sign=False):
178
        """Serialize as an email message.
179
180
        :param mail_to: The address to mail the message to
181
        :param branch: The source branch, to get the signing strategy and
182
            source email address
183
        :param sign: If True, gpg-sign the email
184
        :return: an email message
185
        """
186
        mail_from = branch.get_config().username()
187
        if self.message is not None:
2625.6.2 by Adeodato Simó
Merge bzr.dev, resolving conflicts and updating test_merge_directive.py.
188
            subject = self.message
2520.4.80 by Aaron Bentley
Improve merge directive tests
189
        else:
190
            revision = branch.repository.get_revision(self.revision_id)
2625.6.2 by Adeodato Simó
Merge bzr.dev, resolving conflicts and updating test_merge_directive.py.
191
            subject = revision.message
2520.4.80 by Aaron Bentley
Improve merge directive tests
192
        if sign:
193
            body = self.to_signed(branch)
194
        else:
195
            body = ''.join(self.to_lines())
2625.6.2 by Adeodato Simó
Merge bzr.dev, resolving conflicts and updating test_merge_directive.py.
196
        message = EmailMessage(mail_from, mail_to, subject, body)
2520.4.80 by Aaron Bentley
Improve merge directive tests
197
        return message
198
199
    def install_revisions(self, target_repo):
200
        """Install revisions and return the target revision"""
201
        if not target_repo.has_revision(self.revision_id):
202
            if self.patch_type == 'bundle':
203
                info = bundle_serializer.read_bundle(
204
                    StringIO(self.get_raw_bundle()))
205
                # We don't use the bundle's target revision, because
206
                # MergeDirective.revision_id is authoritative.
1551.19.19 by Aaron Bentley
Merge directives can now fetch prerequisites from the target branch
207
                try:
208
                    info.install_revisions(target_repo, stream_input=False)
209
                except errors.RevisionNotPresent:
210
                    # At least one dependency isn't present.  Try installing
211
                    # missing revisions from the submit branch
1551.19.20 by Aaron Bentley
Updates from review
212
                    submit_branch = _mod_branch.Branch.open(self.target_branch)
213
                    missing_revisions = []
214
                    bundle_revisions = set(r.revision_id for r in
215
                                           info.real_revisions)
1551.19.19 by Aaron Bentley
Merge directives can now fetch prerequisites from the target branch
216
                    for revision in info.real_revisions:
217
                        for parent_id in revision.parent_ids:
1551.19.20 by Aaron Bentley
Updates from review
218
                            if (parent_id not in bundle_revisions and
1551.19.19 by Aaron Bentley
Merge directives can now fetch prerequisites from the target branch
219
                                not target_repo.has_revision(parent_id)):
220
                                missing_revisions.append(parent_id)
1551.19.20 by Aaron Bentley
Updates from review
221
                    # reverse missing revisions to try to get heads first
222
                    unique_missing = []
223
                    unique_missing_set = set()
224
                    for revision in reversed(missing_revisions):
225
                        if revision in unique_missing_set:
226
                            continue
227
                        unique_missing.append(revision)
228
                        unique_missing_set.add(revision)
229
                    for missing_revision in unique_missing:
1551.19.19 by Aaron Bentley
Merge directives can now fetch prerequisites from the target branch
230
                        target_repo.fetch(submit_branch.repository,
231
                                          missing_revision)
232
                    info.install_revisions(target_repo, stream_input=False)
2520.4.80 by Aaron Bentley
Improve merge directive tests
233
            else:
234
                source_branch = _mod_branch.Branch.open(self.source_branch)
235
                target_repo.fetch(source_branch.repository, self.revision_id)
236
        return self.revision_id
237
2520.4.73 by Aaron Bentley
Implement new merge directive format
238
239
class MergeDirective(_BaseMergeDirective):
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
240
1551.12.38 by Aaron Bentley
Add docs for MergeDirective and RIO-patch functions
241
    """A request to perform a merge into a branch.
242
243
    Designed to be serialized and mailed.  It provides all the information
244
    needed to perform a merge automatically, by providing at minimum a revision
245
    bundle or the location of a branch.
246
247
    The serialization format is robust against certain common forms of
248
    deterioration caused by mailing.
249
250
    The format is also designed to be patch-compatible.  If the directive
251
    includes a diff or revision bundle, it should be possible to apply it
252
    directly using the standard patch program.
253
    """
254
1551.12.45 by Aaron Bentley
Change format marker to not experimental
255
    _format_string = 'Bazaar merge directive format 1'
1551.12.12 by Aaron Bentley
Add format header
256
1551.12.4 by Aaron Bentley
Add failing test
257
    def __init__(self, revision_id, testament_sha1, time, timezone,
1551.12.13 by Aaron Bentley
Rename fields
258
                 target_branch, patch=None, patch_type=None,
2520.4.73 by Aaron Bentley
Implement new merge directive format
259
                 source_branch=None, message=None, bundle=None):
1551.12.38 by Aaron Bentley
Add docs for MergeDirective and RIO-patch functions
260
        """Constructor.
261
262
        :param revision_id: The revision to merge
263
        :param testament_sha1: The sha1 of the testament of the revision to
264
            merge.
265
        :param time: The current POSIX timestamp time
266
        :param timezone: The timezone offset
267
        :param target_branch: The branch to apply the merge to
268
        :param patch: The text of a diff or bundle
269
        :param patch_type: None, "diff" or "bundle", depending on the contents
270
            of patch
271
        :param source_branch: A public location to merge the revision from
272
        :param message: The message to use when committing this merge
273
        """
2520.4.73 by Aaron Bentley
Implement new merge directive format
274
        _BaseMergeDirective.__init__(self, revision_id, testament_sha1, time,
275
            timezone, target_branch, patch, source_branch, message)
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
276
        if patch_type not in (None, 'diff', 'bundle'):
277
            raise ValueError(patch_type)
1551.12.13 by Aaron Bentley
Rename fields
278
        if patch_type != 'bundle' and source_branch is None:
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
279
            raise errors.NoMergeSource()
280
        if patch_type is not None and patch is None:
281
            raise errors.PatchMissing(patch_type)
282
        self.patch_type = patch_type
2520.4.73 by Aaron Bentley
Implement new merge directive format
283
284
    def clear_payload(self):
285
        self.patch = None
286
        self.patch_type = None
287
2520.4.80 by Aaron Bentley
Improve merge directive tests
288
    def get_raw_bundle(self):
289
        return self.bundle
290
2520.4.73 by Aaron Bentley
Implement new merge directive format
291
    def _bundle(self):
292
        if self.patch_type == 'bundle':
293
            return self.patch
294
        else:
295
            return None
296
297
    bundle = property(_bundle)
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
298
1551.12.12 by Aaron Bentley
Add format header
299
    @classmethod
300
    def from_lines(klass, lines):
1551.12.38 by Aaron Bentley
Add docs for MergeDirective and RIO-patch functions
301
        """Deserialize a MergeRequest from an iterable of lines
302
303
        :param lines: An iterable of lines
304
        :return: a MergeRequest
305
        """
1551.12.51 by Aaron Bentley
Allow leading junk before merge directive header
306
        line_iter = iter(lines)
307
        for line in line_iter:
2520.4.73 by Aaron Bentley
Implement new merge directive format
308
            if line.startswith('# Bazaar merge directive format '):
1551.12.51 by Aaron Bentley
Allow leading junk before merge directive header
309
                break
310
        else:
1551.12.59 by Aaron Bentley
Correctly handle empty merge directive texts
311
            if len(lines) > 0:
312
                raise errors.NotAMergeDirective(lines[0])
313
            else:
314
                raise errors.NotAMergeDirective('')
2520.4.73 by Aaron Bentley
Implement new merge directive format
315
        return _format_registry.get(line[2:].rstrip())._from_lines(line_iter)
316
317
    @classmethod
318
    def _from_lines(klass, line_iter):
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
319
        stanza = rio.read_patch_stanza(line_iter)
320
        patch_lines = list(line_iter)
321
        if len(patch_lines) == 0:
322
            patch = None
1551.12.53 by Aaron Bentley
Fix deserialization of merge directives with no patch
323
            patch_type = None
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
324
        else:
325
            patch = ''.join(patch_lines)
1551.12.53 by Aaron Bentley
Fix deserialization of merge directives with no patch
326
            try:
327
                bundle_serializer.read_bundle(StringIO(patch))
1551.15.29 by Aaron Bentley
Make merge directives robust against broken bundles
328
            except (errors.NotABundle, errors.BundleNotSupported,
329
                    errors.BadBundle):
1551.12.53 by Aaron Bentley
Fix deserialization of merge directives with no patch
330
                patch_type = 'diff'
331
            else:
332
                patch_type = 'bundle'
1551.12.30 by Aaron Bentley
Use patch-style dates for timestamps in merge directives
333
        time, timezone = timestamp.parse_patch_date(stanza.get('timestamp'))
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
334
        kwargs = {}
1551.12.13 by Aaron Bentley
Rename fields
335
        for key in ('revision_id', 'testament_sha1', 'target_branch',
1551.12.26 by Aaron Bentley
Get email working, with optional message
336
                    'source_branch', 'message'):
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
337
            try:
338
                kwargs[key] = stanza.get(key)
339
            except KeyError:
340
                pass
1551.12.54 by Aaron Bentley
Decoded revision ids are utf-8
341
        kwargs['revision_id'] = kwargs['revision_id'].encode('utf-8')
1551.12.3 by Aaron Bentley
Add timestamps to merge directives
342
        return MergeDirective(time=time, timezone=timezone,
343
                              patch_type=patch_type, patch=patch, **kwargs)
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
344
345
    def to_lines(self):
2520.4.73 by Aaron Bentley
Implement new merge directive format
346
        lines = self._to_lines()
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
347
        if self.patch is not None:
348
            lines.extend(self.patch.splitlines(True))
349
        return lines
350
2520.4.73 by Aaron Bentley
Implement new merge directive format
351
    @staticmethod
352
    def _generate_bundle(repository, revision_id, ancestor_id):
353
        s = StringIO()
354
        bundle_serializer.write_bundle(repository, revision_id,
355
                                       ancestor_id, s, '0.9')
356
        return s.getvalue()
357
2520.4.109 by Aaron Bentley
start work on directive cherry-picking
358
    def get_merge_request(self, repository):
359
        """Provide data for performing a merge
360
361
        Returns suggested base, suggested target, and patch verification status
362
        """
363
        return None, self.revision_id, 'inapplicable'
364
2520.4.76 by Aaron Bentley
Move base64-encoding into merge directives
365
2520.4.73 by Aaron Bentley
Implement new merge directive format
366
class MergeDirective2(_BaseMergeDirective):
367
2687.2.2 by Martin Pool
Fix up other references to 0.19
368
    _format_string = 'Bazaar merge directive format 2 (Bazaar 0.90)'
2520.4.73 by Aaron Bentley
Implement new merge directive format
369
370
    def __init__(self, revision_id, testament_sha1, time, timezone,
371
                 target_branch, patch=None, source_branch=None, message=None,
2520.4.105 by Aaron Bentley
Implement patch verification
372
                 bundle=None, base_revision_id=None):
2520.4.73 by Aaron Bentley
Implement new merge directive format
373
        if source_branch is None and bundle is None:
374
            raise errors.NoMergeSource()
375
        _BaseMergeDirective.__init__(self, revision_id, testament_sha1, time,
376
            timezone, target_branch, patch, source_branch, message)
377
        self.bundle = bundle
2520.4.105 by Aaron Bentley
Implement patch verification
378
        self.base_revision_id = base_revision_id
2520.4.73 by Aaron Bentley
Implement new merge directive format
379
380
    def _patch_type(self):
381
        if self.bundle is not None:
382
            return 'bundle'
383
        elif self.patch is not None:
384
            return 'diff'
385
        else:
386
            return None
387
388
    patch_type = property(_patch_type)
389
390
    def clear_payload(self):
391
        self.patch = None
392
        self.bundle = None
393
2520.4.80 by Aaron Bentley
Improve merge directive tests
394
    def get_raw_bundle(self):
395
        if self.bundle is None:
396
            return None
397
        else:
398
            return self.bundle.decode('base-64')
399
2520.4.73 by Aaron Bentley
Implement new merge directive format
400
    @classmethod
401
    def _from_lines(klass, line_iter):
402
        stanza = rio.read_patch_stanza(line_iter)
403
        patch = None
404
        bundle = None
405
        try:
406
            start = line_iter.next()
407
        except StopIteration:
408
            pass
409
        else:
410
            if start.startswith('# Begin patch'):
411
                patch_lines = []
412
                for line in line_iter:
413
                    if line.startswith('# Begin bundle'):
414
                        start = line
415
                        break
416
                    patch_lines.append(line)
417
                else:
418
                    start = None
419
                patch = ''.join(patch_lines)
420
            if start is not None:
421
                if start.startswith('# Begin bundle'):
422
                    bundle = ''.join(line_iter)
423
                else:
424
                    raise errors.IllegalMergeDirectivePayload(start)
425
        time, timezone = timestamp.parse_patch_date(stanza.get('timestamp'))
426
        kwargs = {}
427
        for key in ('revision_id', 'testament_sha1', 'target_branch',
2520.4.105 by Aaron Bentley
Implement patch verification
428
                    'source_branch', 'message', 'base_revision_id'):
2520.4.73 by Aaron Bentley
Implement new merge directive format
429
            try:
430
                kwargs[key] = stanza.get(key)
431
            except KeyError:
432
                pass
433
        kwargs['revision_id'] = kwargs['revision_id'].encode('utf-8')
2520.4.105 by Aaron Bentley
Implement patch verification
434
        kwargs['base_revision_id'] =\
435
            kwargs['base_revision_id'].encode('utf-8')
2520.4.73 by Aaron Bentley
Implement new merge directive format
436
        return klass(time=time, timezone=timezone, patch=patch, bundle=bundle,
437
                     **kwargs)
438
439
    def to_lines(self):
2520.4.105 by Aaron Bentley
Implement patch verification
440
        lines = self._to_lines(base_revision=True)
2520.4.73 by Aaron Bentley
Implement new merge directive format
441
        if self.patch is not None:
442
            lines.append('# Begin patch\n')
443
            lines.extend(self.patch.splitlines(True))
444
        if self.bundle is not None:
445
            lines.append('# Begin bundle\n')
446
            lines.extend(self.bundle.splitlines(True))
447
        return lines
448
449
    @classmethod
450
    def from_objects(klass, repository, revision_id, time, timezone,
2520.5.4 by Aaron Bentley
Replace 'bundle-revisions' with 'submit' command
451
                 target_branch, include_patch=True, include_bundle=True,
2520.4.112 by Aaron Bentley
Make cherry-pick merge directives possible
452
                 local_target_branch=None, public_branch=None, message=None,
453
                 base_revision_id=None):
2520.4.73 by Aaron Bentley
Implement new merge directive format
454
        """Generate a merge directive from various objects
455
456
        :param repository: The repository containing the revision
457
        :param revision_id: The revision to merge
458
        :param time: The POSIX timestamp of the date the request was issued.
459
        :param timezone: The timezone of the request
460
        :param target_branch: The url of the branch to merge into
2520.5.4 by Aaron Bentley
Replace 'bundle-revisions' with 'submit' command
461
        :param include_patch: If true, include a preview patch
462
        :param include_bundle: If true, include a bundle
2520.4.73 by Aaron Bentley
Implement new merge directive format
463
        :param local_target_branch: a local copy of the target branch
464
        :param public_branch: location of a public branch containing the target
465
            revision.
466
        :param message: Message to use when committing the merge
467
        :return: The merge directive
468
2520.5.4 by Aaron Bentley
Replace 'bundle-revisions' with 'submit' command
469
        The public branch is always used if supplied.  If no bundle is
470
        included, the public branch must be supplied, and will be verified.
2520.4.73 by Aaron Bentley
Implement new merge directive format
471
472
        If the message is not supplied, the message from revision_id will be
473
        used for the commit.
474
        """
2520.4.86 by Aaron Bentley
Improve locking in _BaseMergeDirective.from_object
475
        locked = []
476
        try:
477
            repository.lock_write()
478
            locked.append(repository)
479
            t_revision_id = revision_id
480
            if revision_id == 'null:':
481
                t_revision_id = None
482
            t = testament.StrictTestament3.from_revision(repository,
483
                t_revision_id)
484
            submit_branch = _mod_branch.Branch.open(target_branch)
485
            submit_branch.lock_read()
486
            locked.append(submit_branch)
487
            if submit_branch.get_public_branch() is not None:
488
                target_branch = submit_branch.get_public_branch()
2520.4.105 by Aaron Bentley
Implement patch verification
489
            submit_revision_id = submit_branch.last_revision()
490
            submit_revision_id = _mod_revision.ensure_null(submit_revision_id)
491
            graph = repository.get_graph(submit_branch.repository)
492
            ancestor_id = graph.find_unique_lca(revision_id,
493
                                                submit_revision_id)
2520.4.112 by Aaron Bentley
Make cherry-pick merge directives possible
494
            if base_revision_id is None:
495
                base_revision_id = ancestor_id
2520.5.4 by Aaron Bentley
Replace 'bundle-revisions' with 'submit' command
496
            if (include_patch, include_bundle) != (False, False):
497
                repository.fetch(submit_branch.repository, submit_revision_id)
498
            if include_patch:
499
                patch = klass._generate_diff(repository, revision_id,
500
                                             base_revision_id)
501
            else:
2520.4.86 by Aaron Bentley
Improve locking in _BaseMergeDirective.from_object
502
                patch = None
2520.5.4 by Aaron Bentley
Replace 'bundle-revisions' with 'submit' command
503
504
            if include_bundle:
505
                bundle = klass._generate_bundle(repository, revision_id,
506
                    ancestor_id).encode('base-64')
507
            else:
2520.4.73 by Aaron Bentley
Implement new merge directive format
508
                bundle = None
2520.4.86 by Aaron Bentley
Improve locking in _BaseMergeDirective.from_object
509
2520.5.4 by Aaron Bentley
Replace 'bundle-revisions' with 'submit' command
510
            if public_branch is not None and not include_bundle:
511
                public_branch_obj = _mod_branch.Branch.open(public_branch)
512
                public_branch_obj.lock_read()
513
                locked.append(public_branch_obj)
514
                if not public_branch_obj.repository.has_revision(
515
                    revision_id):
516
                    raise errors.PublicBranchOutOfDate(public_branch,
517
                                                       revision_id)
2520.4.86 by Aaron Bentley
Improve locking in _BaseMergeDirective.from_object
518
        finally:
519
            for entry in reversed(locked):
520
                entry.unlock()
2520.4.73 by Aaron Bentley
Implement new merge directive format
521
        return klass(revision_id, t.as_sha1(), time, timezone, target_branch,
2520.4.112 by Aaron Bentley
Make cherry-pick merge directives possible
522
            patch, public_branch, message, bundle, base_revision_id)
2520.4.105 by Aaron Bentley
Implement patch verification
523
524
    def _verify_patch(self, repository):
525
        calculated_patch = self._generate_diff(repository, self.revision_id,
526
                                               self.base_revision_id)
527
        # Convert line-endings to UNIX
528
        stored_patch = re.sub('\r\n?', '\n', self.patch)
2520.7.2 by Aaron Bentley
Restore patch verification for CR, CRLF files
529
        calculated_patch = re.sub('\r\n?', '\n', calculated_patch)
2520.4.105 by Aaron Bentley
Implement patch verification
530
        # Strip trailing whitespace
531
        calculated_patch = re.sub(' *\n', '\n', calculated_patch)
532
        stored_patch = re.sub(' *\n', '\n', stored_patch)
2520.4.108 by Aaron Bentley
Start work on using merge base from directives
533
        return (calculated_patch == stored_patch)
534
2520.4.109 by Aaron Bentley
start work on directive cherry-picking
535
    def get_merge_request(self, repository):
536
        """Provide data for performing a merge
2520.4.108 by Aaron Bentley
Start work on using merge base from directives
537
2520.4.109 by Aaron Bentley
start work on directive cherry-picking
538
        Returns suggested base, suggested target, and patch verification status
539
        """
2520.4.108 by Aaron Bentley
Start work on using merge base from directives
540
        verified = self._maybe_verify(repository)
2520.4.109 by Aaron Bentley
start work on directive cherry-picking
541
        return self.base_revision_id, self.revision_id, verified
2520.4.105 by Aaron Bentley
Implement patch verification
542
2520.4.108 by Aaron Bentley
Start work on using merge base from directives
543
    def _maybe_verify(self, repository):
544
        if self.patch is not None:
545
            if self._verify_patch(repository):
546
                return 'verified'
547
            else:
548
                return 'failed'
549
        else:
550
            return 'inapplicable'
551
2520.4.73 by Aaron Bentley
Implement new merge directive format
552
553
class MergeDirectiveFormatRegistry(registry.Registry):
554
2694.1.1 by Aaron Bentley
Restore support for Merge directive 2 / 0.19
555
    def register(self, directive, format_string=None):
556
        if format_string is None:
2694.1.3 by Aaron Bentley
Fix whitespace
557
            format_string = directive._format_string
2694.1.1 by Aaron Bentley
Restore support for Merge directive 2 / 0.19
558
        registry.Registry.register(self, format_string, directive)
2520.4.73 by Aaron Bentley
Implement new merge directive format
559
560
561
_format_registry = MergeDirectiveFormatRegistry()
562
_format_registry.register(MergeDirective)
563
_format_registry.register(MergeDirective2)
2694.1.1 by Aaron Bentley
Restore support for Merge directive 2 / 0.19
564
_format_registry.register(MergeDirective2,
565
                          'Bazaar merge directive format 2 (Bazaar 0.19)')