/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
0.169.2 by Vincent Ladeuil
Fix deprecation warning about tree.inventory usage
1
# Copyright (C) 2011, 2012 Canonical Ltd
0.165.1 by Jelmer Vernooij
Support lazily loading.
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
17
"""bzr-upload command implementations."""
18
6645.2.2 by Jelmer Vernooij
Merge lp:brz.
19
from __future__ import absolute_import
20
6645.2.1 by Jelmer Vernooij
Bundle the upload plugin.
21
from ... import (
0.165.1 by Jelmer Vernooij
Support lazily loading.
22
    commands,
0.152.93 by Vincent Ladeuil
Migrate to config stacks
23
    config,
7413.8.11 by Jelmer Vernooij
Don't lazy-import errors.
24
    errors,
0.165.1 by Jelmer Vernooij
Support lazily loading.
25
    lazy_import,
26
    option,
7058.5.1 by Neil Santos, Jelmer Vernooij
Add support for uploading symlinks.
27
    osutils,
0.165.1 by Jelmer Vernooij
Support lazily loading.
28
    )
29
lazy_import.lazy_import(globals(), """
30
import stat
31
6645.2.1 by Jelmer Vernooij
Bundle the upload plugin.
32
from breezy import (
6667.2.1 by Jelmer Vernooij
Some cleanup; s/BzrDir/ControlDir/, remove some unused imports.
33
    controldir,
0.165.1 by Jelmer Vernooij
Support lazily loading.
34
    globbing,
35
    ignores,
36
    revision,
37
    transport,
38
    urlutils,
39
    )
40
""")
41
6754.5.1 by Jelmer Vernooij
Fix some python3 compatibility issues that break 'make check-nodocs3' for me.
42
0.152.93 by Vincent Ladeuil
Migrate to config stacks
43
auto_option = config.Option(
44
    'upload_auto', default=False, from_unicode=config.bool_from_store,
45
    help="""\
46
Whether upload should occur when the tip of the branch changes.
47
""")
48
auto_quiet_option = config.Option(
49
    'upload_auto_quiet', default=False, from_unicode=config.bool_from_store,
50
    help="""\
51
Whether upload should occur quietly.
52
""")
53
location_option = config.Option(
54
    'upload_location', default=None,
55
    help="""\
56
The url to upload the working tree to.
57
""")
58
revid_location_option = config.Option(
59
    'upload_revid_location', default=u'.bzr-upload.revid',
60
    help="""\
61
The relative path to be used to store the uploaded revid.
62
63
The only bzr-related info uploaded with the working tree is the corresponding
64
revision id. The uploaded working tree is not linked to any other bzr data.
65
66
If the layout of your remote server is such that you can't write in the
67
root directory but only in the directories inside that root, you will need
68
to use the 'upload_revid_location' configuration variable to specify the
69
relative path to be used. That configuration variable can be specified in
70
locations.conf or branch.conf.
71
72
For example, given the following layout:
73
74
  Project/
75
    private/
76
    public/
77
78
you may have write access in 'private' and 'public' but in 'Project'
79
itself. In that case, you can add the following in your locations.conf or
80
branch.conf file:
81
82
  upload_revid_location = private/.bzr-upload.revid
83
""")
84
85
86
# FIXME: Add more tests around invalid paths or relative paths that doesn't
87
# exist on remote (if only to get proper error messages) for
88
# 'upload_revid_location'
0.165.1 by Jelmer Vernooij
Support lazily loading.
89
90
91
class BzrUploader(object):
92
93
    def __init__(self, branch, to_transport, outf, tree, rev_id,
94
                 quiet=False):
95
        self.branch = branch
96
        self.to_transport = to_transport
97
        self.outf = outf
98
        self.tree = tree
99
        self.rev_id = rev_id
100
        self.quiet = quiet
101
        self._pending_deletions = []
102
        self._pending_renames = []
103
        self._uploaded_revid = None
104
        self._ignored = None
105
106
    def _up_stat(self, relpath):
107
        return self.to_transport.stat(urlutils.escape(relpath))
108
109
    def _up_rename(self, old_path, new_path):
110
        return self.to_transport.rename(urlutils.escape(old_path),
111
                                        urlutils.escape(new_path))
