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