~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/send.py

  • Committer: Jelmer Vernooij
  • Date: 2009-05-26 14:10:18 UTC
  • mto: This revision was merged to the branch mainline in revision 4389.
  • Revision ID: jelmer@samba.org-20090526141018-5y160uts6358b29e
Move cmd_{send,bundle_revisions} back to bzrlib.builtins per Ians request. 

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
import time
19
19
 
20
20
from bzrlib import (
 
21
    bzrdir,
21
22
    errors,
22
23
    merge_directive,
23
24
    osutils,
24
25
    registry,
25
26
    trace,
26
27
    )
 
28
from bzrlib.branch import (
 
29
    Branch,
 
30
    )
27
31
from bzrlib.commands import (
28
32
    Command,
29
33
    )
31
35
    Option,
32
36
    RegistryOption,
33
37
    )
 
38
from bzrlib.revision import (
 
39
    NULL_REVISION,
 
40
    )
34
41
 
35
42
 
36
43
format_registry = registry.Registry()
37
44
 
38
45
 
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:
 
46
def send(submit_branch, revision, public_branch, remember, format,
 
47
         no_bundle, no_patch, output, from_, mail_to, message, body, 
 
48
         to_file):
 
49
    tree, branch = bzrdir.BzrDir.open_containing_tree_or_branch(from_)[:2]
 
50
    # we may need to write data into branch's repository to calculate
 
51
    # the data to send.
 
52
    branch.lock_write()
 
53
    try:
 
54
        if output is None:
 
55
            config = branch.get_config()
 
56
            if mail_to is None:
 
57
                mail_to = config.get_user_option('submit_to')
 
58
            mail_client = config.get_mail_client()
 
59
            if (not getattr(mail_client, 'supports_body', False)
 
60
                and body is not None):
150
61
                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):
 
62
                    'Mail client "%s" does not support specifying body' %
 
63
                    mail_client.__class__.__name__)
 
64
        if remember and submit_branch is None:
 
65
            raise errors.BzrCommandError(
 
66
                '--remember requires a branch to be specified.')
 
67
        stored_submit_branch = branch.get_submit_branch()
 
68
        remembered_submit_branch = None
 
69
        if submit_branch is None:
 
70
            submit_branch = stored_submit_branch
 
71
            remembered_submit_branch = "submit"
 
72
        else:
 
73
            if stored_submit_branch is None or remember:
 
74
                branch.set_submit_branch(submit_branch)
 
75
        if submit_branch is None:
 
76
            submit_branch = branch.get_parent()
 
77
            remembered_submit_branch = "parent"
 
78
        if submit_branch is None:
 
79
            raise errors.BzrCommandError('No submit branch known or'
 
80
                                         ' specified')
 
81
        if remembered_submit_branch is not None:
 
82
            trace.note('Using saved %s location "%s" to determine what '
 
83
                       'changes to submit.', remembered_submit_branch,
 
84
                       submit_branch)
 
85
 
 
86
        if mail_to is None:
 
87
            submit_config = Branch.open(submit_branch).get_config()
 
88
            mail_to = submit_config.get_user_option("child_submit_to")
 
89
 
 
90
        stored_public_branch = branch.get_public_branch()
 
91
        if public_branch is None:
 
92
            public_branch = stored_public_branch
 
93
        elif stored_public_branch is None or remember:
 
94
            branch.set_public_branch(public_branch)
 
95
        if no_bundle and public_branch is None:
 
96
            raise errors.BzrCommandError('No public branch specified or'
 
97
                                         ' known')
 
98
        base_revision_id = None
 
99
        revision_id = None
 
100
        if revision is not None:
 
101
            if len(revision) > 2:
 
102
                raise errors.BzrCommandError('bzr send takes '
 
103
                    'at most two one revision identifiers')
 
104
            revision_id = revision[-1].as_revision_id(branch)
 
105
            if len(revision) == 2:
 
106
                base_revision_id = revision[0].as_revision_id(branch)
 
107
        if revision_id is None:
 
108
            revision_id = branch.last_revision()
 
109
        if revision_id == NULL_REVISION:
 
110
            raise errors.BzrCommandError('No revisions to submit.')
 
111
        if format is None:
 
112
            # TODO: Query submit branch for its preferred format
 
113
            format = format_registry.get()
 
114
        directive = format(branch, revision_id, submit_branch, 
 
115
            public_branch, no_patch, no_bundle, message, base_revision_id)
280
116
        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)
 
117
            directive.compose_merge_request(mail_client, mail_to, body,
 
118
                                            branch, tree)
 
119
        else:
 
120
            if output == '-':
 
121
                outfile = to_file
 
122
            else:
 
123
                outfile = open(output, 'wb')
 
124
            try:
 
125
                outfile.writelines(directive.to_lines())
 
126
            finally:
 
127
                if outfile is not to_file:
 
128
                    outfile.close()
 
129
    finally:
 
130
        branch.unlock()
285
131
 
286
132
 
287
133
def _send_4(branch, revision_id, submit_branch, public_branch,