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