/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.152.72 by Vincent Ladeuil
Avoid the recursive loading to be able to BZR_PLUGINS_AT.
1
# Copyright (C) 2008, 2009, 2010 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.63 by Vincent Ladeuil
Make the doc more easily discoverable.
19
Quickstart
20
----------
21
22
To get started, it's as simple as running::
23
24
    bzr upload sftp://user@host/location/on/webserver
25
26
This will initially upload the whole working tree, and leave a file on the
27
remote location indicating the last revision that was uploaded
28
(.bzr-upload.revid), in order to avoid uploading unnecessary information the
29
next time.
30
31
If you would like to upload a specific revision, you just do:
32
33
    bzr upload -r X  sftp://user@host/location/on/webserver
34
35
bzr-upload, just as bzr does, will remember the location where you upload the 
36
first time, so you don't need to specify it every time.
37
38
If you need to re-upload the whole working tree for some reason, you can:
39
40
    bzr upload --full sftp://user@host/location/on/webserver
41
0.159.4 by Gary van der Merwe
Check that the revision we are uploading is a descendent from the revision that was uploaded.
42
This command only works on the revision beening uploaded is a decendent of the
43
revision that was previously uploaded, and that they are hence from branches
44
that have not diverged. Branches are considered diverged if the destination
45
branch's most recent commit is one that has not been merged (directly or
46
indirectly) by the source branch.
47
48
If branches have diverged, you can use 'bzr upload --overwrite' to replace
49
the other branch completely, discarding its unmerged changes.
50
0.152.63 by Vincent Ladeuil
Make the doc more easily discoverable.
51
52
Automatically Uploading
53
-----------------------
54
55
bzr-upload comes with a hook that can be used to trigger an upload whenever
56
the tip of the branch changes, including on commit, push, uncommit etc. This
57
would allow you to keep the code on the target up to date automatically.
58
59
The easiest way to enable this is to run upload with the --auto option.
60
61
     bzr upload --auto
62
63
will enable the hook for this branch. If you were to do a commit in this branch
64
now you would see it trigger the upload automatically.
65
66
If you wish to disable this for a branch again then you can use the --no-auto
67
option.
68
69
     bzr upload --no-auto
70
71
will disable the feature for that branch.
72
73
Since the auto hook is triggered automatically, you can't use the --quiet
74
option available for the upload command. Instead, you can set the
75
'upload_auto_quiet' configuration variable to True or False in either
76
bazaar.conf, locations.conf or branch.conf.
77
78
79
Storing the '.bzr-upload.revid' file
80
------------------------------------
81
0.152.17 by Vincent Ladeuil
Handle deletes (trivial implementation).
82
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.
83
revision id. The uploaded working tree is not linked to any other bzr data.
84
0.152.63 by Vincent Ladeuil
Make the doc more easily discoverable.
85
If the layout of your remote server is such that you can't write in the
86
root directory but only in the directories inside that root, you will need
87
to use the 'upload_revid_location' configuration variable to specify the
88
relative path to be used. That configuration variable can be specified in
89
locations.conf or branch.conf.
90
91
For example, given the following layout:
92
93
  Project/
94
    private/
95
    public/
96
97
you may have write access in 'private' and 'public' but in 'Project'
98
itself. In that case, you can add the following in your locations.conf or
99
branch.conf file:
100
101
  upload_revid_location = private/.bzr-upload.revid
102
103
104
Upload from Remote Location
105
---------------------------
106
107
It is possible to upload to a remote location from another remote location by
108
specifying it with the --directory option:
109
110
    bzr upload ftp://public.example.com --directory sftp://private.example.com 
111
112
This, together with --auto, can be used to upload when you push to your
113
central branch, rather than when you commit to your local branch.
114
115
Note that you will consume more bandwith this way than uploading from a local
116
branch.
117
0.160.5 by Martin Albisetti
Add docs
118
Ignoring certain files
119
-----------------------
120
121
If you want to version a file, but not upload it, you can create a file called
122
.bzrignore-upload, which works in the same way as the regular .bzrignore file,
123
but only applies to bzr-upload.
124
125
0.152.63 by Vincent Ladeuil
Make the doc more easily discoverable.
126
Collaborating
127
-------------
128
129
While we don't have any platform setup, you can branch from trunk:
130
131
    bzr branch lp:bzr-upload
132
0.160.5 by Martin Albisetti
Add docs
133
And change anything you'd like, and file a merge proposal on Launchpad.
0.152.63 by Vincent Ladeuil
Make the doc more easily discoverable.
134
135
136
Known Issues
137
------------
138
0.162.1 by Vincent Ladeuil
Emit warnings instead of crashing when symlinks are encountered
139
 * Symlinks are not supported (warnings are emitted when they are encountered).
