~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/send.py

  • Committer: Jelmer Vernooij
  • Date: 2009-05-21 19:33:00 UTC
  • mto: This revision was merged to the branch mainline in revision 4389.
  • Revision ID: jelmer@samba.org-20090521193300-t9lh68tu76djhb9h
Move send command to a separate file, move send format registry out of cmd_send.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2009 Canonical Ltd
 
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
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
 
 
18
import time
 
19
 
 
20
from bzrlib import (
 
21
    errors,
 
22
    merge_directive,
 
23
    osutils,
 
24
    registry,
 
25
    trace,
 
26
    )
 
27
from bzrlib.commands import (
 
28
    Command,
 
29
    )
 
30
from bzrlib.option import (
 
31
    Option,
 
32
    RegistryOption,
 
33
    )
 
34
 
 
35
 
 
36
format_registry = registry.Registry()
 
37
 
 
38
 
 
39
class cmd_send(Command):
 
40
    """Mail or create a merge-directive for submitting changes.
 
41
 
 
42
    A merge directive provides many things needed for requesting merges:
 
43
 
 
44
    * A machine-readable description of the merge to perform
 
45
 
 
46
    * An optional patch that is a preview of the changes requested
 
47
 
 
48
    * An optional bundle of revision data, so that the changes can be applied
 
49
      directly from the merge directive, without retrieving data from a
 
50
      branch.
 
51
 
 
52
    If --no-bundle is specified, then public_branch is needed (and must be
 
53
    up-to-date), so that the receiver can perform the merge using the
 
54
    public_branch.  The public_branch is always included if known, so that
 
55
    people can check it later.
 
56
 
 
57
    The submit branch defaults to the parent, but can be overridden.  Both
 
58
    submit branch and public branch will be remembered if supplied.
 
59
 
 
60
    If a public_branch is known for the submit_branch, that public submit
 
61
    branch is used in the merge instructions.  This means that a local mirror
 
62
    can be used as your actual submit branch, once you have set public_branch
 
63
    for that mirror.
 
64
 
 
65
    Mail is sent using your preferred mail program.  This should be transparent
 
66
    on Windows (it uses MAPI).  On Linux, it requires the xdg-email utility.
 
67
    If the preferred client can't be found (or used), your editor will be used.
 
68
 
 
69
    To use a specific mail program, set the mail_client configuration option.
 
70
    (For Thunderbird 1.5, this works around some bugs.)  Supported values for
 
71
    specific clients are "claws", "evolution", "kmail", "mutt", and
 
72
    "thunderbird"; generic options are "default", "editor", "emacsclient",
 
73
    "mapi", and "xdg-email".  Plugins may also add supported clients.
 
74
 
 
75
    If mail is being sent, a to address is required.  This can be supplied
 
76
    either on the commandline, by setting the submit_to configuration
 
77
    option in the branch itself or the child_submit_to configuration option
 
78
    in the submit branch.
 
79
 
 
80
    Two formats are currently supported: "4" uses revision bundle format 4 and
 
81
    merge directive format 2.  It is significantly faster and smaller than
 
82
    older formats.  It is compatible with Bazaar 0.19 and later.  It is the
 
83
    default.  "0.9" uses revision bundle format 0.9 and merge directive
 
84
    format 1.  It is compatible with Bazaar 0.12 - 0.18.
 
85
 
 
86
    The merge directives created by bzr send may be applied using bzr merge or
 
87
    bzr pull by specifying a file containing a merge directive as the location.
 
88
    """
 
89
 
 
90
    encoding_type = 'exact'
 
91
 
 
92
    _see_also = ['merge', 'pull']
 
93
 
 
94
    takes_args = ['submit_branch?', 'public_branch?']
 
95
 
 
96
    takes_options = [
 
97
        Option('no-bundle',
 
98
               help='Do not include a bundle in the merge directive.'),
 
99
        Option('no-patch', help='Do not include a preview patch in the merge'
 
100
               ' directive.'),
 
101
        Option('remember',
 
102
               help='Remember submit and public branch.'),
 
103
        Option('from',
 
104
               help='Branch to generate the submission from, '
 
105
               'rather than the one containing the working directory.',
 
106
               short_name='f',
 
107
               type=unicode),
 
108
        Option('output', short_name='o',
 
109
               help='Write merge directive to this file; '
 
110
                    'use - for stdout.',
 
111
               type=unicode),
 
112
        Option('mail-to', help='Mail the request to this address.',
 
113
               type=unicode),
 
114
        'revision',
 
115
        'message',
 
116
        Option('body', help='Body for the email.', type=unicode),
 
117
        RegistryOption('format',
 
118
                       help='Use the specified output format.', 
 
119
                       registry=format_registry)
 
120
        ]
 
