/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.152.6 by Vincent Ladeuil
Really use the transports and test against all targeted protocols.
1
# Copyright (C) 2008 Canonical Ltd
0.152.1 by Vincent Ladeuil
Empty shell
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
0.152.6 by Vincent Ladeuil
Really use the transports and test against all targeted protocols.
17
"""Upload a working tree, incrementally.
18
0.152.17 by Vincent Ladeuil
Handle deletes (trivial implementation).
19
The only bzr-related info uploaded with the working tree is the corresponding
0.152.6 by Vincent Ladeuil
Really use the transports and test against all targeted protocols.
20
revision id. The uploaded working tree is not linked to any other bzr data.
21
0.152.35 by Martin Albisetti
* Tell the user if the remote location is already up to date
22
The intended use is for web developers which keep their web sites versioned
23
with bzr, can can use either FTP or SFTP to upload their site.
0.152.17 by Vincent Ladeuil
Handle deletes (trivial implementation).
24
25
Known limitations:
26
- Symlinks are ignored,
27
0.158.24 by Vincent Ladeuil
Tweak Gary's tweaks :)
28
- chmod bits (other than the owner's execution bit) are not supported.
0.152.6 by Vincent Ladeuil
Really use the transports and test against all targeted protocols.
29
"""
0.152.1 by Vincent Ladeuil
Empty shell
30
0.152.17 by Vincent Ladeuil
Handle deletes (trivial implementation).
31
# TODO: the chmod bits *can* be supported via the upload protocols
32
# (i.e. poorly), but since the web developers use these protocols to upload
33
# manually, it is expected that the associated web server is coherent with
34
# their presence/absence. In other words, if a web hosting provider requires
35
# chmod bits but don't provide an ftp server that support them, well, better
36
# find another provider ;-)
37
0.152.44 by Vincent Ladeuil
More robust full upload (at least regarding files changed to dirs and vice-versa).
38
# TODO: The message emitted in verbose mode displays local paths. That may be
39
# scary for the user when we say 'Deleting <path>' and are referring to
40
# remote files...
0.152.19 by Vincent Ladeuil
Handle kind_change. Trivial implementation, blocked by bug #205636.
41
0.152.3 by v.ladeuil+lp at free
Make the tests fail not error out.
42
from bzrlib import (
0.155.2 by James Westby
Add a post_change_branch_tip hook to upload.
43
    branch,
0.152.3 by v.ladeuil+lp at free
Make the tests fail not error out.
44
    commands,
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
45
    lazy_import,
0.152.3 by v.ladeuil+lp at free
Make the tests fail not error out.
46
    option,
47
    )
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
48
lazy_import.lazy_import(globals(), """
0.152.44 by Vincent Ladeuil
More robust full upload (at least regarding files changed to dirs and vice-versa).
49
import stat
50
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
51
from bzrlib import (
0.158.1 by Gary van der Merwe
Don't require a working tree.
52
    bzrdir,
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
53
    errors,
54
    revisionspec,
55
    transport,
0.152.44 by Vincent Ladeuil
More robust full upload (at least regarding files changed to dirs and vice-versa).
56
    osutils,
0.152.40 by Martin Albisetti
We need to import urlutils if we are going to use it
57
    urlutils,
0.152.27 by Martin Albisetti
* Added error message if the working tree has uncommited changes
58
    workingtree,
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
59
    )
60
""")
0.152.16 by Vincent Ladeuil
Handle renames. Robust implementation.
61
0.152.54 by Vincent Ladeuil
Let's start dev for a 1.0.
62
version_info = (1, 0, 0, 'dev', 0)
0.152.23 by Martin Albisetti
Added version_info and plugin_name
63
plugin_name = 'upload'
64
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
65
0.155.3 by James Westby
Add some tests for the hook, rename the option to "upload_auto"
66
def _get_branch_option(branch, option):
67
    return branch.get_config().get_user_option(option)
68
69
def _set_branch_option(branch, option, value):
70
    branch.get_config().set_user_option(option, value)
71
72
def get_upload_location(branch):
73
    return _get_branch_option(branch, 'upload_location')
74
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
75
def set_upload_location(branch, location):
0.155.3 by James Westby
Add some tests for the hook, rename the option to "upload_auto"
76
    _set_branch_option(branch, 'upload_location', location)