0.152.63 by Vincent Ladeuil
Make the doc more easily discoverable.
140
141
0.152.6 by Vincent Ladeuil
Really use the transports and test against all targeted protocols.
142
"""
0.152.1 by Vincent Ladeuil
Empty shell
143
0.152.17 by Vincent Ladeuil
Handle deletes (trivial implementation).
144
# TODO: the chmod bits *can* be supported via the upload protocols
145
# (i.e. poorly), but since the web developers use these protocols to upload
146
# manually, it is expected that the associated web server is coherent with
147
# their presence/absence. In other words, if a web hosting provider requires
148
# chmod bits but don't provide an ftp server that support them, well, better
149
# find another provider ;-)
150
0.152.44 by Vincent Ladeuil
More robust full upload (at least regarding files changed to dirs and vice-versa).
151
# TODO: The message emitted in verbose mode displays local paths. That may be
152
# scary for the user when we say 'Deleting <path>' and are referring to
153
# remote files...
0.152.19 by Vincent Ladeuil
Handle kind_change. Trivial implementation, blocked by bug #205636.
154
0.152.78 by Vincent Ladeuil
Prepare 1.0.0 release by being a better packager and bzr citizen.
155
import bzrlib
156
import bzrlib.api
157
158
from info import (
159
    bzr_plugin_version as version_info,
160
    bzr_compatible_versions,
161
    )
162
163
if version_info[3] == 'final':
164
    version_string = '%d.%d.%d' % version_info[:3]
165
else:
166
    version_string = '%d.%d.%d%s%d' % version_info
167
__version__ = version_string
168
169
bzrlib.api.require_any_api(bzrlib, bzr_compatible_versions)
170
0.152.3 by v.ladeuil+lp at free
Make the tests fail not error out.
171
from bzrlib import (
0.155.2 by James Westby
Add a post_change_branch_tip hook to upload.
172
    branch,
0.152.3 by v.ladeuil+lp at free
Make the tests fail not error out.
173
    commands,
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
174
    lazy_import,
0.152.3 by v.ladeuil+lp at free
Make the tests fail not error out.
175
    option,
176
    )
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
177
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).
178
import stat
0.152.72 by Vincent Ladeuil
Avoid the recursive loading to be able to BZR_PLUGINS_AT.
179
import sys
0.152.44 by Vincent Ladeuil
More robust full upload (at least regarding files changed to dirs and vice-versa).
180
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
181
from bzrlib import (
0.158.1 by Gary van der Merwe
Don't require a working tree.
182
    bzrdir,
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
183
    errors,
0.161.4 by Vincent Ladeuil
Handle regexps in .bzrignore-upload and take parents into account.
184
    globbing,
0.160.3 by Martin Albisetti
Start implementing
185
    ignores,
0.152.73 by Vincent Ladeuil
Fix imports (order and a missing one).
186
    osutils,
187
    revision,
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
188
    revisionspec,
0.152.73 by Vincent Ladeuil
Fix imports (order and a missing one).
189
    trace,
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
190
    transport,
0.152.40 by Martin Albisetti
We need to import urlutils if we are going to use it
191
    urlutils,
0.152.27 by Martin Albisetti
* Added error message if the working tree has uncommited changes
192
    workingtree,
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
193
    )
194
""")
0.152.16 by Vincent Ladeuil
Handle renames. Robust implementation.
195
0.152.78 by Vincent Ladeuil
Prepare 1.0.0 release by being a better packager and bzr citizen.
196
version_info = (1, 0, 0, 'final', 0)
0.152.23 by Martin Albisetti
Added version_info and plugin_name
197
plugin_name = 'upload'
198
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
199
0.155.3 by James Westby
Add some tests for the hook, rename the option to "upload_auto"
200
def _get_branch_option(branch, option):
201
    return branch.get_config().get_user_option(option)
202
0.152.64 by Vincent Ladeuil
Fix compatibility with bzr.
203
# FIXME: Get rid of that as soon as we depend on a bzr API that includes
204
# get_user_option_as_bool
205
def _get_branch_bool_option(branch, option):
206
    conf = branch.get_config()
207
    if hasattr(conf, 'get_user_option_as_bool'):
208
        value = conf.get_user_option_as_bool(option)
209
    else:
210
        value = conf.get_user_option(option)
211
        if value is not None:
212
            if value.lower().strip() == 'true':
213
                value = True
214
            else:
215
                value = False
216
    return value
0.152.61 by Vincent Ladeuil
Fix bug #312686 and add an 'upload_auto_quiet' config variable.
217
0.155.3 by James Westby
Add some tests for the hook, rename the option to "upload_auto"
218
def _set_branch_option(branch, option, value):
219
    branch.get_config().set_user_option(option, value)
220
0.152.61 by Vincent Ladeuil
Fix bug #312686 and add an 'upload_auto_quiet' config variable.
221
0.155.3 by James Westby
Add some tests for the hook, rename the option to "upload_auto"
222
def get_upload_location(branch):
223
    return _get_branch_option(branch, 'upload_location')
224
0.152.61 by Vincent Ladeuil
Fix bug #312686 and add an 'upload_auto_quiet' config variable.
225
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
226
def set_upload_location(branch, location):
0.155.3 by James Westby
Add some tests for the hook, rename the option to "upload_auto"
227
    _set_branch_option(branch, 'upload_location', location)
228
0.152.61 by Vincent Ladeuil
Fix bug #312686 and add an 'upload_auto_quiet' config variable.
229
0.152.62 by Vincent Ladeuil
Fix bug #423331 by adding a way to configure the path used to
230
# FIXME: Add more tests around invalid paths used here or relative paths that
231
# doesn't exist on remote (if only to get proper error messages)
232
def get_upload_revid_location(branch):
233
    loc =  _get_branch_option(branch, 'upload_revid_location')
234
    if loc is None:
235
        loc = '.bzr-upload.revid'
236
    return loc
237
238
239
def set_upload_revid_location(branch, location):
240
    _set_branch_option(branch, 'upload_revid_location', location)
241
242
0.155.3 by James Westby
Add some tests for the hook, rename the option to "upload_auto"
243
def get_upload_auto(branch):
0.152.64 by Vincent Ladeuil
Fix compatibility with bzr.
244
    auto = _get_branch_bool_option(branch, 'upload_auto')
0.152.61 by Vincent Ladeuil
Fix bug #312686 and add an 'upload_auto_quiet' config variable.
245
    if auto is None:
246
        auto = False # Default to False if not specified
247
    return auto
248
0.155.3 by James Westby
Add some tests for the hook, rename the option to "upload_auto"
249
250
def set_upload_auto(branch, auto):
0.152.61 by Vincent Ladeuil
Fix bug #312686 and add an 'upload_auto_quiet' config variable.
251
    # FIXME: What's the point in allowing a boolean here instead of requiring
252
    # the callers to use strings instead ?
0.155.3 by James Westby
Add some tests for the hook, rename the option to "upload_auto"
253
    if auto:
254
        auto_str = "True"
255
    else:
256
        auto_str = "False"
257
    _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.