112
113
    def _up_delete(self, relpath):
114
        return self.to_transport.delete(urlutils.escape(relpath))
115
116
    def _up_delete_tree(self, relpath):
117
        return self.to_transport.delete_tree(urlutils.escape(relpath))
118
119
    def _up_mkdir(self, relpath, mode):
120
        return self.to_transport.mkdir(urlutils.escape(relpath), mode)
121
122
    def _up_rmdir(self, relpath):
123
        return self.to_transport.rmdir(urlutils.escape(relpath))
124
125
    def _up_put_bytes(self, relpath, bytes, mode):
126
        self.to_transport.put_bytes(urlutils.escape(relpath), bytes, mode)
127
128
    def _up_get_bytes(self, relpath):
129
        return self.to_transport.get_bytes(urlutils.escape(relpath))
130
131
    def set_uploaded_revid(self, rev_id):
132
        # XXX: Add tests for concurrent updates, etc.
0.152.93 by Vincent Ladeuil
Migrate to config stacks
133
        revid_path = self.branch.get_config_stack().get('upload_revid_location')
0.165.1 by Jelmer Vernooij
Support lazily loading.
134
        self.to_transport.put_bytes(urlutils.escape(revid_path), rev_id)
135
        self._uploaded_revid = rev_id
136
137
    def get_uploaded_revid(self):
138
        if self._uploaded_revid is None:
0.152.93 by Vincent Ladeuil
Migrate to config stacks
139
            revid_path = self.branch.get_config_stack(
140
                ).get('upload_revid_location')
0.165.1 by Jelmer Vernooij
Support lazily loading.
141
            try:
142
                self._uploaded_revid = self._up_get_bytes(revid_path)
143
            except errors.NoSuchFile:
0.152.93 by Vincent Ladeuil
Migrate to config stacks
144
                # We have not uploaded to here.
0.165.1 by Jelmer Vernooij
Support lazily loading.
145
                self._uploaded_revid = revision.NULL_REVISION
146
        return self._uploaded_revid
147
148
    def _get_ignored(self):
149
        if self._ignored is None:
150
            try:
0.152.89 by Vincent Ladeuil
Cope with bzr.dev changes
151
                ignore_file_path = '.bzrignore-upload'
6809.4.7 by Jelmer Vernooij
Swap arguments for get_symlink_target and kind/stored_kind.
152
                ignore_file = self.tree.get_file(ignore_file_path)
153
            except errors.NoSuchFile:
154
                ignored_patterns = []
155
            else:
0.165.1 by Jelmer Vernooij
Support lazily loading.
156
                ignored_patterns = ignores.parse_ignore_file(ignore_file)
157
            self._ignored = globbing.Globster(ignored_patterns)
158
        return self._ignored
159
160
    def is_ignored(self, relpath):
161
        glob = self._get_ignored()
162
        ignored = glob.match(relpath)
163
        import os
164
        if not ignored:
165
            # We still need to check that all parents are not ignored
166
            dir = os.path.dirname(relpath)
167
            while dir and not ignored:
168
                ignored = glob.match(dir)
169
                if not ignored:
170
                    dir = os.path.dirname(dir)
171
        return ignored
172
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
173
    def upload_file(self, old_relpath, new_relpath, mode=None):
0.165.1 by Jelmer Vernooij
Support lazily loading.
174
        if mode is None:
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
175
            if self.tree.is_executable(new_relpath):
6754.5.1 by Jelmer Vernooij
Fix some python3 compatibility issues that break 'make check-nodocs3' for me.
176
                mode = 0o775
0.165.1 by Jelmer Vernooij
Support lazily loading.
177
            else:
6754.5.1 by Jelmer Vernooij
Fix some python3 compatibility issues that break 'make check-nodocs3' for me.
178
                mode = 0o664
0.165.1 by Jelmer Vernooij
Support lazily loading.
179
        if not self.quiet:
6874.2.1 by Jelmer Vernooij
Make Tree.iter_files_bytes() take paths rather than file_ids.
180
            self.outf.write('Uploading %s\n' % old_relpath)