121
 
 
122
    def run(self, submit_branch=None, public_branch=None, no_bundle=False,
 
123
            no_patch=False, revision=None, remember=False, output=None,
 
124
            format=None, mail_to=None, message=None, body=None, **kwargs):
 
125
        return self._run(submit_branch, revision, public_branch, remember,
 
126
                         format, no_bundle, no_patch, output,
 
127
                         kwargs.get('from', '.'), mail_to, message, body)
 
128
 
 
129
    def _run(self, submit_branch, revision, public_branch, remember, format,
 
130
             no_bundle, no_patch, output, from_, mail_to, message, body):
 
131
        from bzrlib import bzrdir
 
132
        from bzrlib.branch import Branch
 
133
        from bzrlib.revision import NULL_REVISION
 
134
        tree, branch = bzrdir.BzrDir.open_containing_tree_or_branch(from_)[:2]
 
135
        # we may need to write data into branch's repository to calculate
 
136
        # the data to send.
 
137
        branch.lock_write()
 
138
        try:
 
139
            if output is None:
 
140
                config = branch.get_config()
 
141
                if mail_to is None:
 
142
                    mail_to = config.get_user_option('submit_to')
 
143
                mail_client = config.get_mail_client()
 
144
                if (not getattr(mail_client, 'supports_body', False)
 
145
                    and body is not None):
 
146
                    raise errors.BzrCommandError(
 
147
                        'Mail client "%s" does not support specifying body' %
 
148
                        mail_client.__class__.__name__)
 
149
            if remember and submit_branch is None:
 
150
                raise errors.BzrCommandError(
 
151
                    '--remember requires a branch to be specified.')
 
152
            stored_submit_branch = branch.get_submit_branch()
 
153
            remembered_submit_branch = None
 
154
            if submit_branch is None:
 
155
                submit_branch = stored_submit_branch
 
156
                remembered_submit_branch = "submit"
 
157
            else:
 
158
                if stored_submit_branch is None or remember:
 
159
                    branch.set_submit_branch(submit_branch)
 
160
            if submit_branch is None:
 
161
                submit_branch = branch.get_parent()
 
162
                remembered_submit_branch = "parent"
 
163
            if submit_branch is None:
 
164
                raise errors.BzrCommandError('No submit branch known or'
 
165
                                             ' specified')
 
166
            if remembered_submit_branch is not None:
 
167
                trace.note('Using saved %s location "%s" to determine what '
 
168
                           'changes to submit.', remembered_submit_branch,
 
169
                           submit_branch)
 
170
 
 
171
            if mail_to is None:
 
172
                submit_config = Branch.open(submit_branch).get_config()
 
173
                mail_to = submit_config.get_user_option("child_submit_to")
 
174
 
 
175
            stored_public_branch = branch.get_public_branch()
 
176
            if public_branch is None:
 
177
                public_branch = stored_public_branch
 
178
            elif stored_public_branch is None or remember:
 
179
                branch.set_public_branch(public_branch)
 
180
            if no_bundle and public_branch is None:
 
181
                raise errors.BzrCommandError('No public branch specified or'
 
182
                                             ' known')
 
183
            base_revision_id = None
 
184
            revision_id = None
 
185
            if revision is not None:
 
186
                if len(revision) > 2:
 
187
                    raise errors.BzrCommandError('bzr send takes '
 
188
                        'at most two one revision identifiers')
 
189
                revision_id = revision[-1].as_revision_id(branch)
 
190
                if len(revision) == 2:
 
191
                    base_revision_id = revision[0].as_revision_id(branch)
 
192
            if revision_id is None:
 
193
                revision_id = branch.last_revision()
 
194
            if revision_id == NULL_REVISION:
 
195
                raise errors.BzrCommandError('No revisions to submit.')
 
196
            if format is None:
 
197
                # TODO: Query submit branch for its preferred format
 
198
                format = format_registry.get()
 
199
            directive = format(branch, revision_id, submit_branch, 
 
200
                public_branch, no_patch, no_bundle, message, base_revision_id)
 
201
            if output is None:
 
202
                directive.compose_merge_request(mail_client, mail_to, body,
 
203
                                                branch, tree)
 
204
            else:
 
205
                if output == '-':
 
206
                    outfile = self.outf
 
207
                else:
 
208
                    outfile = open(output, 'wb')
 
209
                try:
 
210
                    outfile.writelines(directive.to_lines())
 
211
                finally:
 
212
                    if outfile is not self.outf:
 
213
                        outfile.close()
 
214
        finally:
 
215
            branch.unlock()
 