258
259
0.152.61 by Vincent Ladeuil
Fix bug #312686 and add an 'upload_auto_quiet' config variable.
260
def get_upload_auto_quiet(branch):
0.152.64 by Vincent Ladeuil
Fix compatibility with bzr.
261
    quiet = _get_branch_bool_option(branch, 'upload_auto_quiet')
0.152.61 by Vincent Ladeuil
Fix bug #312686 and add an 'upload_auto_quiet' config variable.
262
    if quiet is None:
263
        quiet = False # Default to False if not specified
264
    return quiet
265
266
267
def set_upload_auto_quiet(branch, quiet):
268
    _set_branch_option(branch, 'upload_auto_quiet', quiet)
269
270
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
271
class BzrUploader(object):
272
273
    def __init__(self, branch, to_transport, outf, tree, rev_id,
0.152.62 by Vincent Ladeuil
Fix bug #423331 by adding a way to configure the path used to
274
                 quiet=False):
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
275
        self.branch = branch
276
        self.to_transport = to_transport
277
        self.outf = outf
278
        self.tree = tree
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
279
        self.rev_id = rev_id
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
280
        self.quiet = quiet
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
281
        self._pending_deletions = []
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
282
        self._pending_renames = []
0.159.1 by Gary van der Merwe
Cache the uploaded_revid, so that we can query it more that once, with out having to worry about extra network calls.
283
        self._uploaded_revid = None
0.161.4 by Vincent Ladeuil
Handle regexps in .bzrignore-upload and take parents into account.
284
        self._ignored = None
0.152.12 by Vincent Ladeuil
Implement 'upload_location' in config files.
285
0.152.70 by Vincent Ladeuil
Ensure we use the transport API correctly for unicode paths.
286
    def _up_stat(self, relpath):
287
        return self.to_transport.stat(urlutils.escape(relpath))
288
289
    def _up_rename(self, old_path, new_path):
290
        return self.to_transport.rename(urlutils.escape(old_path),
291
                                        urlutils.escape(new_path))
292
293
    def _up_delete(self, relpath):
294
        return self.to_transport.delete(urlutils.escape(relpath))
295
296
    def _up_delete_tree(self, relpath):
297
        return self.to_transport.delete_tree(urlutils.escape(relpath))
298
299
    def _up_mkdir(self, relpath, mode):
300
        return self.to_transport.mkdir(urlutils.escape(relpath), mode)
301
302
    def _up_rmdir(self, relpath):
303
        return self.to_transport.rmdir(urlutils.escape(relpath))
304
305
    def _up_put_bytes(self, relpath, bytes, mode):
306
        self.to_transport.put_bytes(urlutils.escape(relpath), bytes, mode)
307
308
    def _up_get_bytes(self, relpath):
309
        return self.to_transport.get_bytes(urlutils.escape(relpath))
310
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
311
    def set_uploaded_revid(self, rev_id):
0.152.10 by Vincent Ladeuil
Fix incremental upload cheat.
312
        # XXX: Add tests for concurrent updates, etc.
0.152.62 by Vincent Ladeuil
Fix bug #423331 by adding a way to configure the path used to
313
        revid_path = get_upload_revid_location(self.branch)
0.152.70 by Vincent Ladeuil
Ensure we use the transport API correctly for unicode paths.
314
        self.to_transport.put_bytes(urlutils.escape(revid_path), rev_id)
0.159.1 by Gary van der Merwe
Cache the uploaded_revid, so that we can query it more that once, with out having to worry about extra network calls.
315
        self._uploaded_revid = rev_id
0.152.10 by Vincent Ladeuil
Fix incremental upload cheat.
316
317
    def get_uploaded_revid(self):
0.159.1 by Gary van der Merwe
Cache the uploaded_revid, so that we can query it more that once, with out having to worry about extra network calls.
318
        if self._uploaded_revid is None:
319
            revid_path = get_upload_revid_location(self.branch)
0.159.2 by Gary van der Merwe
If there has not been an upload, treat the uploaded revid as revision.NULL_REVISION. This enables this request to be cached.
320
            try:
0.152.70 by Vincent Ladeuil
Ensure we use the transport API correctly for unicode paths.
321
                self._uploaded_revid = self._up_get_bytes(revid_path)
0.159.2 by Gary van der Merwe
If there has not been an upload, treat the uploaded revid as revision.NULL_REVISION. This enables this request to be cached.
322
            except errors.NoSuchFile:
323
                # We have not upload to here.
324
                self._uploaded_revid = revision.NULL_REVISION
0.159.1 by Gary van der Merwe
Cache the uploaded_revid, so that we can query it more that once, with out having to worry about extra network calls.
325
        return self._uploaded_revid
0.152.7 by Vincent Ladeuil
Slight refactoring.
326
0.161.4 by Vincent Ladeuil
Handle regexps in .bzrignore-upload and take parents into account.
327
    def _get_ignored(self):
328
        if self._ignored is None:
329
            try:
330
                ignore_file = self.tree.get_file_by_path('.bzrignore-upload')
331
                ignored_patterns = ignores.parse_ignore_file(ignore_file)
332
            except errors.NoSuchId:
333
                ignored_patterns = []
334
            self._ignored = globbing.Globster(ignored_patterns)
335
        return self._ignored
336
337
    def is_ignored(self, relpath):
338
        glob = self._get_ignored()
339
        ignored = glob.match(relpath)
340
        import os
341
        if not ignored:
342
            # We still need to check that all parents are not ignored
343
            dir = os.path.dirname(relpath)
344
            while dir and not ignored:
345
                ignored = glob.match(dir)
346
                if not ignored:
347
                    dir = os.path.dirname(dir)
348
        return ignored
0.160.3 by Martin Albisetti
Start implementing
349
0.152.46 by Vincent Ladeuil
Handle x mode bit for files and provides default mode bits for
350
    def upload_file(self, relpath, id, mode=None):