77
78
def get_upload_auto(branch):
79
    result = _get_branch_option(branch, 'upload_auto')
80
    # FIXME: is there a better way to do this with bzr's config API?
0.155.4 by James Westby
Add the groundwork for --auto that enables the hook for a branch.
81
    if result is not None and result.strip() == "True":
0.155.3 by James Westby
Add some tests for the hook, rename the option to "upload_auto"
82
        return True
83
    return False
84
85
def set_upload_auto(branch, auto):
86
    if auto:
87
        auto_str = "True"
88
    else:
89
        auto_str = "False"
90
    _set_branch_option(branch, 'upload_auto', auto_str)
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
91
92
93
class BzrUploader(object):
94
95
    def __init__(self, branch, to_transport, outf, tree, rev_id,
96
            quiet=False):
97
        self.branch = branch
98
        self.to_transport = to_transport
99
        self.outf = outf
100
        self.tree = tree
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
101
        self.rev_id = rev_id
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
102
        self.quiet = quiet
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
103
        self._pending_deletions = []
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
104
        self._pending_renames = []
0.152.12 by Vincent Ladeuil
Implement 'upload_location' in config files.
105
0.152.10 by Vincent Ladeuil
Fix incremental upload cheat.
106
    bzr_upload_revid_file_name = '.bzr-upload.revid'
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
107
108
    def set_uploaded_revid(self, rev_id):
0.152.10 by Vincent Ladeuil
Fix incremental upload cheat.
109
        # XXX: Add tests for concurrent updates, etc.
110
        self.to_transport.put_bytes(self.bzr_upload_revid_file_name, rev_id)
111
112
    def get_uploaded_revid(self):
113
        return self.to_transport.get_bytes(self.bzr_upload_revid_file_name)
0.152.7 by Vincent Ladeuil
Slight refactoring.
114
0.152.46 by Vincent Ladeuil
Handle x mode bit for files and provides default mode bits for
115
    def upload_file(self, relpath, id, mode=None):
116
        if mode is None:
117
            if self.tree.is_executable(id):
118
                mode = 0775
119
            else:
120
                mode = 0664
0.152.34 by Martin Albisetti
* Change the default behaviour to be more verbose
121
        if not self.quiet:
122
            self.outf.write('Uploading %s\n' % relpath)
0.152.46 by Vincent Ladeuil
Handle x mode bit for files and provides default mode bits for
123
        self.to_transport.put_bytes(relpath, self.tree.get_file_text(id), mode)
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
124
0.152.46 by Vincent Ladeuil
Handle x mode bit for files and provides default mode bits for
125
    def upload_file_robustly(self, relpath, id, mode=None):
0.152.44 by Vincent Ladeuil
More robust full upload (at least regarding files changed to dirs and vice-versa).
126
        """Upload a file, clearing the way on the remote side.
127
128
        When doing a full upload, it may happen that a directory exists where
129
        we want to put our file.
130
        """
131
        try:
132
            st = self.to_transport.stat(relpath)
133
            if stat.S_ISDIR(st.st_mode):
134
                # A simple rmdir may not be enough
135
                if not self.quiet:
136
                    self.outf.write('Clearing %s/%s\n' % (
137
                            self.to_transport.external_url(), relpath))
138
                self.to_transport.delete_tree(relpath)
139
        except errors.PathError:
140
            pass
0.152.46 by Vincent Ladeuil
Handle x mode bit for files and provides default mode bits for
141
        self.upload_file(relpath, id, mode)
142
143
    def make_remote_dir(self, relpath, mode=None):
144
        if mode is None:
145
            mode = 0775
146
        self.to_transport.mkdir(relpath, mode)
147
148
    def make_remote_dir_robustly(self, relpath, mode=None):
0.152.44 by Vincent Ladeuil
More robust full upload (at least regarding files changed to dirs and vice-versa).
149
        """Create a remote directory, clearing the way on the remote side.
150
151
        When doing a full upload, it may happen that a file exists where we
152
        want to create our directory.
153
        """
154
        try:
155
            st = self.to_transport.stat(relpath)
156
            if not stat.S_ISDIR(st.st_mode):
157
                if not self.quiet:
158
                    self.outf.write('Deleting %s/%s\n' % (
159
                            self.to_transport.external_url(), relpath))
