/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
4763.2.4 by John Arbash Meinel
merge bzr.2.1 in preparation for NEWS entry.
1
# Copyright (C) 2007-2010 Canonical Ltd
1551.12.36 by Aaron Bentley
Fix failing tests
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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1551.12.36 by Aaron Bentley
Fix failing tests
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,
4098.5.16 by Aaron Bentley
Move hook to MergeDirective, implement MergeDirective.compose_merge_request.
26
    hooks,
2520.4.73 by Aaron Bentley
Implement new merge directive format
27
    registry,
1551.12.5 by Aaron Bentley
Get MergeDirective.from_objects working
28
    revision as _mod_revision,
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
29
    rio,
1551.12.5 by Aaron Bentley
Get MergeDirective.from_objects working
30
    testament,
1551.12.30 by Aaron Bentley
Use patch-style dates for timestamps in merge directives
31
    timestamp,
4098.5.18 by Aaron Bentley
Gracefully handle mail clients that don't support bodies.
32
    trace,
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
33
    )
1551.14.4 by Aaron Bentley
Change bundle reader and merge directive to both be 'mergeables'
34
from bzrlib.bundle import (
35
    serializer as bundle_serializer,
36
    )
2625.6.1 by Adeodato Simó
New EmailMessage class, façade around email.Message and MIMEMultipart.
37
from bzrlib.email_message import EmailMessage
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
38
39
4098.5.16 by Aaron Bentley
Move hook to MergeDirective, implement MergeDirective.compose_merge_request.
40
class MergeRequestBodyParams(object):
4098.5.17 by Aaron Bentley
cleanup
41
    """Parameter object for the merge_request_body hook."""
4098.5.16 by Aaron Bentley
Move hook to MergeDirective, implement MergeDirective.compose_merge_request.
42
43
    def __init__(self, body, orig_body, directive, to, basename, subject,
44
                 branch, tree=None):
45
        self.body = body
46
        self.orig_body = orig_body
47
        self.directive = directive
48
        self.branch = branch
49
        self.tree = tree
50
        self.to = to
51
        self.basename = basename
52
        self.subject = subject
53
54
55
class MergeDirectiveHooks(hooks.Hooks):
4098.5.17 by Aaron Bentley
cleanup
56
    """Hooks for MergeDirective classes."""
4098.5.16 by Aaron Bentley
Move hook to MergeDirective, implement MergeDirective.compose_merge_request.
57
58
    def __init__(self):
59
        hooks.Hooks.__init__(self)
60
        self.create_hook(hooks.HookPoint('merge_request_body',
61
            "Called with a MergeRequestBodyParams when a body is needed for"
62
            " a merge request.  Callbacks must return a body.  If more"
63
            " than one callback is registered, the output of one callback is"
64
            " provided to the next.", (1, 15, 0), False))
65
66
5086.3.1 by Jelmer Vernooij
``bzrlib.merge_directive._BaseMergeDirective`` has been renamed to
67
class BaseMergeDirective(object):
2520.4.73 by Aaron Bentley
Implement new merge directive format
68
4098.5.16 by Aaron Bentley
Move hook to MergeDirective, implement MergeDirective.compose_merge_request.
69
    hooks = MergeDirectiveHooks()
70
2520.4.73 by Aaron Bentley
Implement new merge directive format
71
    def __init__(self, revision_id, testament_sha1, time, timezone,
72
                 target_branch, patch=None, source_branch=None, message=None,
73
                 bundle=None):
74
        """Constructor.
75
76
        :param revision_id: The revision to merge
77
        :param testament_sha1: The sha1 of the testament of the revision to
78
            merge.
79
        :param time: The current POSIX timestamp time
80
        :param timezone: The timezone offset
81
        :param target_branch: The branch to apply the merge to
82
        :param patch: The text of a diff or bundle
83
        :param source_branch: A public location to merge the revision from
84
        :param message: The message to use when committing this merge
85
        """
86
        self.revision_id = revision_id
87
        self.testament_sha1 = testament_sha1
88
        self.time = time
89
        self.timezone = timezone
90
        self.target_branch = target_branch
91
        self.patch = patch
92
        self.source_branch = source_branch
93
        self.message = message
94
5086.3.1 by Jelmer Vernooij
``bzrlib.merge_directive._BaseMergeDirective`` has been renamed to
95
    def to_lines(self):
96
        """Serialize as a list of lines
97
98
        :return: a list of lines
99
        """
100
        raise NotImplementedError(self.to_lines)
101
102
    def get_raw_bundle(self):