7143.15.2 by Jelmer Vernooij
Run autopep8.
181
        self._up_put_bytes(
7143.15.15 by Jelmer Vernooij
Merge trunk.
182
            old_relpath, self.tree.get_file_text(new_relpath), mode)
0.165.1 by Jelmer Vernooij
Support lazily loading.
183
7058.5.1 by Neil Santos, Jelmer Vernooij
Add support for uploading symlinks.
184
    def _force_clear(self, relpath):
185
        try:
186
            st = self._up_stat(relpath)
187
            if stat.S_ISDIR(st.st_mode):
188
                # A simple rmdir may not be enough
189
                if not self.quiet:
190
                    self.outf.write('Clearing %s/%s\n' % (
7143.15.2 by Jelmer Vernooij
Run autopep8.
191
                        self.to_transport.external_url(), relpath))
7058.5.1 by Neil Santos, Jelmer Vernooij
Add support for uploading symlinks.
192
                self._up_delete_tree(relpath)
193
            elif stat.S_ISLNK(st.st_mode):
194
                if not self.quiet:
195
                    self.outf.write('Clearing %s/%s\n' % (
196
                        self.to_transport.external_url(), relpath))
197
                self._up_delete(relpath)
198
        except errors.PathError:
199
            pass
200
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
201
    def upload_file_robustly(self, relpath, mode=None):
0.165.1 by Jelmer Vernooij
Support lazily loading.
202
        """Upload a file, clearing the way on the remote side.
203
204
        When doing a full upload, it may happen that a directory exists where
205
        we want to put our file.
206
        """
7058.5.1 by Neil Santos, Jelmer Vernooij
Add support for uploading symlinks.
207
        self._force_clear(relpath)
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
208
        self.upload_file(relpath, relpath, mode)
0.165.1 by Jelmer Vernooij
Support lazily loading.
209
7058.5.4 by Jelmer Vernooij
Add upload support for symlinks.
210
    def upload_symlink(self, relpath, target):
211
        self.to_transport.symlink(target, relpath)
7058.5.1 by Neil Santos, Jelmer Vernooij
Add support for uploading symlinks.
212
7058.5.4 by Jelmer Vernooij
Add upload support for symlinks.
213
    def upload_symlink_robustly(self, relpath, target):
7058.5.1 by Neil Santos, Jelmer Vernooij
Add support for uploading symlinks.
214
        """Handle uploading symlinks.
215
        """
216
        self._force_clear(relpath)
217
        # Target might not be there at this time; dummy file should be
218
        # overwritten at some point, possibly by another upload.
7058.5.4 by Jelmer Vernooij
Add upload support for symlinks.
219
        target = osutils.normpath(osutils.pathjoin(
7058.5.1 by Neil Santos, Jelmer Vernooij
Add support for uploading symlinks.
220
            osutils.dirname(relpath),
7058.5.4 by Jelmer Vernooij
Add upload support for symlinks.
221
            target)
7058.5.1 by Neil Santos, Jelmer Vernooij
Add support for uploading symlinks.
222
        )
7058.5.4 by Jelmer Vernooij
Add upload support for symlinks.
223
        self.upload_symlink(relpath, target)
7058.5.1 by Neil Santos, Jelmer Vernooij
Add support for uploading symlinks.
224
0.165.1 by Jelmer Vernooij
Support lazily loading.
225
    def make_remote_dir(self, relpath, mode=None):
226
        if mode is None:
6754.5.1 by Jelmer Vernooij
Fix some python3 compatibility issues that break 'make check-nodocs3' for me.
227
            mode = 0o775
0.165.1 by Jelmer Vernooij
Support lazily loading.
228
        self._up_mkdir(relpath, mode)
229
230
    def make_remote_dir_robustly(self, relpath, mode=None):
231
        """Create a remote directory, clearing the way on the remote side.
232
233
        When doing a full upload, it may happen that a file exists where we
234
        want to create our directory.
235
        """
236
        try:
237
            st = self._up_stat(relpath)
238
            if not stat.S_ISDIR(st.st_mode):
239
                if not self.quiet:
240
                    self.outf.write('Deleting %s/%s\n' % (
7143.15.2 by Jelmer Vernooij
Run autopep8.
241
                        self.to_transport.external_url(), relpath))
0.165.1 by Jelmer Vernooij
Support lazily loading.
242
                self._up_delete(relpath)