160
                self.to_transport.delete(relpath)
0.152.47 by Vincent Ladeuil
Don't fail a full upload on an already existing dir.
161
            else:
162
                # Ok the remote dir already exists, nothing to do
163
                return
0.152.44 by Vincent Ladeuil
More robust full upload (at least regarding files changed to dirs and vice-versa).
164
        except errors.PathError:
165
            pass
0.152.46 by Vincent Ladeuil
Handle x mode bit for files and provides default mode bits for
166
        self.make_remote_dir(relpath, mode)
0.152.44 by Vincent Ladeuil
More robust full upload (at least regarding files changed to dirs and vice-versa).
167
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
168
    def delete_remote_file(self, relpath):
0.152.34 by Martin Albisetti
* Change the default behaviour to be more verbose
169
        if not self.quiet:
0.152.30 by Vincent Ladeuil
Comply to verbose.
170
            self.outf.write('Deleting %s\n' % relpath)
0.152.17 by Vincent Ladeuil
Handle deletes (trivial implementation).
171
        self.to_transport.delete(relpath)
172
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
173
    def delete_remote_dir(self, relpath):
0.152.34 by Martin Albisetti
* Change the default behaviour to be more verbose
174
        if not self.quiet:
0.152.30 by Vincent Ladeuil
Comply to verbose.
175
            self.outf.write('Deleting %s\n' % relpath)
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
176
        self.to_transport.rmdir(relpath)
177
178
    def delete_remote_dir_maybe(self, relpath):
179
        """Try to delete relpath, keeping failures to retry later."""
180
        try:
181
            self.to_transport.rmdir(relpath)
182
        # any kind of PathError would be OK, though we normally expect
183
        # DirectoryNotEmpty
184
        except errors.PathError:
185
            self._pending_deletions.append(relpath)
186
187
    def finish_deletions(self):
188
        if self._pending_deletions:
189
            # Process the previously failed deletions in reverse order to
190
            # delete children before parents
191
            for relpath in reversed(self._pending_deletions):
192
                self.to_transport.rmdir(relpath)
193
            # The following shouldn't be needed since we use it once per
194
            # upload, but better safe than sorry ;-)
195
            self._pending_deletions = []
196
0.152.16 by Vincent Ladeuil
Handle renames. Robust implementation.
197
    def rename_remote(self, old_relpath, new_relpath):
198
        """Rename a remote file or directory taking care of collisions.
199
200
        To avoid collisions during bulk renames, each renamed target is
201
        temporarily assigned a unique name. When all renames have been done,
202
        each target get its proper name.
203
        """
204
        # We generate a sufficiently random name to *assume* that
205
        # no collisions will occur and don't worry about it (nor
206
        # handle it).
207
        import os
208
        import random
209
        import time
210
211
        stamp = '.tmp.%.9f.%d.%d' % (time.time(),
212
                                     os.getpid(),
213
                                     random.randint(0,0x7FFFFFFF))
0.152.34 by Martin Albisetti
* Change the default behaviour to be more verbose
214
        if not self.quiet:
0.152.30 by Vincent Ladeuil
Comply to verbose.
215
            self.outf.write('Renaming %s to %s\n' % (old_relpath, new_relpath))
0.152.16 by Vincent Ladeuil
Handle renames. Robust implementation.
216
        self.to_transport.rename(old_relpath, stamp)
217
        self._pending_renames.append((stamp, new_relpath))
218
219
    def finish_renames(self):
220
        for (stamp, new_path) in self._pending_renames:
221
            self.to_transport.rename(stamp, new_path)
222
        # The following shouldn't be needed since we use it once per upload,
223
        # but better safe than sorry ;-)
224
        self._pending_renames = []
225
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
226
    def upload_full_tree(self):
0.152.14 by Vincent Ladeuil
Handle renames (trivial implementation).
227
        self.to_transport.ensure_base() # XXX: Handle errors (add
228
                                        # --create-prefix option ?)
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
229
        self.tree.lock_read()
230
        try:
0.152.44 by Vincent Ladeuil
More robust full upload (at least regarding files changed to dirs and vice-versa).
231
            for relpath, ie in self.tree.inventory.iter_entries():
232
                if relpath in ('', '.bzrignore'):
0.152.10 by Vincent Ladeuil
Fix incremental upload cheat.
233
                    # skip root ('')