0.161.5 by Vincent Ladeuil
Cleanup.
351
        if mode is None:
352
            if self.tree.is_executable(id):
353
                mode = 0775
354
            else:
355
                mode = 0664
356
        if not self.quiet:
357
            self.outf.write('Uploading %s\n' % relpath)
0.152.70 by Vincent Ladeuil
Ensure we use the transport API correctly for unicode paths.
358
        self._up_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.
359
0.152.46 by Vincent Ladeuil
Handle x mode bit for files and provides default mode bits for
360
    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).
361
        """Upload a file, clearing the way on the remote side.
362
363
        When doing a full upload, it may happen that a directory exists where
364
        we want to put our file.
365
        """
366
        try:
0.152.70 by Vincent Ladeuil
Ensure we use the transport API correctly for unicode paths.
367
            st = self._up_stat(relpath)
0.152.44 by Vincent Ladeuil
More robust full upload (at least regarding files changed to dirs and vice-versa).
368
            if stat.S_ISDIR(st.st_mode):
369
                # A simple rmdir may not be enough
370
                if not self.quiet:
371
                    self.outf.write('Clearing %s/%s\n' % (
372
                            self.to_transport.external_url(), relpath))
0.152.70 by Vincent Ladeuil
Ensure we use the transport API correctly for unicode paths.
373
                self._up_delete_tree(relpath)
0.152.44 by Vincent Ladeuil
More robust full upload (at least regarding files changed to dirs and vice-versa).
374
        except errors.PathError:
375
            pass
0.152.46 by Vincent Ladeuil
Handle x mode bit for files and provides default mode bits for
376
        self.upload_file(relpath, id, mode)
377
378
    def make_remote_dir(self, relpath, mode=None):
0.161.5 by Vincent Ladeuil
Cleanup.
379
        if mode is None:
380
            mode = 0775
0.152.70 by Vincent Ladeuil
Ensure we use the transport API correctly for unicode paths.
381
        self._up_mkdir(relpath, mode)
0.152.46 by Vincent Ladeuil
Handle x mode bit for files and provides default mode bits for
382
383
    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).
384
        """Create a remote directory, clearing the way on the remote side.
385
386
        When doing a full upload, it may happen that a file exists where we
387
        want to create our directory.
388
        """
389
        try:
0.152.70 by Vincent Ladeuil
Ensure we use the transport API correctly for unicode paths.
390
            st = self._up_stat(relpath)
0.152.44 by Vincent Ladeuil
More robust full upload (at least regarding files changed to dirs and vice-versa).
391
            if not stat.S_ISDIR(st.st_mode):
392
                if not self.quiet:
393
                    self.outf.write('Deleting %s/%s\n' % (
394
                            self.to_transport.external_url(), relpath))
0.152.70 by Vincent Ladeuil
Ensure we use the transport API correctly for unicode paths.
395
                self._up_delete(relpath)
0.152.47 by Vincent Ladeuil
Don't fail a full upload on an already existing dir.
396
            else:
397
                # Ok the remote dir already exists, nothing to do
398
                return
0.152.44 by Vincent Ladeuil
More robust full upload (at least regarding files changed to dirs and vice-versa).
399
        except errors.PathError:
400
            pass
0.152.46 by Vincent Ladeuil
Handle x mode bit for files and provides default mode bits for
401
        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).
402
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
403
    def delete_remote_file(self, relpath):
0.161.5 by Vincent Ladeuil
Cleanup.
404
        if not self.quiet:
405
            self.outf.write('Deleting %s\n' % relpath)
0.152.70 by Vincent Ladeuil
Ensure we use the transport API correctly for unicode paths.
406
        self._up_delete(relpath)
0.152.17 by Vincent Ladeuil
Handle deletes (trivial implementation).
407
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
408
    def delete_remote_dir(self, relpath):
0.161.5 by Vincent Ladeuil
Cleanup.
409
        if not self.quiet:
410
            self.outf.write('Deleting %s\n' % relpath)
0.152.70 by Vincent Ladeuil
Ensure we use the transport API correctly for unicode paths.
411
        self._up_rmdir(relpath)
0.161.5 by Vincent Ladeuil
Cleanup.
412
        # XXX: Add a test where a subdir is ignored but we still want to
413
        # delete the dir -- vila 100106
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
414
415
    def delete_remote_dir_maybe(self, relpath):
416
        """Try to delete relpath, keeping failures to retry later."""
417
        try:
0.152.70 by Vincent Ladeuil
Ensure we use the transport API correctly for unicode paths.
418
            self._up_rmdir(relpath)
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
419
        # any kind of PathError would be OK, though we normally expect
420
        # DirectoryNotEmpty
421
        except errors.PathError:
422
            self._pending_deletions.append(relpath)
423
424
    def finish_deletions(self):
425
        if self._pending_deletions:
426
            # Process the previously failed deletions in reverse order to
427
            # delete children before parents
428
            for relpath in reversed(self._pending_deletions):
0.152.70 by Vincent Ladeuil
Ensure we use the transport API correctly for unicode paths.
429
                self._up_rmdir(relpath)
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
430
            # The following shouldn't be needed since we use it once per
431
            # upload, but better safe than sorry ;-)
432
            self._pending_deletions = []
433
0.152.16 by Vincent Ladeuil
Handle renames. Robust implementation.
434
    def rename_remote(self, old_relpath, new_relpath):
435
        """Rename a remote file or directory taking care of collisions.
436
437
        To avoid collisions during bulk renames, each renamed target is
438
        temporarily assigned a unique name. When all renames have been done,
439
        each target get its proper name.
440
        """
441
        # We generate a sufficiently random name to *assume* that
442
        # no collisions will occur and don't worry about it (nor
443
        # handle it).
444
        import os
445
        import random
446
        import time
447
448
        stamp = '.tmp.%.9f.%d.%d' % (time.time(),
449
                                     os.getpid(),
450
                                     random.randint(0,0x7FFFFFFF))