243
            else:
244
                # Ok the remote dir already exists, nothing to do
245
                return
246
        except errors.PathError:
247
            pass
248
        self.make_remote_dir(relpath, mode)
249
250
    def delete_remote_file(self, relpath):
251
        if not self.quiet:
252
            self.outf.write('Deleting %s\n' % relpath)
253
        self._up_delete(relpath)
254
255
    def delete_remote_dir(self, relpath):
256
        if not self.quiet:
257
            self.outf.write('Deleting %s\n' % relpath)
258
        self._up_rmdir(relpath)
259
        # XXX: Add a test where a subdir is ignored but we still want to
260
        # delete the dir -- vila 100106
261
262
    def delete_remote_dir_maybe(self, relpath):
263
        """Try to delete relpath, keeping failures to retry later."""
264
        try:
265
            self._up_rmdir(relpath)
266
        # any kind of PathError would be OK, though we normally expect
267
        # DirectoryNotEmpty
268
        except errors.PathError:
269
            self._pending_deletions.append(relpath)
270
271
    def finish_deletions(self):
272
        if self._pending_deletions:
273
            # Process the previously failed deletions in reverse order to
274
            # delete children before parents
275
            for relpath in reversed(self._pending_deletions):
276
                self._up_rmdir(relpath)
277
            # The following shouldn't be needed since we use it once per
278
            # upload, but better safe than sorry ;-)
279
            self._pending_deletions = []
280
281
    def rename_remote(self, old_relpath, new_relpath):
282
        """Rename a remote file or directory taking care of collisions.
283
284
        To avoid collisions during bulk renames, each renamed target is
285
        temporarily assigned a unique name. When all renames have been done,
286
        each target get its proper name.
287
        """
288
        # We generate a sufficiently random name to *assume* that
289
        # no collisions will occur and don't worry about it (nor
290
        # handle it).
291
        import os
292
        import random
293
        import time
294
295
        stamp = '.tmp.%.9f.%d.%d' % (time.time(),
296
                                     os.getpid(),
6809.1.1 by Martin
Apply 2to3 ws_comma fixer
297
                                     random.randint(0, 0x7FFFFFFF))
0.165.1 by Jelmer Vernooij
Support lazily loading.
298
        if not self.quiet:
299
            self.outf.write('Renaming %s to %s\n' % (old_relpath, new_relpath))
300
        self._up_rename(old_relpath, stamp)
301
        self._pending_renames.append((stamp, new_relpath))
302
303
    def finish_renames(self):
304
        for (stamp, new_path) in self._pending_renames:
305
            self._up_rename(stamp, new_path)
306
        # The following shouldn't be needed since we use it once per upload,
307
        # but better safe than sorry ;-)
308
        self._pending_renames = []
309
310
    def upload_full_tree(self):
7143.15.2 by Jelmer Vernooij
Run autopep8.
311
        self.to_transport.ensure_base()  # XXX: Handle errors (add
312
        # --create-prefix option ?)
6754.8.4 by Jelmer Vernooij
Use new context stuff.
313
        with self.tree.lock_read():
0.169.2 by Vincent Ladeuil
Fix deprecation warning about tree.inventory usage
314
            for relpath, ie in self.tree.iter_entries_by_dir():
0.165.1 by Jelmer Vernooij
Support lazily loading.
315
                if relpath in ('', '.bzrignore', '.bzrignore-upload'):
316
                    # skip root ('')
317
                    # .bzrignore and .bzrignore-upload have no meaning outside
318
                    # a working tree so do not upload them
319
                    continue
320
                if self.is_ignored(relpath):
321
                    if not self.quiet:
322
                        self.outf.write('Ignoring %s\n' % relpath)
323
                    continue
324
                if ie.kind == 'file':
7141.7.1 by Jelmer Vernooij
Get rid of file_ids in most of Tree.
325
                    self.upload_file_robustly(relpath)
7058.5.1 by Neil Santos, Jelmer Vernooij
Add support for uploading symlinks.
326
                elif ie.kind == 'symlink':
327
                    try:
7143.15.2 by Jelmer Vernooij
Run autopep8.
328
                        self.upload_symlink_robustly(
329
                            relpath, ie.symlink_target)