234
                    # .bzrignore has no meaning outside of a working tree
235
                    # so do not upload it
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
236
                    continue
0.152.8 by Vincent Ladeuil
Handle uploading directories.
237
                if ie.kind == 'file':
0.152.44 by Vincent Ladeuil
More robust full upload (at least regarding files changed to dirs and vice-versa).
238
                    self.upload_file_robustly(relpath, ie.file_id)
0.152.8 by Vincent Ladeuil
Handle uploading directories.
239
                elif ie.kind == 'directory':
0.152.44 by Vincent Ladeuil
More robust full upload (at least regarding files changed to dirs and vice-versa).
240
                    self.make_remote_dir_robustly(relpath)
0.152.8 by Vincent Ladeuil
Handle uploading directories.
241
                else:
242
                    raise NotImplementedError
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
243
            self.set_uploaded_revid(self.rev_id)
244
        finally:
245
            self.tree.unlock()
246
0.152.10 by Vincent Ladeuil
Fix incremental upload cheat.
247
    def upload_tree(self):
0.152.24 by Martin Albisetti
Changed the way we upload the full tree if its never been uploaded
248
        # If we can't find the revid file on the remote location, upload the
249
        # full tree instead
250
        try:
251
            rev_id = self.get_uploaded_revid()
252
        except errors.NoSuchFile:
0.152.34 by Martin Albisetti
* Change the default behaviour to be more verbose
253
            if not self.quiet:
0.152.29 by Vincent Ladeuil
Work around test fixture limitation regarding self.outf (cough). All tests passing again.
254
                self.outf.write('No uploaded revision id found,'
0.152.34 by Martin Albisetti
* Change the default behaviour to be more verbose
255
                                ' switching to full upload\n')
0.152.24 by Martin Albisetti
Changed the way we upload the full tree if its never been uploaded
256
            self.upload_full_tree()
0.152.29 by Vincent Ladeuil
Work around test fixture limitation regarding self.outf (cough). All tests passing again.
257
            # We're done
258
            return
0.152.24 by Martin Albisetti
Changed the way we upload the full tree if its never been uploaded
259
0.152.35 by Martin Albisetti
* Tell the user if the remote location is already up to date
260
        # Check if the revision hasn't already been uploaded
261
        if rev_id == self.rev_id:
262
            if not self.quiet:
263
                self.outf.write('Remote location already up to date\n')
264
0.152.10 by Vincent Ladeuil
Fix incremental upload cheat.
265
        # XXX: errors out if rev_id not in branch history (probably someone
266
        # uploaded from a different branch).
267
        from_tree = self.branch.repository.revision_tree(rev_id)
0.152.14 by Vincent Ladeuil
Handle renames (trivial implementation).
268
        self.to_transport.ensure_base() # XXX: Handle errors (add
269
                                        # --create-prefix option ?)
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
270
        changes = self.tree.changes_from(from_tree)
271
        self.tree.lock_read()
272
        try:
0.152.17 by Vincent Ladeuil
Handle deletes (trivial implementation).
273
            for (path, id, kind) in changes.removed:
274
                if kind is 'file':
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
275
                    self.delete_remote_file(path)
276
                elif kind is  'directory':
277
                    self.delete_remote_dir_maybe(path)
0.152.17 by Vincent Ladeuil
Handle deletes (trivial implementation).
278
                else:
279
                    raise NotImplementedError
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
280
0.152.14 by Vincent Ladeuil
Handle renames (trivial implementation).
281
            for (old_path, new_path, id, kind,
282
                 content_change, exec_change) in changes.renamed:
0.152.52 by Vincent Ladeuil
Fix bug #270219 by handling content changes during renames.
283
                if content_change:
284
                    # We update the old_path content because renames and
285
                    # deletions are differed.
286
                    self.upload_file(old_path, id)
0.152.16 by Vincent Ladeuil
Handle renames. Robust implementation.
287
                self.rename_remote(old_path, new_path)
288
            self.finish_renames()
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
289
            self.finish_deletions()
0.152.16 by Vincent Ladeuil
Handle renames. Robust implementation.
290
0.152.19 by Vincent Ladeuil
Handle kind_change. Trivial implementation, blocked by bug #205636.
291
            for (path, id, old_kind, new_kind) in changes.kind_changed:
292
                if old_kind is 'file':
293
                    self.delete_remote_file(path)
294
                elif old_kind is  'directory':
295
                    self.delete_remote_dir(path)
296
                else:
297
                    raise NotImplementedError
298
299
                if new_kind is 'file':
300
                    self.upload_file(path, id)
301
                elif new_kind is 'directory':
302
                    self.make_remote_dir(path)
303
                else:
304
                    raise NotImplementedError
305
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
306
            for (path, id, kind) in changes.added:
307
                if kind is 'file':
0.152.7 by Vincent Ladeuil
Slight refactoring.
308
                    self.upload_file(path, id)
0.152.8 by Vincent Ladeuil
Handle uploading directories.
309
                elif kind is 'directory':
310
                    self.make_remote_dir(path)
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
311
                else:
312
                    raise NotImplementedError
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
313
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
314
            # XXX: Add a test for exec_change
315
            for (path, id, kind,
316
                 content_change, exec_change) in changes.modified:
317
                if kind is 'file':
0.152.7 by Vincent Ladeuil
Slight refactoring.
318
                    self.upload_file(path, id)
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
319
                else:
320
                    raise NotImplementedError
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
321
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
322
            self.set_uploaded_revid(self.rev_id)
323
        finally:
324
            self.tree.unlock()
0.152.4 by v.ladeuil+lp at free
Implement a trivial implementation to make one test pass.
325
0.158.19 by Vincent Ladeuil
Bzr has facilities for exceptions, let's use them.
326
class CannotUploadToWorkingTreeError(errors.BzrCommandError):
327
328
    _fmt = 'Cannot upload to a bzr managed working tree: %(url)s".'
329
330
    def __init__(self, url):
331
        super(CannotUploadToWorkingTreeError, self).__init__(self)
332
        self.url = url
333
0.152.1 by Vincent Ladeuil
Empty shell
334
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
335
class cmd_upload(commands.Command):
336
    """Upload a working tree, as a whole or incrementally.
337
338
    If no destination is specified use the last one used.
339
    If no revision is specified upload the changes since the last upload.
340
    """
341
    takes_args = ['location?']
342
    takes_options = [
343
        'revision',
344
        'remember',
345
        option.Option('full', 'Upload the full working tree.'),
346
        option.Option('quiet', 'Do not output what is being done.',
347
                       short_name='q'),
348
        option.Option('directory',
349
                      help='Branch to upload from, '
350
                      'rather than the one containing the working directory.',
351
                      short_name='d',
352
                      type=unicode,
353
                      ),
0.155.5 by James Westby
Hook up the --upload option and document it.
354
        option.Option('auto',
355
                      'Trigger an upload from this branch whenever the tip '
356
                      'revision changes.')
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
357
       ]
358
359
    def run(self, location=None, full=False, revision=None, remember=None,
0.155.4 by James Westby
Add the groundwork for --auto that enables the hook for a branch.
360
            directory=None, quiet=False, auto=None
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
361
            ):
362
        if directory is None:
363
            directory = u'.'
0.158.7 by Gary van der Merwe
Clean up white space.
364
0.157.1 by James Westby
Don't try and register the hook if install_named_hook is not available
365
        if auto and not auto_hook_available:
366
            raise BzrCommandError("Your version of bzr does not have the "
367
                    "hooks necessary for --auto to work")
368
0.158.19 by Vincent Ladeuil
Bzr has facilities for exceptions, let's use them.
369
        (wt, branch,
370
         relpath) = bzrdir.BzrDir.open_containing_tree_or_branch(directory)
0.158.7 by Gary van der Merwe
Clean up white space.
371
0.158.1 by Gary van der Merwe
Don't require a working tree.
372
        if wt:
373
            changes = wt.changes_from(wt.basis_tree())
0.158.7 by Gary van der Merwe
Clean up white space.
374
0.158.1 by Gary van der Merwe
Don't require a working tree.
375
            if revision is None and  changes.has_changed():
376
                raise errors.UncommittedChanges(wt)
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
377
378
        if location is None:
379
            stored_loc = get_upload_location(branch)
380
            if stored_loc is None:
381
                raise errors.BzrCommandError('No upload location'
382
                                             ' known or specified.')
383
            else:
384
                # FIXME: Not currently tested