0.152.34 by Martin Albisetti
* Change the default behaviour to be more verbose
451
        if not self.quiet:
0.152.30 by Vincent Ladeuil
Comply to verbose.
452
            self.outf.write('Renaming %s to %s\n' % (old_relpath, new_relpath))
0.152.70 by Vincent Ladeuil
Ensure we use the transport API correctly for unicode paths.
453
        self._up_rename(old_relpath, stamp)
0.152.16 by Vincent Ladeuil
Handle renames. Robust implementation.
454
        self._pending_renames.append((stamp, new_relpath))
455
456
    def finish_renames(self):
457
        for (stamp, new_path) in self._pending_renames:
0.152.70 by Vincent Ladeuil
Ensure we use the transport API correctly for unicode paths.
458
            self._up_rename(stamp, new_path)
0.152.16 by Vincent Ladeuil
Handle renames. Robust implementation.
459
        # The following shouldn't be needed since we use it once per upload,
460
        # but better safe than sorry ;-)
461
        self._pending_renames = []
462
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
463
    def upload_full_tree(self):
0.152.14 by Vincent Ladeuil
Handle renames (trivial implementation).
464
        self.to_transport.ensure_base() # XXX: Handle errors (add
465
                                        # --create-prefix option ?)
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
466
        self.tree.lock_read()
467
        try:
0.152.44 by Vincent Ladeuil
More robust full upload (at least regarding files changed to dirs and vice-versa).
468
            for relpath, ie in self.tree.inventory.iter_entries():
0.160.4 by Martin Albisetti
Test passes!
469
                if relpath in ('', '.bzrignore', '.bzrignore-upload'):
0.152.10 by Vincent Ladeuil
Fix incremental upload cheat.
470
                    # skip root ('')
0.161.4 by Vincent Ladeuil
Handle regexps in .bzrignore-upload and take parents into account.
471
                    # .bzrignore and .bzrignore-upload have no meaning outside
472
                    # a working tree so do not upload them
473
                    continue
474
                if self.is_ignored(relpath):
475
                    if not self.quiet:
476
                        self.outf.write('Ignoring %s\n' % relpath)
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
477
                    continue
0.152.8 by Vincent Ladeuil
Handle uploading directories.
478
                if ie.kind == 'file':
0.152.44 by Vincent Ladeuil
More robust full upload (at least regarding files changed to dirs and vice-versa).
479
                    self.upload_file_robustly(relpath, ie.file_id)
0.152.8 by Vincent Ladeuil
Handle uploading directories.
480
                elif ie.kind == 'directory':
0.152.44 by Vincent Ladeuil
More robust full upload (at least regarding files changed to dirs and vice-versa).
481
                    self.make_remote_dir_robustly(relpath)
0.162.1 by Vincent Ladeuil
Emit warnings instead of crashing when symlinks are encountered
482
                elif ie.kind == 'symlink':
483
                    if not self.quiet:
484
                        target = self.tree.path_content_summary(path)[3]
485
                        self.outf.write('Not uploading symlink %s -> %s\n'
486
                                        % (path, target))
0.152.8 by Vincent Ladeuil
Handle uploading directories.
487
                else:
488
                    raise NotImplementedError
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
489
            self.set_uploaded_revid(self.rev_id)
490
        finally:
491
            self.tree.unlock()
492
0.152.10 by Vincent Ladeuil
Fix incremental upload cheat.
493
    def upload_tree(self):
0.152.24 by Martin Albisetti
Changed the way we upload the full tree if its never been uploaded
494
        # If we can't find the revid file on the remote location, upload the
495
        # full tree instead
0.159.2 by Gary van der Merwe
If there has not been an upload, treat the uploaded revid as revision.NULL_REVISION. This enables this request to be cached.
496
        rev_id = self.get_uploaded_revid()
0.160.2 by Martin Albisetti
Clean up whitespace to appease the neurotic in me
497
0.159.2 by Gary van der Merwe
If there has not been an upload, treat the uploaded revid as revision.NULL_REVISION. This enables this request to be cached.
498
        if rev_id == revision.NULL_REVISION:
0.152.34 by Martin Albisetti
* Change the default behaviour to be more verbose
499
            if not self.quiet:
0.152.29 by Vincent Ladeuil
Work around test fixture limitation regarding self.outf (cough). All tests passing again.
500
                self.outf.write('No uploaded revision id found,'
0.152.34 by Martin Albisetti
* Change the default behaviour to be more verbose
501
                                ' switching to full upload\n')
0.152.24 by Martin Albisetti
Changed the way we upload the full tree if its never been uploaded
502
            self.upload_full_tree()
0.152.29 by Vincent Ladeuil
Work around test fixture limitation regarding self.outf (cough). All tests passing again.
503
            # We're done
504
            return
0.152.24 by Martin Albisetti
Changed the way we upload the full tree if its never been uploaded
505
0.152.35 by Martin Albisetti
* Tell the user if the remote location is already up to date
506
        # Check if the revision hasn't already been uploaded
507
        if rev_id == self.rev_id:
508
            if not self.quiet:
509
                self.outf.write('Remote location already up to date\n')
510
0.152.10 by Vincent Ladeuil
Fix incremental upload cheat.
511
        from_tree = self.branch.repository.revision_tree(rev_id)
0.152.14 by Vincent Ladeuil
Handle renames (trivial implementation).
512
        self.to_transport.ensure_base() # XXX: Handle errors (add
513
                                        # --create-prefix option ?)
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
514
        changes = self.tree.changes_from(from_tree)
515
        self.tree.lock_read()
516
        try:
0.152.17 by Vincent Ladeuil
Handle deletes (trivial implementation).
517
            for (path, id, kind) in changes.removed:
0.161.4 by Vincent Ladeuil
Handle regexps in .bzrignore-upload and take parents into account.
518
                if self.is_ignored(path):
519
                    if not self.quiet:
520
                        self.outf.write('Ignoring %s\n' % path)
521
                    continue
0.152.17 by Vincent Ladeuil
Handle deletes (trivial implementation).
522
                if kind is 'file':
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
523
                    self.delete_remote_file(path)
524
                elif kind is  'directory':
525
                    self.delete_remote_dir_maybe(path)
0.162.1 by Vincent Ladeuil
Emit warnings instead of crashing when symlinks are encountered
526
                elif kind == 'symlink':
527
                    if not self.quiet:
528
                        target = self.tree.path_content_summary(path)[3]
529
                        self.outf.write('Not deleting remote symlink %s -> %s\n'
530
                                        % (path, target))
0.152.17 by Vincent Ladeuil
Handle deletes (trivial implementation).
531
                else:
532
                    raise NotImplementedError
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
533
0.152.14 by Vincent Ladeuil
Handle renames (trivial implementation).
534
            for (old_path, new_path, id, kind,
535
                 content_change, exec_change) in changes.renamed:
0.161.4 by Vincent Ladeuil
Handle regexps in .bzrignore-upload and take parents into account.
536
                if self.is_ignored(old_path) and self.is_ignored(new_path):
537
                    if not self.quiet:
538
                        self.outf.write('Ignoring %s\n' % old_path)
539
                        self.outf.write('Ignoring %s\n' % new_path)
540
                    continue
0.152.52 by Vincent Ladeuil
Fix bug #270219 by handling content changes during renames.
541
                if content_change:
542
                    # We update the old_path content because renames and
543
                    # deletions are differed.
544
                    self.upload_file(old_path, id)
0.162.1 by Vincent Ladeuil
Emit warnings instead of crashing when symlinks are encountered
545
                if kind == 'symlink':
546
                    if not self.quiet:
547
                        self.outf.write('Not renaming remote symlink %s to %s\n'
548
                                        % (old_path, new_path))
549
                else:
550
                    self.rename_remote(old_path, new_path)
0.152.16 by Vincent Ladeuil
Handle renames. Robust implementation.
551
            self.finish_renames()
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
552
            self.finish_deletions()
0.152.16 by Vincent Ladeuil
Handle renames. Robust implementation.
553
0.152.19 by Vincent Ladeuil
Handle kind_change. Trivial implementation, blocked by bug #205636.
554
            for (path, id, old_kind, new_kind) in changes.kind_changed:
0.161.4 by Vincent Ladeuil
Handle regexps in .bzrignore-upload and take parents into account.
555
                if self.is_ignored(path):
556
                    if not self.quiet:
557
                        self.outf.write('Ignoring %s\n' % path)
558
                    continue
0.162.1 by Vincent Ladeuil
Emit warnings instead of crashing when symlinks are encountered
559
                if old_kind == 'file':
0.152.19 by Vincent Ladeuil
Handle kind_change. Trivial implementation, blocked by bug #205636.
560
                    self.delete_remote_file(path)
0.162.1 by Vincent Ladeuil
Emit warnings instead of crashing when symlinks are encountered
561
                elif old_kind ==  'directory':
0.152.19 by Vincent Ladeuil
Handle kind_change. Trivial implementation, blocked by bug #205636.
562
                    self.delete_remote_dir(path)
563
                else:
564
                    raise NotImplementedError
565
0.162.1 by Vincent Ladeuil
Emit warnings instead of crashing when symlinks are encountered
566
                if new_kind == 'file':
0.152.19 by Vincent Ladeuil
Handle kind_change. Trivial implementation, blocked by bug #205636.
567
                    self.upload_file(path, id)
568
                elif new_kind is 'directory':
569
                    self.make_remote_dir(path)
570
                else:
571
                    raise NotImplementedError
572
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
573
            for (path, id, kind) in changes.added:
0.161.4 by Vincent Ladeuil
Handle regexps in .bzrignore-upload and take parents into account.
574
                if self.is_ignored(path):
575
                    if not self.quiet:
576
                        self.outf.write('Ignoring %s\n' % path)
577
                    continue
0.162.1 by Vincent Ladeuil
Emit warnings instead of crashing when symlinks are encountered
578
                if kind == 'file':
0.152.7 by Vincent Ladeuil
Slight refactoring.
579
                    self.upload_file(path, id)
0.162.1 by Vincent Ladeuil
Emit warnings instead of crashing when symlinks are encountered
580
                elif kind == 'directory':
0.152.8 by Vincent Ladeuil
Handle uploading directories.
581
                    self.make_remote_dir(path)
0.162.1 by Vincent Ladeuil
Emit warnings instead of crashing when symlinks are encountered
582
                elif kind == 'symlink':
583
                    if not self.quiet:
584
                        target = self.tree.path_content_summary(path)[3]
585
                        self.outf.write('Not uploading symlink %s -> %s\n'
586
                                        % (path, target))
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
587
                else:
588
                    raise NotImplementedError
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
589
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
590
            # XXX: Add a test for exec_change
591
            for (path, id, kind,
592
                 content_change, exec_change) in changes.modified:
0.161.4 by Vincent Ladeuil
Handle regexps in .bzrignore-upload and take parents into account.
593
                if self.is_ignored(path):
594
                    if not self.quiet:
595
                        self.outf.write('Ignoring %s\n' % path)
596
                    continue
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
597
                if kind is 'file':
0.152.7 by Vincent Ladeuil
Slight refactoring.
598
                    self.upload_file(path, id)
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
599
                else:
600
                    raise NotImplementedError
0.152.18 by Vincent Ladeuil
Handle deletions. Robust implementation.
601
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
602
            self.set_uploaded_revid(self.rev_id)
603
        finally:
604
            self.tree.unlock()
0.152.4 by v.ladeuil+lp at free
Implement a trivial implementation to make one test pass.
605
0.152.57 by Vincent Ladeuil
Fix minor 2.4 compatibility bug.
606
0.152.68 by Vincent Ladeuil
Cleanup some pending changes.
607
class CannotUploadToWorkingTree(errors.BzrCommandError):
0.158.19 by Vincent Ladeuil
Bzr has facilities for exceptions, let's use them.
608
609
    _fmt = 'Cannot upload to a bzr managed working tree: %(url)s".'