7058.5.1 by Neil Santos, Jelmer Vernooij
Add support for uploading symlinks.
330
                    except errors.TransportNotPossible:
331
                        if not self.quiet:
332
                            target = self.tree.path_content_summary(relpath)[3]
333
                            self.outf.write('Not uploading symlink %s -> %s\n'
334
                                            % (relpath, target))
0.165.1 by Jelmer Vernooij
Support lazily loading.
335
                elif ie.kind == 'directory':
336
                    self.make_remote_dir_robustly(relpath)
337
                else:
338
                    raise NotImplementedError
339
            self.set_uploaded_revid(self.rev_id)
340
341
    def upload_tree(self):
342
        # If we can't find the revid file on the remote location, upload the
343
        # full tree instead
344
        rev_id = self.get_uploaded_revid()
345
346
        if rev_id == revision.NULL_REVISION:
347
            if not self.quiet:
348
                self.outf.write('No uploaded revision id found,'
349
                                ' switching to full upload\n')
350
            self.upload_full_tree()
351
            # We're done
352
            return
353
354
        # Check if the revision hasn't already been uploaded
355
        if rev_id == self.rev_id:
356
            if not self.quiet:
357
                self.outf.write('Remote location already up to date\n')
358
359
        from_tree = self.branch.repository.revision_tree(rev_id)
7143.15.2 by Jelmer Vernooij
Run autopep8.
360
        self.to_transport.ensure_base()  # XXX: Handle errors (add
361
        # --create-prefix option ?)
0.165.1 by Jelmer Vernooij
Support lazily loading.
362
        changes = self.tree.changes_from(from_tree)
6754.8.4 by Jelmer Vernooij
Use new context stuff.
363
        with self.tree.lock_read():
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
364
            for change in changes.removed:
365
                if self.is_ignored(change.path[0]):
0.165.1 by Jelmer Vernooij
Support lazily loading.
366
                    if not self.quiet:
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
367
                        self.outf.write('Ignoring %s\n' % change.path[0])
0.165.1 by Jelmer Vernooij
Support lazily loading.
368
                    continue
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
369
                if change.kind[0] == 'file':
370
                    self.delete_remote_file(change.path[0])
371
                elif change.kind[0] == 'directory':
372
                    self.delete_remote_dir_maybe(change.path[0])
373
                elif change.kind[0] == 'symlink':
374
                    self.delete_remote_file(change.path[0])
0.165.1 by Jelmer Vernooij
Support lazily loading.
375
                else:
376
                    raise NotImplementedError
377
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
378
            for change in changes.renamed:
379
                if self.is_ignored(change.path[0]) and self.is_ignored(change.path[1]):
0.165.1 by Jelmer Vernooij
Support lazily loading.
380
                    if not self.quiet:
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
381
                        self.outf.write('Ignoring %s\n' % change.path[0])
382
                        self.outf.write('Ignoring %s\n' % change.path[1])
0.165.1 by Jelmer Vernooij
Support lazily loading.
383
                    continue
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
384
                if change.changed_content:
385
                    # We update the change.path[0] content because renames and
0.165.1 by Jelmer Vernooij
Support lazily loading.
386
                    # deletions are differed.
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
387
                    self.upload_file(change.path[0], change.path[1])
388
                self.rename_remote(change.path[0], change.path[1])
0.165.1 by Jelmer Vernooij
Support lazily loading.
389
            self.finish_renames()
390
            self.finish_deletions()
391
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
392
            for change in changes.kind_changed:
393
                if self.is_ignored(change.path[1]):
394
                    if not self.quiet:
395
                        self.outf.write('Ignoring %s\n' % change.path[1])
396
                    continue
397
                if change.kind[0] in ('file', 'symlink'):
398
                    self.delete_remote_file(change.path[0])
399
                elif change.kind[0] == 'directory':
400
                    self.delete_remote_dir(change.path[0])
401
                else:
402
                    raise NotImplementedError
403
404
                if change.kind[1] == 'file':
405
                    self.upload_file(change.path[1], change.path[1])
406
                elif change.kind[1] == 'symlink':
407
                    target = self.tree.get_symlink_target(change.path[1])
