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