/brz/remove-bazaar

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