103
        """Return the bundle for this merge directive.
104
105
        :return: bundle text or None if there is no bundle
106
        """
107
        return None
108
2520.4.105 by Aaron Bentley
Implement patch verification
109
    def _to_lines(self, base_revision=False):
2520.4.73 by Aaron Bentley
Implement new merge directive format
110
        """Serialize as a list of lines
111
112
        :return: a list of lines
113
        """
114
        time_str = timestamp.format_patch_date(self.time, self.timezone)
115
        stanza = rio.Stanza(revision_id=self.revision_id, timestamp=time_str,
116
                            target_branch=self.target_branch,
117
                            testament_sha1=self.testament_sha1)
118
        for key in ('source_branch', 'message'):
119
            if self.__dict__[key] is not None:
120
                stanza.add(key, self.__dict__[key])
2520.4.105 by Aaron Bentley
Implement patch verification
121
        if base_revision:
122
            stanza.add('base_revision_id', self.base_revision_id)
2520.4.73 by Aaron Bentley
Implement new merge directive format
123
        lines = ['# ' + self._format_string + '\n']
124
        lines.extend(rio.to_patch_lines(stanza))
125
        lines.append('# \n')
126
        return lines
127
128
    @classmethod
129
    def from_objects(klass, repository, revision_id, time, timezone,
130
                 target_branch, patch_type='bundle',
131
                 local_target_branch=None, public_branch=None, message=None):
132
        """Generate a merge directive from various objects
133
134
        :param repository: The repository containing the revision
135
        :param revision_id: The revision to merge
136
        :param time: The POSIX timestamp of the date the request was issued.
137
        :param timezone: The timezone of the request
138
        :param target_branch: The url of the branch to merge into
139
        :param patch_type: 'bundle', 'diff' or None, depending on the type of
140
            patch desired.
141
        :param local_target_branch: a local copy of the target branch
142
        :param public_branch: location of a public branch containing the target
143
            revision.
144
        :param message: Message to use when committing the merge
145
        :return: The merge directive
146
147
        The public branch is always used if supplied.  If the patch_type is
148
        not 'bundle', the public branch must be supplied, and will be verified.
149
150
        If the message is not supplied, the message from revision_id will be
151
        used for the commit.
152
        """
153
        t_revision_id = revision_id
2520.4.86 by Aaron Bentley
Improve locking in _BaseMergeDirective.from_object
154
        if revision_id == _mod_revision.NULL_REVISION:
2520.4.73 by Aaron Bentley
Implement new merge directive format
155
            t_revision_id = None
156
        t = testament.StrictTestament3.from_revision(repository, t_revision_id)
157
        submit_branch = _mod_branch.Branch.open(target_branch)
158
        if submit_branch.get_public_branch() is not None:
159
            target_branch = submit_branch.get_public_branch()
160
        if patch_type is None:
161
            patch = None
162
        else:
163
            submit_revision_id = submit_branch.last_revision()
164
            submit_revision_id = _mod_revision.ensure_null(submit_revision_id)
165
            repository.fetch(submit_branch.repository, submit_revision_id)
166
            graph = repository.get_graph()
167
            ancestor_id = graph.find_unique_lca(revision_id,
168
                                                submit_revision_id)
169
            type_handler = {'bundle': klass._generate_bundle,
170
                            'diff': klass._generate_diff,
171
                            None: lambda x, y, z: None }
172
            patch = type_handler[patch_type](repository, revision_id,
173
                                             ancestor_id)
174
2520.5.4 by Aaron Bentley
Replace 'bundle-revisions' with 'submit' command
175
        if public_branch is not None and patch_type != 'bundle':
176
            public_branch_obj = _mod_branch.Branch.open(public_branch)
177
            if not public_branch_obj.repository.has_revision(revision_id):
178
                raise errors.PublicBranchOutOfDate(public_branch,
179
                                                   revision_id)
2520.4.73 by Aaron Bentley
Implement new merge directive format
180
181
        return klass(revision_id, t.as_sha1(), time, timezone, target_branch,
182
            patch, patch_type, public_branch, message)
183
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
184
    def get_disk_name(self, branch):
185
        """Generate a suitable basename for storing this directive on disk
186
187
        :param branch: The Branch this merge directive was generated fro
188
        :return: A string
189
        """
190
        revno, revision_id = branch.last_revision_info()
191
        if self.revision_id == revision_id:
3251.2.2 by Aaron Bentley
Fix bug in last revno handling
192
            revno = [revno]
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
193
        else:
194
            revno = branch.get_revision_id_to_revno_map().get(self.revision_id,
195
                ['merge'])
3449.4.1 by Lukáš Lalinský
Sanitize branch nick before using it as an attachment filename in ``bzr send``
196
        nick = re.sub('(\W+)', '-', branch.nick).strip('-')
197
        return '%s-%s' % (nick, '.'.join(str(n) for n in revno))
3251.2.1 by Aaron Bentley
Use nick/revno-based names for merge directives
198
2520.4.73 by Aaron Bentley
Implement new merge directive format
199
    @staticmethod
200
    def _generate_diff(repository, revision_id, ancestor_id):
201
        tree_1 = repository.revision_tree(ancestor_id)
202
        tree_2 = repository.revision_tree(revision_id)
203
        s = StringIO()
204
        diff.show_diff_trees(tree_1, tree_2, s, old_label='', new_label='')
205
        return s.getvalue()
206
207
    @staticmethod
208
    def _generate_bundle(repository, revision_id, ancestor_id):
209
        s = StringIO()
210
        bundle_serializer.write_bundle(repository, revision_id,
211
                                       ancestor_id, s)
212
        return s.getvalue()
213
2520.4.80 by Aaron Bentley
Improve merge directive tests
214
    def to_signed(self, branch):
215
        """Serialize as a signed string.
216
217
        :param branch: The source branch, to get the signing strategy
218
        :return: a string
219
        """
220
        my_gpg = gpg.GPGStrategy(branch.get_config())
221
        return my_gpg.sign(''.join(self.to_lines()))
222
223
    def to_email(self, mail_to, branch, sign=False):
224
        """Serialize as an email message.
225
226
        :param mail_to: The address to mail the message to
227
        :param branch: The source branch, to get the signing strategy and
228
            source email address
229
        :param sign: If True, gpg-sign the email
230
        :return: an email message
231
        """
232
        mail_from = branch.get_config().username()
233
        if self.message is not None:
2625.6.2 by Adeodato Simó
Merge bzr.dev, resolving conflicts and updating test_merge_directive.py.
234
            subject = self.message
2520.4.80 by Aaron Bentley
Improve merge directive tests
235
        else:
236
            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.
237
            subject = revision.message
2520.4.80 by Aaron Bentley
Improve merge directive tests
238
        if sign:
239
            body = self.to_signed(branch)
240
        else:
241
            body = ''.join(self.to_lines())
2625.6.2 by Adeodato Simó
Merge bzr.dev, resolving conflicts and updating test_merge_directive.py.
242
        message = EmailMessage(mail_from, mail_to, subject, body)
2520.4.80 by Aaron Bentley
Improve merge directive tests
243
        return message
244
245
    def install_revisions(self, target_repo):
246
        """Install revisions and return the target revision"""
247
        if not target_repo.has_revision(self.revision_id):
248
            if self.patch_type == 'bundle':
249
                info = bundle_serializer.read_bundle(
250
                    StringIO(self.get_raw_bundle()))
251
                # We don't use the bundle's target revision, because
252
                # MergeDirective.revision_id is authoritative.
1551.19.19 by Aaron Bentley
Merge directives can now fetch prerequisites from the target branch
253
                try:
254
                    info.install_revisions(target_repo, stream_input=False)
255
                except errors.RevisionNotPresent:
256
                    # At least one dependency isn't present.  Try installing
257
                    # missing revisions from the submit branch
3535.8.1 by James Westby
Handle something that isn't a branch being specified in target_branch.
258
                    try:
259
                        submit_branch = \
260
                            _mod_branch.Branch.open(self.target_branch)
261
                    except errors.NotBranchError:
262
                        raise errors.TargetNotBranch(self.target_branch)
1551.19.20 by Aaron Bentley
Updates from review
263
                    missing_revisions = []
264
                    bundle_revisions = set(r.revision_id for r in
265
                                           info.real_revisions)
1551.19.19 by Aaron Bentley
Merge directives can now fetch prerequisites from the target branch
266
                    for revision in info.real_revisions:
267
                        for parent_id in revision.parent_ids:
1551.19.20 by Aaron Bentley
Updates from review
268
                            if (parent_id not in bundle_revisions and
1551.19.19 by Aaron Bentley
Merge directives can now fetch prerequisites from the target branch
269
                                not target_repo.has_revision(parent_id)):
270
                                missing_revisions.append(parent_id)
1551.19.20 by Aaron Bentley
Updates from review
271
                    # reverse missing revisions to try to get heads first