610
0.152.1 by Vincent Ladeuil
Empty shell
611
0.152.68 by Vincent Ladeuil
Cleanup some pending changes.
612
class DivergedUploadedTree(errors.BzrCommandError):
613
614
    _fmt = ("Your branch (%(revid)s)"
615
            " and the uploaded tree (%(uploaded_revid)s) have diverged: ")
616
617
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
618
class cmd_upload(commands.Command):
619
    """Upload a working tree, as a whole or incrementally.
620
621
    If no destination is specified use the last one used.
622
    If no revision is specified upload the changes since the last upload.
0.152.56 by Vincent Ladeuil
Small tweaks including doc fix (#275538).
623
624
    Changes include files added, renamed, modified or removed.
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
625
    """
0.152.63 by Vincent Ladeuil
Make the doc more easily discoverable.
626
    _see_also = ['plugins/upload']
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
627
    takes_args = ['location?']
628
    takes_options = [
629
        'revision',
630
        'remember',
0.159.4 by Gary van der Merwe
Check that the revision we are uploading is a descendent from the revision that was uploaded.
631
        'overwrite',
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
632
        option.Option('full', 'Upload the full working tree.'),
633
        option.Option('quiet', 'Do not output what is being done.',
634
                       short_name='q'),
635
        option.Option('directory',
636
                      help='Branch to upload from, '
637
                      'rather than the one containing the working directory.',
638
                      short_name='d',
639
                      type=unicode,
640
                      ),
0.155.5 by James Westby
Hook up the --upload option and document it.
641
        option.Option('auto',
642
                      'Trigger an upload from this branch whenever the tip '
643
                      'revision changes.')
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
644
       ]
645
646
    def run(self, location=None, full=False, revision=None, remember=None,
0.159.4 by Gary van der Merwe
Check that the revision we are uploading is a descendent from the revision that was uploaded.
647
            directory=None, quiet=False, auto=None, overwrite=False
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
648
            ):
649
        if directory is None:
650
            directory = u'.'
0.158.7 by Gary van der Merwe
Clean up white space.
651
0.157.1 by James Westby
Don't try and register the hook if install_named_hook is not available
652
        if auto and not auto_hook_available:
653
            raise BzrCommandError("Your version of bzr does not have the "
654
                    "hooks necessary for --auto to work")
655
0.158.19 by Vincent Ladeuil
Bzr has facilities for exceptions, let's use them.
656
        (wt, branch,
657
         relpath) = bzrdir.BzrDir.open_containing_tree_or_branch(directory)
0.160.2 by Martin Albisetti
Clean up whitespace to appease the neurotic in me
658
0.158.1 by Gary van der Merwe
Don't require a working tree.
659
        if wt:
0.159.4 by Gary van der Merwe
Check that the revision we are uploading is a descendent from the revision that was uploaded.
660
            wt.lock_read()
0.152.68 by Vincent Ladeuil
Cleanup some pending changes.
661
            locked = wt
0.159.4 by Gary van der Merwe
Check that the revision we are uploading is a descendent from the revision that was uploaded.
662
        else:
663
            branch.lock_read()
0.152.68 by Vincent Ladeuil
Cleanup some pending changes.
664
            locked = branch
0.158.9 by Gary van der Merwe
Add a check to make sure we are not uploading to an existing wt.
665
        try:
0.159.4 by Gary van der Merwe
Check that the revision we are uploading is a descendent from the revision that was uploaded.
666
            if wt:
667
                changes = wt.changes_from(wt.basis_tree())
0.160.2 by Martin Albisetti
Clean up whitespace to appease the neurotic in me
668
0.161.2 by Martin Albisetti
Remove commented lines for debugging
669
                if revision is None and  changes.has_changed():
670
                    raise errors.UncommittedChanges(wt)
0.160.2 by Martin Albisetti
Clean up whitespace to appease the neurotic in me
671
0.159.4 by Gary van der Merwe
Check that the revision we are uploading is a descendent from the revision that was uploaded.
672
            if location is None:
673
                stored_loc = get_upload_location(branch)
674
                if stored_loc is None:
0.164.1 by Vincent Ladeuil
Add a test to cover bug #600628 but it is already fixed.
675
                    raise errors.BzrCommandError(
676
                        'No upload location known or specified.')
0.159.4 by Gary van der Merwe
Check that the revision we are uploading is a descendent from the revision that was uploaded.
677
                else:
678
                    # FIXME: Not currently tested
679
                    display_url = urlutils.unescape_for_display(stored_loc,
680
                            self.outf.encoding)
681
                    self.outf.write("Using saved location: %s\n" % display_url)
682
                    location = stored_loc
0.160.2 by Martin Albisetti
Clean up whitespace to appease the neurotic in me
683
0.159.4 by Gary van der Merwe
Check that the revision we are uploading is a descendent from the revision that was uploaded.
684
            to_transport = transport.get_transport(location)
0.160.2 by Martin Albisetti
Clean up whitespace to appease the neurotic in me
685
0.159.4 by Gary van der Merwe
Check that the revision we are uploading is a descendent from the revision that was uploaded.
686
            # Check that we are not uploading to a existing working tree.
687
            try:
688
                to_bzr_dir = bzrdir.BzrDir.open_from_transport(to_transport)
689
                has_wt = to_bzr_dir.has_workingtree()
690
            except errors.NotBranchError:
691
                has_wt = False
692
            except errors.NotLocalUrl:
693
                # The exception raised is a bit weird... but that's life.
694
                has_wt = True
0.160.2 by Martin Albisetti
Clean up whitespace to appease the neurotic in me
695
0.159.4 by Gary van der Merwe
Check that the revision we are uploading is a descendent from the revision that was uploaded.
696
            if has_wt:
0.152.68 by Vincent Ladeuil
Cleanup some pending changes.
697
                raise CannotUploadToWorkingTree(url=location)
0.159.4 by Gary van der Merwe
Check that the revision we are uploading is a descendent from the revision that was uploaded.
698
            if revision is None:
699
                rev_id = branch.last_revision()
700
            else:
701
                if len(revision) != 1:
702
                    raise errors.BzrCommandError(
703
                        'bzr upload --revision takes exactly 1 argument')
704
                rev_id = revision[0].in_history(branch).rev_id
0.160.2 by Martin Albisetti
Clean up whitespace to appease the neurotic in me
705
0.159.4 by Gary van der Merwe
Check that the revision we are uploading is a descendent from the revision that was uploaded.
706
            tree = branch.repository.revision_tree(rev_id)
0.160.2 by Martin Albisetti
Clean up whitespace to appease the neurotic in me
707
0.159.4 by Gary van der Merwe
Check that the revision we are uploading is a descendent from the revision that was uploaded.
708
            uploader = BzrUploader(branch, to_transport, self.outf, tree,
709
                                   rev_id, quiet=quiet)
0.160.2 by Martin Albisetti
Clean up whitespace to appease the neurotic in me
710
0.159.4 by Gary van der Merwe
Check that the revision we are uploading is a descendent from the revision that was uploaded.
711
            if not overwrite:
712
                prev_uploaded_rev_id = uploader.get_uploaded_revid()
713
                graph = branch.repository.get_graph()
714
                if not graph.is_ancestor(prev_uploaded_rev_id, rev_id):
0.152.68 by Vincent Ladeuil
Cleanup some pending changes.
715
                    raise DivergedUploadedTree(
716
                        revid=rev_id, uploaded_revid=prev_uploaded_rev_id)
0.159.4 by Gary van der Merwe
Check that the revision we are uploading is a descendent from the revision that was uploaded.
717
            if full:
718
                uploader.upload_full_tree()
719
            else:
720
                uploader.upload_tree()
721
        finally:
0.152.68 by Vincent Ladeuil
Cleanup some pending changes.
722
            locked.unlock()
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
723
724
        # We uploaded successfully, remember it
725
        if get_upload_location(branch) is None or remember:
0.152.70 by Vincent Ladeuil
Ensure we use the transport API correctly for unicode paths.
726
            set_upload_location(branch, urlutils.unescape(to_transport.base))
0.155.4 by James Westby
Add the groundwork for --auto that enables the hook for a branch.
727
        if auto is not None:
728
            set_upload_auto(branch, auto)
0.155.1 by James Westby
Switch most of the logic to a class outside of the command class.
729
730
0.152.1 by Vincent Ladeuil
Empty shell
731
commands.register_command(cmd_upload)
732
0.152.68 by Vincent Ladeuil
Cleanup some pending changes.
733
0.152.72 by Vincent Ladeuil
Avoid the recursive loading to be able to BZR_PLUGINS_AT.
734
def auto_upload_hook(params):
735
    source_branch = params.branch
736
    destination = get_upload_location(source_branch)
737
    if destination is None:
738
        return
739
    auto_upload = get_upload_auto(source_branch)
740
    if not auto_upload:
741
        return
742
    quiet = get_upload_auto_quiet(source_branch)
743
    if not quiet:
744
        display_url = urlutils.unescape_for_display(
745
            destination, osutils.get_terminal_encoding())
746
        trace.note('Automatically uploading to %s', display_url)
747
    to_transport = transport.get_transport(destination)
748
    last_revision = source_branch.last_revision()
749
    last_tree = source_branch.repository.revision_tree(last_revision)
750
    uploader = BzrUploader(source_branch, to_transport, sys.stdout,
751
                           last_tree, last_revision, quiet=quiet)
752
    uploader.upload_tree()
753
754
0.152.61 by Vincent Ladeuil
Fix bug #312686 and add an 'upload_auto_quiet' config variable.
755
def install_auto_upload_hook():
0.157.1 by James Westby
Don't try and register the hook if install_named_hook is not available
756
    branch.Branch.hooks.install_named_hook('post_change_branch_tip',
0.152.72 by Vincent Ladeuil
Avoid the recursive loading to be able to BZR_PLUGINS_AT.
757
            auto_upload_hook,
0.157.1 by James Westby
Don't try and register the hook if install_named_hook is not available
758
            'Auto upload code from a branch when it is changed.')
0.152.61 by Vincent Ladeuil
Fix bug #312686 and add an 'upload_auto_quiet' config variable.
759
760
761
if hasattr(branch.Branch.hooks, "install_named_hook"):
762
    install_auto_upload_hook()
0.157.1 by James Westby
Don't try and register the hook if install_named_hook is not available
763
    auto_hook_available = True
764
else:
765
    auto_hook_available = False
0.155.2 by James Westby
Add a post_change_branch_tip hook to upload.
766
767
0.152.29 by Vincent Ladeuil
Work around test fixture limitation regarding self.outf (cough). All tests passing again.
768
def load_tests(basic_tests, module, loader):
0.153.1 by Vincent Ladeuil
Clean up references to verbose.
769
    # 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.
770
    # that. I prefer to update basic_tests with the other tests to detect
771
    # unwanted tests and I think that's sufficient.
0.152.1 by Vincent Ladeuil
Empty shell
772
773
    testmod_names = [
0.154.1 by Vincent Ladeuil
Create a simple setup.py and rework tests modules accordingly.
774
        'tests',
0.152.1 by Vincent Ladeuil
Empty shell
775
        ]
0.154.1 by Vincent Ladeuil
Create a simple setup.py and rework tests modules accordingly.
776
    basic_tests.addTest(loader.loadTestsFromModuleNames(
0.152.1 by Vincent Ladeuil
Empty shell
777
            ["%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.
778
    return basic_tests