385
                display_url = urlutils.unescape_for_display(stored_loc,
386
                        self.outf.encoding)
387
                self.outf.write("Using saved location: %s\n" % display_url)
388
                location = stored_loc
389
390
        to_transport = transport.get_transport(location)
0.158.9 by Gary van der Merwe
Add a check to make sure we are not uploading to an existing wt.
391
392
        # Check that we are not uploading to a existing working tree.
393
        try:
394
            to_bzr_dir = bzrdir.BzrDir.open_from_transport(to_transport)
395
            has_wt = to_bzr_dir.has_workingtree()
396
        except errors.NotBranchError:
397
            has_wt = False
398
        except errors.NotLocalUrl:
0.158.19 by Vincent Ladeuil
Bzr has facilities for exceptions, let's use them.
399
            # The exception raised is a bit weird... but that's life.
0.158.9 by Gary van der Merwe
Add a check to make sure we are not uploading to an existing wt.
400
            has_wt = True
401
402
        if has_wt:
0.158.19 by Vincent Ladeuil
Bzr has facilities for exceptions, let's use them.
403
            raise CannotUploadToWorkingTreeError(location)
0.158.9 by Gary van der Merwe
Add a check to make sure we are not uploading to an existing wt.
404
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
405
        if revision is None:
406
            rev_id = branch.last_revision()
407
        else:
408
            if len(revision) != 1:
409
                raise errors.BzrCommandError(
410
                    'bzr upload --revision takes exactly 1 argument')
411
            rev_id = revision[0].in_history(branch).rev_id
412
413
        tree = branch.repository.revision_tree(rev_id)
414
415
        uploader = BzrUploader(branch, to_transport, self.outf, tree,
416
                rev_id, quiet=quiet)
417
418
        if full:
419
            uploader.upload_full_tree()
420
        else:
421
            uploader.upload_tree()
422
423
        # We uploaded successfully, remember it
424
        if get_upload_location(branch) is None or remember:
425
            set_upload_location(branch, to_transport.base)
0.155.4 by James Westby
Add the groundwork for --auto that enables the hook for a branch.
426
        if auto is not None:
427
            set_upload_auto(branch, auto)
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
428
429
0.152.1 by Vincent Ladeuil
Empty shell
430
commands.register_command(cmd_upload)
431
0.152.4 by v.ladeuil+lp at free
Implement a trivial implementation to make one test pass.
432
0.155.2 by James Westby
Add a post_change_branch_tip hook to upload.
433
from bzrlib.plugins.upload.auto_upload_hook import auto_upload_hook
434
0.157.1 by James Westby
Don't try and register the hook if install_named_hook is not available
435
436
if hasattr(branch.Branch.hooks, "install_named_hook"):
437
    branch.Branch.hooks.install_named_hook('post_change_branch_tip',
438
            auto_upload_hook,
439
            'Auto upload code from a branch when it is changed.')
440
    auto_hook_available = True
441
else:
442
    auto_hook_available = False
0.155.2 by James Westby
Add a post_change_branch_tip hook to upload.
443
444
0.152.29 by Vincent Ladeuil
Work around test fixture limitation regarding self.outf (cough). All tests passing again.
445
def load_tests(basic_tests, module, loader):
0.153.1 by Vincent Ladeuil
Clean up references to verbose.
446
    # This module shouldn't define any tests but I don't know how to report
0.154.1 by Vincent Ladeuil
Create a simple setup.py and rework tests modules accordingly.
447
    # that. I prefer to update basic_tests with the other tests to detect
448
    # unwanted tests and I think that's sufficient.
0.152.1 by Vincent Ladeuil
Empty shell
449
450
    testmod_names = [
0.154.1 by Vincent Ladeuil
Create a simple setup.py and rework tests modules accordingly.
451
        'tests',
0.152.1 by Vincent Ladeuil
Empty shell
452
        ]
0.154.1 by Vincent Ladeuil
Create a simple setup.py and rework tests modules accordingly.
453
    basic_tests.addTest(loader.loadTestsFromModuleNames(
0.152.1 by Vincent Ladeuil
Empty shell
454
            ["%s.%s" % (__name__, tmn) for tmn in testmod_names]))
0.154.1 by Vincent Ladeuil
Create a simple setup.py and rework tests modules accordingly.
455
    return basic_tests