272
                    unique_missing = []
273
                    unique_missing_set = set()
274
                    for revision in reversed(missing_revisions):
275
                        if revision in unique_missing_set:
276
                            continue
277
                        unique_missing.append(revision)
278
                        unique_missing_set.add(revision)
279
                    for missing_revision in unique_missing:
1551.19.19 by Aaron Bentley
Merge directives can now fetch prerequisites from the target branch
280
                        target_repo.fetch(submit_branch.repository,
281
                                          missing_revision)
282
                    info.install_revisions(target_repo, stream_input=False)
2520.4.80 by Aaron Bentley
Improve merge directive tests
283
            else:
284
                source_branch = _mod_branch.Branch.open(self.source_branch)
285
                target_repo.fetch(source_branch.repository, self.revision_id)
286
        return self.revision_id
287
4098.5.16 by Aaron Bentley
Move hook to MergeDirective, implement MergeDirective.compose_merge_request.
288
    def compose_merge_request(self, mail_client, to, body, branch, tree=None):
4098.5.17 by Aaron Bentley
cleanup
289
        """Compose a request to merge this directive.
290
291
        :param mail_client: The mail client to use for composing this request.
292
        :param to: The address to compose the request to.
293
        :param branch: The Branch that was used to produce this directive.
294
        :param tree: The Tree (if any) for the Branch used to produce this
295
            directive.
296
        """
4098.5.16 by Aaron Bentley
Move hook to MergeDirective, implement MergeDirective.compose_merge_request.
297
        basename = self.get_disk_name(branch)
298
        subject = '[MERGE] '
299
        if self.message is not None:
300
            subject += self.message
301
        else:
302
            revision = branch.repository.get_revision(self.revision_id)
303
            subject += revision.get_summary()
4098.5.18 by Aaron Bentley
Gracefully handle mail clients that don't support bodies.
304
        if getattr(mail_client, 'supports_body', False):
305
            orig_body = body
306
            for hook in self.hooks['merge_request_body']:
307
                params = MergeRequestBodyParams(body, orig_body, self,
308
                                                to, basename, subject, branch,
309
                                                tree)
310
                body = hook(params)
311
        elif len(self.hooks['merge_request_body']) > 0:
312
            trace.warning('Cannot run merge_request_body hooks because mail'
313
                          ' client %s does not support message bodies.',
314
                        mail_client.__class__.__name__)
4098.5.16 by Aaron Bentley
Move hook to MergeDirective, implement MergeDirective.compose_merge_request.
315
        mail_client.compose_merge_request(to, subject,
316
                                          ''.join(self.to_lines()),
317
                                          basename, body)
318
2520.4.73 by Aaron Bentley
Implement new merge directive format
319
5086.3.1 by Jelmer Vernooij
``bzrlib.merge_directive._BaseMergeDirective`` has been renamed to
320
class MergeDirective(BaseMergeDirective):
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
321
1551.12.38 by Aaron Bentley
Add docs for MergeDirective and RIO-patch functions
322
    """A request to perform a merge into a branch.
323
324
    Designed to be serialized and mailed.  It provides all the information
325
    needed to perform a merge automatically, by providing at minimum a revision
326
    bundle or the location of a branch.
327
328
    The serialization format is robust against certain common forms of
329
    deterioration caused by mailing.
330
331
    The format is also designed to be patch-compatible.  If the directive
332
    includes a diff or revision bundle, it should be possible to apply it
333
    directly using the standard patch program.
334
    """
335
1551.12.45 by Aaron Bentley
Change format marker to not experimental
336
    _format_string = 'Bazaar merge directive format 1'
1551.12.12 by Aaron Bentley
Add format header
337
1551.12.4 by Aaron Bentley
Add failing test
338
    def __init__(self, revision_id, testament_sha1, time, timezone,
1551.12.13 by Aaron Bentley
Rename fields
339
                 target_branch, patch=None, patch_type=None,
2520.4.73 by Aaron Bentley
Implement new merge directive format
340
                 source_branch=None, message=None, bundle=None):
1551.12.38 by Aaron Bentley
Add docs for MergeDirective and RIO-patch functions
341
        """Constructor.
342
343
        :param revision_id: The revision to merge
344
        :param testament_sha1: The sha1 of the testament of the revision to
345
            merge.
346
        :param time: The current POSIX timestamp time
347
        :param timezone: The timezone offset
348
        :param target_branch: The branch to apply the merge to
349
        :param patch: The text of a diff or bundle
350
        :param patch_type: None, "diff" or "bundle", depending on the contents
351
            of patch
352
        :param source_branch: A public location to merge the revision from
353
        :param message: The message to use when committing this merge
354
        """
