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