408
                    self.upload_symlink(change.path[1], target)
409
                elif change.kind[1] == 'directory':
410
                    self.make_remote_dir(change.path[1])
411
                else:
412
                    raise NotImplementedError
413
7358.17.5 by Jelmer Vernooij
Fix more tests.
414
            for change in changes.added + changes.copied:
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
415
                if self.is_ignored(change.path[1]):
416
                    if not self.quiet:
417
                        self.outf.write('Ignoring %s\n' % change.path[1])
418
                    continue
419
                if change.kind[1] == 'file':
420
                    self.upload_file(change.path[1], change.path[1])
421
                elif change.kind[1] == 'directory':
422
                    self.make_remote_dir(change.path[1])
423
                elif change.kind[1] == 'symlink':
424
                    target = self.tree.get_symlink_target(change.path[1])
7058.5.4 by Jelmer Vernooij
Add upload support for symlinks.
425
                    try:
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
426
                        self.upload_symlink(change.path[1], target)
7058.5.4 by Jelmer Vernooij
Add upload support for symlinks.
427
                    except errors.TransportNotPossible:
428
                        if not self.quiet:
429
                            self.outf.write('Not uploading symlink %s -> %s\n'
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
430
                                            % (change.path[1], target))
0.165.1 by Jelmer Vernooij
Support lazily loading.
431
                else:
432
                    raise NotImplementedError
433
434
            # XXX: Add a test for exec_change
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
435
            for change in changes.modified:
436
                if self.is_ignored(change.path[1]):
0.165.1 by Jelmer Vernooij
Support lazily loading.
437
                    if not self.quiet:
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
438
                        self.outf.write('Ignoring %s\n' % change.path[1])
0.165.1 by Jelmer Vernooij
Support lazily loading.
439
                    continue
7358.11.3 by Jelmer Vernooij
TreeDelta holds TreeChange objects rather than tuples of various sizes.
440
                if change.kind[1] == 'file':
441
                    self.upload_file(change.path[1], change.path[1])
442
                elif change.kind[1] == 'symlink':
443
                    target = self.tree.get_symlink_target(change.path[1])
444
                    self.upload_symlink(change.path[1], target)
0.165.1 by Jelmer Vernooij
Support lazily loading.
445
                else:
446
                    raise NotImplementedError
447
448
            self.set_uploaded_revid(self.rev_id)
449
450
451
class CannotUploadToWorkingTree(errors.BzrCommandError):
452
453
    _fmt = 'Cannot upload to a bzr managed working tree: %(url)s".'
454
455
456
class DivergedUploadedTree(errors.BzrCommandError):
457
458
    _fmt = ("Your branch (%(revid)s)"
459
            " and the uploaded tree (%(uploaded_revid)s) have diverged: ")
460
461
462
class cmd_upload(commands.Command):
463
    """Upload a working tree, as a whole or incrementally.
464
465
    If no destination is specified use the last one used.
466
    If no revision is specified upload the changes since the last upload.
467
468
    Changes include files added, renamed, modified or removed.
469
    """
470
    _see_also = ['plugins/upload']
471
    takes_args = ['location?']
472
    takes_options = [
473
        'revision',
474
        'remember',
475
        'overwrite',
476
        option.Option('full', 'Upload the full working tree.'),
477
        option.Option('quiet', 'Do not output what is being done.',
7143.15.2 by Jelmer Vernooij
Run autopep8.
478
                      short_name='q'),
0.165.1 by Jelmer Vernooij
Support lazily loading.
479
        option.Option('directory',
480
                      help='Branch to upload from, '
481
                      'rather than the one containing the working directory.',
482
                      short_name='d',
7479.2.1 by Jelmer Vernooij
Drop python2 support.
483
                      type=str,
0.165.1 by Jelmer Vernooij
Support lazily loading.
484
                      ),
485
        option.Option('auto',
486
                      'Trigger an upload from this branch whenever the tip '
487
                      'revision changes.')
7143.15.2 by Jelmer Vernooij
Run autopep8.
488
        ]
0.165.1 by Jelmer Vernooij
Support lazily loading.
489
490
    def run(self, location=None, full=False, revision=None, remember=None,
491
            directory=None, quiet=False, auto=None, overwrite=False
492
            ):