5086.3.1 by Jelmer Vernooij
``bzrlib.merge_directive._BaseMergeDirective`` has been renamed to
355
        BaseMergeDirective.__init__(self, revision_id, testament_sha1, time,
2520.4.73 by Aaron Bentley
Implement new merge directive format
356
            timezone, target_branch, patch, source_branch, message)
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
357
        if patch_type not in (None, 'diff', 'bundle'):
358
            raise ValueError(patch_type)
1551.12.13 by Aaron Bentley
Rename fields
359
        if patch_type != 'bundle' and source_branch is None:
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
360
            raise errors.NoMergeSource()
361
        if patch_type is not None and patch is None:
362
            raise errors.PatchMissing(patch_type)
363
        self.patch_type = patch_type
2520.4.73 by Aaron Bentley
Implement new merge directive format
364
365
    def clear_payload(self):
366
        self.patch = None
367
        self.patch_type = None
368
2520.4.80 by Aaron Bentley
Improve merge directive tests
369
    def get_raw_bundle(self):
370
        return self.bundle
371
2520.4.73 by Aaron Bentley
Implement new merge directive format
372
    def _bundle(self):
373
        if self.patch_type == 'bundle':
374
            return self.patch
375
        else:
376
            return None
377
378
    bundle = property(_bundle)
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
379
1551.12.12 by Aaron Bentley
Add format header
380
    @classmethod
381
    def from_lines(klass, lines):
1551.12.38 by Aaron Bentley
Add docs for MergeDirective and RIO-patch functions
382
        """Deserialize a MergeRequest from an iterable of lines
383
384
        :param lines: An iterable of lines
385
        :return: a MergeRequest
386
        """
1551.12.51 by Aaron Bentley
Allow leading junk before merge directive header
387
        line_iter = iter(lines)
4792.7.3 by Martin
MergeDirective.from_lines claims to want an iterable but currently requires a list, rewrite so it really wants an iterable
388
        firstline = ""
1551.12.51 by Aaron Bentley
Allow leading junk before merge directive header
389
        for line in line_iter:
2520.4.73 by Aaron Bentley
Implement new merge directive format
390
            if line.startswith('# Bazaar merge directive format '):
4792.7.3 by Martin
MergeDirective.from_lines claims to want an iterable but currently requires a list, rewrite so it really wants an iterable
391
                return _format_registry.get(line[2:].rstrip())._from_lines(
392
                    line_iter)
393
            firstline = firstline or line.strip()
394
        raise errors.NotAMergeDirective(firstline)
2520.4.73 by Aaron Bentley
Implement new merge directive format
395
396
    @classmethod
397
    def _from_lines(klass, line_iter):
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
398
        stanza = rio.read_patch_stanza(line_iter)
399
        patch_lines = list(line_iter)
400
        if len(patch_lines) == 0:
401
            patch = None
1551.12.53 by Aaron Bentley
Fix deserialization of merge directives with no patch
402
            patch_type = None
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
403
        else:
404
            patch = ''.join(patch_lines)
1551.12.53 by Aaron Bentley
Fix deserialization of merge directives with no patch
405
            try:
406
                bundle_serializer.read_bundle(StringIO(patch))
1551.15.29 by Aaron Bentley
Make merge directives robust against broken bundles
407
            except (errors.NotABundle, errors.BundleNotSupported,
408
                    errors.BadBundle):
1551.12.53 by Aaron Bentley
Fix deserialization of merge directives with no patch
409
                patch_type = 'diff'
410
            else:
411
                patch_type = 'bundle'
1551.12.30 by Aaron Bentley
Use patch-style dates for timestamps in merge directives
412
        time, timezone = timestamp.parse_patch_date(stanza.get('timestamp'))
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
413
        kwargs = {}
1551.12.13 by Aaron Bentley
Rename fields
414
        for key in ('revision_id', 'testament_sha1', 'target_branch',
1551.12.26 by Aaron Bentley
Get email working, with optional message
415
                    'source_branch', 'message'):
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
416
            try:
417
                kwargs[key] = stanza.get(key)
418
            except KeyError:
419
                pass
1551.12.54 by Aaron Bentley
Decoded revision ids are utf-8
420
        kwargs['revision_id'] = kwargs['revision_id'].encode('utf-8')