216
 
 
217
 
 
218
class cmd_bundle_revisions(cmd_send):
 
219
 
 
220
    """Create a merge-directive for submitting changes.
 
221
 
 
222
    A merge directive provides many things needed for requesting merges:
 
223
 
 
224
    * A machine-readable description of the merge to perform
 
225
 
 
226
    * An optional patch that is a preview of the changes requested
 
227
 
 
228
    * An optional bundle of revision data, so that the changes can be applied
 
229
      directly from the merge directive, without retrieving data from a
 
230
      branch.
 
231
 
 
232
    If --no-bundle is specified, then public_branch is needed (and must be
 
233
    up-to-date), so that the receiver can perform the merge using the
 
234
    public_branch.  The public_branch is always included if known, so that
 
235
    people can check it later.
 
236
 
 
237
    The submit branch defaults to the parent, but can be overridden.  Both
 
238
    submit branch and public branch will be remembered if supplied.
 
239
 
 
240
    If a public_branch is known for the submit_branch, that public submit
 
241
    branch is used in the merge instructions.  This means that a local mirror
 
242
    can be used as your actual submit branch, once you have set public_branch
 
243
    for that mirror.
 
244
 
 
245
    Two formats are currently supported: "4" uses revision bundle format 4 and
 
246
    merge directive format 2.  It is significantly faster and smaller than
 
247
    older formats.  It is compatible with Bazaar 0.19 and later.  It is the
 
248
    default.  "0.9" uses revision bundle format 0.9 and merge directive
 
249
    format 1.  It is compatible with Bazaar 0.12 - 0.18.
 
250
    """
 
251
 
 
252
    takes_options = [
 
253
        Option('no-bundle',
 
254
               help='Do not include a bundle in the merge directive.'),
 
255
        Option('no-patch', help='Do not include a preview patch in the merge'
 
256
               ' directive.'),
 
257
        Option('remember',
 
258
               help='Remember submit and public branch.'),
 
259
        Option('from',
 
260
               help='Branch to generate the submission from, '
 
261
               'rather than the one containing the working directory.',
 
262
               short_name='f',
 
263
               type=unicode),
 
264
        Option('output', short_name='o', help='Write directive to this file.',
 
265
               type=unicode),
 
266
        'revision',
 
267
        RegistryOption('format',
 
268
                       help='Use the specified output format.',
 
269
                       registry=format_registry),
 
270
        ]
 
271
    aliases = ['bundle']
 
272
 
 
273
    _see_also = ['send', 'merge']
 
274
 
 
275
    hidden = True
 
276
 
 
277
    def run(self, submit_branch=None, public_branch=None, no_bundle=False,
 
278
            no_patch=False, revision=None, remember=False, output=None,
 
279
            format=None, **kwargs):
 
280
        if output is None:
 
281
            output = '-'
 
282
        return self._run(submit_branch, revision, public_branch, remember,
 
283
                         format, no_bundle, no_patch, output,
 
284
                         kwargs.get('from', '.'), None, None, None)
 
285
 
 
286
 
 
287
def _send_4(branch, revision_id, submit_branch, public_branch,
 
288
            no_patch, no_bundle, message, base_revision_id):
 
289
    return merge_directive.MergeDirective2.from_objects(
 
290
        branch.repository, revision_id, time.time(),
 
291
        osutils.local_time_offset(), submit_branch,
 
292
        public_branch=public_branch, include_patch=not no_patch,
 
293
        include_bundle=not no_bundle, message=message,
 
294
        base_revision_id=base_revision_id)
 
295
 
 
296
 
 
297
def _send_0_9(branch, revision_id, submit_branch, public_branch,
 
298
              no_patch, no_bundle, message, base_revision_id):
 
299
    if not no_bundle:
 
300
        if not no_patch:
 
301
            patch_type = 'bundle'
 
302
        else:
 
303
            raise errors.BzrCommandError('Format 0.9 does not'
 
304
                ' permit bundle with no patch')
 
305
    else:
 
306
        if not no_patch:
 
307
            patch_type = 'diff'
 
308
        else:
 
309
            patch_type = None
 
310
    return merge_directive.MergeDirective.from_objects(
 
311
        branch.repository, revision_id, time.time(),
 
312
        osutils.local_time_offset(), submit_branch,
 
313
        public_branch=public_branch, patch_type=patch_type,
 
314
        message=message)
 
315
 
 
316
 
 
317
format_registry.register('4', 
 
318
    _send_4, 'Bundle format 4, Merge Directive 2 (default)')
 
319
format_registry.register('0.9',
 
320
    _send_0_9, 'Bundle format 0.9, Merge Directive 1')
 
321
format_registry.default_key = '4'