493
        if directory is None:
494
            directory = u'.'
495
496
        (wt, branch,
6667.2.1 by Jelmer Vernooij
Some cleanup; s/BzrDir/ControlDir/, remove some unused imports.
497
         relpath) = controldir.ControlDir.open_containing_tree_or_branch(
498
             directory)
0.165.1 by Jelmer Vernooij
Support lazily loading.
499
500
        if wt:
501
            locked = wt
502
        else:
503
            locked = branch
7356.1.5 by Jelmer Vernooij
Use more ExitStacks.
504
        with locked.lock_read():
0.165.1 by Jelmer Vernooij
Support lazily loading.
505
            if wt:
506
                changes = wt.changes_from(wt.basis_tree())
507
7143.15.2 by Jelmer Vernooij
Run autopep8.
508
                if revision is None and changes.has_changed():
0.165.1 by Jelmer Vernooij
Support lazily loading.
509
                    raise errors.UncommittedChanges(wt)
510
0.152.93 by Vincent Ladeuil
Migrate to config stacks
511
            conf = branch.get_config_stack()
0.165.1 by Jelmer Vernooij
Support lazily loading.
512
            if location is None:
0.152.93 by Vincent Ladeuil
Migrate to config stacks
513
                stored_loc = conf.get('upload_location')
0.165.1 by Jelmer Vernooij
Support lazily loading.
514
                if stored_loc is None:
515
                    raise errors.BzrCommandError(
516
                        'No upload location known or specified.')
517
                else:
518
                    # FIXME: Not currently tested
519
                    display_url = urlutils.unescape_for_display(stored_loc,
7143.15.2 by Jelmer Vernooij
Run autopep8.
520
                                                                self.outf.encoding)
0.165.1 by Jelmer Vernooij
Support lazily loading.
521
                    self.outf.write("Using saved location: %s\n" % display_url)
522
                    location = stored_loc
523
524
            to_transport = transport.get_transport(location)
525
526
            # Check that we are not uploading to a existing working tree.
527
            try:
6667.2.1 by Jelmer Vernooij
Some cleanup; s/BzrDir/ControlDir/, remove some unused imports.
528
                to_bzr_dir = controldir.ControlDir.open_from_transport(
7143.15.2 by Jelmer Vernooij
Run autopep8.
529
                    to_transport)
0.165.1 by Jelmer Vernooij
Support lazily loading.
530
                has_wt = to_bzr_dir.has_workingtree()
531
            except errors.NotBranchError:
532
                has_wt = False
533
            except errors.NotLocalUrl:
534
                # The exception raised is a bit weird... but that's life.
535
                has_wt = True
536
537
            if has_wt:
538
                raise CannotUploadToWorkingTree(url=location)
539
            if revision is None:
540
                rev_id = branch.last_revision()
541
            else:
542
                if len(revision) != 1:
543
                    raise errors.BzrCommandError(
544
                        'bzr upload --revision takes exactly 1 argument')
545
                rev_id = revision[0].in_history(branch).rev_id
546
547
            tree = branch.repository.revision_tree(rev_id)
548
549
            uploader = BzrUploader(branch, to_transport, self.outf, tree,
550
                                   rev_id, quiet=quiet)
551
552
            if not overwrite:
553
                prev_uploaded_rev_id = uploader.get_uploaded_revid()
554
                graph = branch.repository.get_graph()
555
                if not graph.is_ancestor(prev_uploaded_rev_id, rev_id):
556
                    raise DivergedUploadedTree(
557
                        revid=rev_id, uploaded_revid=prev_uploaded_rev_id)
558
            if full:
559
                uploader.upload_full_tree()
560
            else:
561
                uploader.upload_tree()
562
563
        # We uploaded successfully, remember it
7058.5.4 by Jelmer Vernooij
Add upload support for symlinks.
564
        with branch.lock_write():
0.152.93 by Vincent Ladeuil
Migrate to config stacks
565
            upload_location = conf.get('upload_location')
566
            if upload_location is None or remember:
567
                conf.set('upload_location',
568
                         urlutils.unescape(to_transport.base))
569
            if auto is not None:
570
                conf.set('upload_auto', auto)