1551.12.3 by Aaron Bentley
Add timestamps to merge directives
421
        return MergeDirective(time=time, timezone=timezone,
422
                              patch_type=patch_type, patch=patch, **kwargs)
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
423
424
    def to_lines(self):
2520.4.73 by Aaron Bentley
Implement new merge directive format
425
        lines = self._to_lines()
1551.12.2 by Aaron Bentley
Got directives round-tripping, with bundles and everything
426
        if self.patch is not None:
427
            lines.extend(self.patch.splitlines(True))
428
        return lines
429
2520.4.73 by Aaron Bentley
Implement new merge directive format
430
    @staticmethod
431
    def _generate_bundle(repository, revision_id, ancestor_id):
432
        s = StringIO()
433
        bundle_serializer.write_bundle(repository, revision_id,
434
                                       ancestor_id, s, '0.9')
435
        return s.getvalue()
436
2520.4.109 by Aaron Bentley
start work on directive cherry-picking
437
    def get_merge_request(self, repository):
438
        """Provide data for performing a merge
439
440
        Returns suggested base, suggested target, and patch verification status
441
        """
442
        return None, self.revision_id, 'inapplicable'
443
2520.4.76 by Aaron Bentley
Move base64-encoding into merge directives
444
5086.3.1 by Jelmer Vernooij
``bzrlib.merge_directive._BaseMergeDirective`` has been renamed to
445
class MergeDirective2(BaseMergeDirective):
2520.4.73 by Aaron Bentley
Implement new merge directive format
446
2687.2.2 by Martin Pool
Fix up other references to 0.19
447
    _format_string = 'Bazaar merge directive format 2 (Bazaar 0.90)'
2520.4.73 by Aaron Bentley
Implement new merge directive format
448
449
    def __init__(self, revision_id, testament_sha1, time, timezone,
450
                 target_branch, patch=None, source_branch=None, message=None,
2520.4.105 by Aaron Bentley
Implement patch verification
451
                 bundle=None, base_revision_id=None):
2520.4.73 by Aaron Bentley
Implement new merge directive format
452
        if source_branch is None and bundle is None:
453
            raise errors.NoMergeSource()
5086.3.1 by Jelmer Vernooij
``bzrlib.merge_directive._BaseMergeDirective`` has been renamed to
454
        BaseMergeDirective.__init__(self, revision_id, testament_sha1, time,
2520.4.73 by Aaron Bentley
Implement new merge directive format
455
            timezone, target_branch, patch, source_branch, message)
456
        self.bundle = bundle
2520.4.105 by Aaron Bentley
Implement patch verification
457
        self.base_revision_id = base_revision_id
2520.4.73 by Aaron Bentley
Implement new merge directive format
458
459
    def _patch_type(self):
460
        if self.bundle is not None:
461
            return 'bundle'
462
        elif self.patch is not None:
463
            return 'diff'
464
        else:
465
            return None
466
467
    patch_type = property(_patch_type)
468
469
    def clear_payload(self):
470
        self.patch = None
471
        self.bundle = None
472
2520.4.80 by Aaron Bentley
Improve merge directive tests
473
    def get_raw_bundle(self):
474
        if self.bundle is None:
475
            return None
476
        else:
477
            return self.bundle.decode('base-64')
478
2520.4.73 by Aaron Bentley
Implement new merge directive format
479
    @classmethod
480
    def _from_lines(klass, line_iter):
481
        stanza = rio.read_patch_stanza(line_iter)
482
        patch = None
483
        bundle = None
484
        try:
485
            start = line_iter.next()
486
        except StopIteration:
487
            pass
488
        else:
489
            if start.startswith('# Begin patch'):
490
                patch_lines = []
491
                for line in line_iter:
492
                    if line.startswith('# Begin bundle'):
493
                        start = line
494
                        break
495
                    patch_lines.append(line)
496
                else:
497
                    start = None
498
                patch = ''.join(patch_lines)
499
            if start is not None:
500
                if start.startswith('# Begin bundle'):
501
                    bundle = ''.join(line_iter)
502
                else:
503
                    raise errors.IllegalMergeDirectivePayload(start)
504
        time, timezone = timestamp.parse_patch_date(stanza.get('timestamp'))
505
        kwargs = {}
506
        for key in ('revision_id', 'testament_sha1', 'target_branch',
2520.4.105 by Aaron Bentley
Implement patch verification
507
                    'source_branch', 'message', 'base_revision_id'):
2520.4.73 by Aaron Bentley
Implement new merge directive format
508
            try:
509
                kwargs[key] = stanza.get(key)
510
            except KeyError:
511
                pass
512
        kwargs['revision_id'] = kwargs['revision_id'].encode('utf-8')
2520.4.105 by Aaron Bentley
Implement patch verification
513
        kwargs['base_revision_id'] =\
514
            kwargs['base_revision_id'].encode('utf-8')
2520.4.73 by Aaron Bentley
Implement new merge directive format
515
        return klass(time=time, timezone=timezone, patch=patch, bundle=bundle,
516
                     **kwargs)
517
518
    def to_lines(self):
2520.4.105 by Aaron Bentley
Implement patch verification
519
        lines = self._to_lines(base_revision=True)
2520.4.73 by Aaron Bentley
Implement new merge directive format
520
        if self.patch is not None:
521
            lines.append('# Begin patch\n')
522
            lines.extend(self.patch.splitlines(True))
523
        if self.bundle is not None:
524
            lines.append('# Begin bundle\n')
525
            lines.extend(self.bundle.splitlines(True))
526
        return lines
527
528
    @classmethod
529
    def from_objects(klass, repository, revision_id, time, timezone,
2520.5.4 by Aaron Bentley
Replace 'bundle-revisions' with 'submit' command
530
                 target_branch, include_patch=True, include_bundle=True,
2520.4.112 by Aaron Bentley
Make cherry-pick merge directives possible
531
                 local_target_branch=None, public_branch=None, message=None,
532
                 base_revision_id=None):
2520.4.73 by Aaron Bentley
Implement new merge directive format
533
        """Generate a merge directive from various objects
534
535
        :param repository: The repository containing the revision
536
        :param revision_id: The revision to merge
537
        :param time: The POSIX timestamp of the date the request was issued.
538
        :param timezone: The timezone of the request
539
        :param target_branch: The url of the branch to merge into
2520.5.4 by Aaron Bentley
Replace 'bundle-revisions' with 'submit' command
540
        :param include_patch: If true, include a preview patch
541
        :param include_bundle: If true, include a bundle
2520.4.73 by Aaron Bentley
Implement new merge directive format
542
        :param local_target_branch: a local copy of the target branch
543
        :param public_branch: location of a public branch containing the target
544
            revision.
545
        :param message: Message to use when committing the merge
546
        :return: The merge directive
547
2520.5.4 by Aaron Bentley
Replace 'bundle-revisions' with 'submit' command
548
        The public branch is always used if supplied.  If no bundle is
549
        included, the public branch must be supplied, and will be verified.
2520.4.73 by Aaron Bentley
Implement new merge directive format
550
551
        If the message is not supplied, the message from revision_id will be
552
        used for the commit.
553
        """
2520.4.86 by Aaron Bentley
Improve locking in _BaseMergeDirective.from_object
554
        locked = []
555
        try:
556
            repository.lock_write()
557
            locked.append(repository)
558
            t_revision_id = revision_id
559
            if revision_id == 'null:':
560
                t_revision_id = None
561
            t = testament.StrictTestament3.from_revision(repository,
562
                t_revision_id)
563
            submit_branch = _mod_branch.Branch.open(target_branch)
564
            submit_branch.lock_read()
565
            locked.append(submit_branch)
566
            if submit_branch.get_public_branch() is not None:
567
                target_branch = submit_branch.get_public_branch()
2520.4.105 by Aaron Bentley
Implement patch verification
568
            submit_revision_id = submit_branch.last_revision()
569
            submit_revision_id = _mod_revision.ensure_null(submit_revision_id)
570
            graph = repository.get_graph(submit_branch.repository)
571
            ancestor_id = graph.find_unique_lca(revision_id,
572
                                                submit_revision_id)
2520.4.112 by Aaron Bentley
Make cherry-pick merge directives possible
573
            if base_revision_id is None:
574
                base_revision_id = ancestor_id
2520.5.4 by Aaron Bentley
Replace 'bundle-revisions' with 'submit' command
575
            if (include_patch, include_bundle) != (False, False):
576
                repository.fetch(submit_branch.repository, submit_revision_id)
577
            if include_patch:
578
                patch = klass._generate_diff(repository, revision_id,
579
                                             base_revision_id)
580
            else:
2520.4.86 by Aaron Bentley
Improve locking in _BaseMergeDirective.from_object
581
                patch = None
2520.5.4 by Aaron Bentley
Replace 'bundle-revisions' with 'submit' command
582
583
            if include_bundle:
584
                bundle = klass._generate_bundle(repository, revision_id,
585
                    ancestor_id).encode('base-64')
586
            else:
2520.4.73 by Aaron Bentley
Implement new merge directive format
587
                bundle = None
2520.4.86 by Aaron Bentley
Improve locking in _BaseMergeDirective.from_object
588
2520.5.4 by Aaron Bentley
Replace 'bundle-revisions' with 'submit' command
589
            if public_branch is not None and not include_bundle:
590
                public_branch_obj = _mod_branch.Branch.open(public_branch)
591
                public_branch_obj.lock_read()
592
                locked.append(public_branch_obj)
593
                if not public_branch_obj.repository.has_revision(
594
                    revision_id):
595
                    raise errors.PublicBranchOutOfDate(public_branch,
596
                                                       revision_id)
4634.90.3 by Andrew Bennetts
Fix other bugs revealed by clearing chk_map page cache during blackbox tests.
597
            testament_sha1 = t.as_sha1()
2520.4.86 by Aaron Bentley
Improve locking in _BaseMergeDirective.from_object
598
        finally:
599
            for entry in reversed(locked):
600
                entry.unlock()
4634.90.3 by Andrew Bennetts
Fix other bugs revealed by clearing chk_map page cache during blackbox tests.
601
        return klass(revision_id, testament_sha1, time, timezone,
602
            target_branch, patch, public_branch, message, bundle,
603
            base_revision_id)
2520.4.105 by Aaron Bentley
Implement patch verification
604
605
    def _verify_patch(self, repository):
606
        calculated_patch = self._generate_diff(repository, self.revision_id,
607
                                               self.base_revision_id)
608
        # Convert line-endings to UNIX
609
        stored_patch = re.sub('\r\n?', '\n', self.patch)
2520.7.2 by Aaron Bentley
Restore patch verification for CR, CRLF files
610
        calculated_patch = re.sub('\r\n?', '\n', calculated_patch)
2520.4.105 by Aaron Bentley
Implement patch verification
611
        # Strip trailing whitespace
612
        calculated_patch = re.sub(' *\n', '\n', calculated_patch)
613
        stored_patch = re.sub(' *\n', '\n', stored_patch)
2520.4.108 by Aaron Bentley
Start work on using merge base from directives
614
        return (calculated_patch == stored_patch)
615
2520.4.109 by Aaron Bentley
start work on directive cherry-picking
616
    def get_merge_request(self, repository):
617
        """Provide data for performing a merge
2520.4.108 by Aaron Bentley
Start work on using merge base from directives
618
2520.4.109 by Aaron Bentley
start work on directive cherry-picking
619
        Returns suggested base, suggested target, and patch verification status
620
        """
2520.4.108 by Aaron Bentley
Start work on using merge base from directives
621
        verified = self._maybe_verify(repository)
2520.4.109 by Aaron Bentley
start work on directive cherry-picking
622
        return self.base_revision_id, self.revision_id, verified
2520.4.105 by Aaron Bentley
Implement patch verification
623
2520.4.108 by Aaron Bentley
Start work on using merge base from directives
624
    def _maybe_verify(self, repository):
625
        if self.patch is not None:
626
            if self._verify_patch(repository):
627
                return 'verified'
628
            else:
629
                return 'failed'
630
        else:
631
            return 'inapplicable'
632
2520.4.73 by Aaron Bentley
Implement new merge directive format
633
634
class MergeDirectiveFormatRegistry(registry.Registry):
635
2694.1.1 by Aaron Bentley
Restore support for Merge directive 2 / 0.19
636
    def register(self, directive, format_string=None):
637
        if format_string is None:
2694.1.3 by Aaron Bentley
Fix whitespace
638
            format_string = directive._format_string
2694.1.1 by Aaron Bentley
Restore support for Merge directive 2 / 0.19
639
        registry.Registry.register(self, format_string, directive)
2520.4.73 by Aaron Bentley
Implement new merge directive format
640
641
642
_format_registry = MergeDirectiveFormatRegistry()
643
_format_registry.register(MergeDirective)
644
_format_registry.register(MergeDirective2)
4241.14.12 by Vincent Ladeuil
Far too many modifications for a single commit, need to restart.
645
# 0.19 never existed.  It got renamed to 0.90.  But by that point, there were
646
# already merge directives in the wild that used 0.19. Registering with the old
647
# format string to retain compatibility with those merge directives.
2694.1.1 by Aaron Bentley
Restore support for Merge directive 2 / 0.19
648
_format_registry.register(MergeDirective2,
649
                          'Bazaar merge directive format 2 (Bazaar 0.19)')