/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
19
The only bzr-related info uploaded with the working tree is the corrseponding
20
revision id. The uploaded working tree is not linked to any other bzr data.
21
22
The intended use is for web sites.
23
"""
0.152.1 by Vincent Ladeuil
Empty shell
24
0.152.3 by v.ladeuil+lp at free
Make the tests fail not error out.
25
from bzrlib import (
26
    commands,
27
    option,
28
    )
0.152.1 by Vincent Ladeuil
Empty shell
29
0.152.16 by Vincent Ladeuil
Handle renames. Robust implementation.
30
0.152.1 by Vincent Ladeuil
Empty shell
31
class cmd_upload(commands.Command):
0.152.2 by v.ladeuil+lp at free
Add failing tests for basic usage.
32
    """Upload a working tree, as a whole or incrementally.
33
34
    If no destination is specified use the last one used.
35
    If no revision is specified upload the changes since the last upload.
36
    """
0.152.7 by Vincent Ladeuil
Slight refactoring.
37
    takes_args = ['location?']
0.152.2 by v.ladeuil+lp at free
Add failing tests for basic usage.
38
    takes_options = [
39
        'revision',
0.152.12 by Vincent Ladeuil
Implement 'upload_location' in config files.
40
        'remember',
0.152.3 by v.ladeuil+lp at free
Make the tests fail not error out.
41
        option.Option('full', 'Upload the full working tree.'),
0.152.11 by Vincent Ladeuil
Be coherent with bzr push.
42
        option.Option('directory',
43
                      help='Branch to upload from, '
44
                      'rather than the one containing the working directory.',
45
                      short_name='d',
46
                      type=unicode,
47
                      ),
0.152.4 by v.ladeuil+lp at free
Implement a trivial implementation to make one test pass.
48
       ]
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
49
0.152.12 by Vincent Ladeuil
Implement 'upload_location' in config files.
50
    def run(self, location, full=False, revision=None, remember=None,
0.152.11 by Vincent Ladeuil
Be coherent with bzr push.
51
            directory=None,
0.152.8 by Vincent Ladeuil
Handle uploading directories.
52
            ):
0.152.14 by Vincent Ladeuil
Handle renames (trivial implementation).
53
        # Import the needed modules but only once we are required to run to
54
        # avoid degrading bzr startup time
55
        from bzrlib import (
56
            branch,
57
            errors,
58
            revisionspec,
59
            transport,
60
            )
61
0.152.11 by Vincent Ladeuil
Be coherent with bzr push.
62
        if directory is None:
63
            directory = u'.'
0.152.12 by Vincent Ladeuil
Implement 'upload_location' in config files.
64
        self.branch = branch.Branch.open_containing(directory)[0]
0.152.4 by v.ladeuil+lp at free
Implement a trivial implementation to make one test pass.
65
0.152.7 by Vincent Ladeuil
Slight refactoring.
66
        if location is None:
0.152.12 by Vincent Ladeuil
Implement 'upload_location' in config files.
67
            stored_loc = self.get_upload_location()
68
            if stored_loc is None:
69
                raise errors.BzrCommandError('No upload location'
70
                                             ' known or specified.')
71
            else:
72
                display_url = urlutils.unescape_for_display(stored_loc,
73
                        self.outf.encoding)
74
                self.outf.write("Using saved location: %s\n" % display_url)
75
                location = stored_loc
76
77
        self.to_transport = transport.get_transport(location)
0.152.4 by v.ladeuil+lp at free
Implement a trivial implementation to make one test pass.
78
        if revision is None:
0.152.12 by Vincent Ladeuil
Implement 'upload_location' in config files.
79
            rev_id = self.branch.last_revision()
0.152.4 by v.ladeuil+lp at free
Implement a trivial implementation to make one test pass.
80
        else:
81
            if len(revision) != 1:
82
                raise errors.BzrCommandError(
83
                    'bzr upload --revision takes exactly 1 argument')
0.152.10 by Vincent Ladeuil
Fix incremental upload cheat.
84
            rev_id = revision[0].in_history(self.branch).rev_id
0.152.4 by v.ladeuil+lp at free
Implement a trivial implementation to make one test pass.
85
0.152.12 by Vincent Ladeuil
Implement 'upload_location' in config files.
86
        self.tree = self.branch.repository.revision_tree(rev_id)
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
87
        self.rev_id = rev_id
0.152.16 by Vincent Ladeuil
Handle renames. Robust implementation.
88
        self._pending_renames = []
0.152.4 by v.ladeuil+lp at free
Implement a trivial implementation to make one test pass.
89
        if full:
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
90
            self.upload_full_tree()
0.152.4 by v.ladeuil+lp at free
Implement a trivial implementation to make one test pass.
91
        else:
0.152.10 by Vincent Ladeuil
Fix incremental upload cheat.
92
            self.upload_tree()
93
0.152.12 by Vincent Ladeuil
Implement 'upload_location' in config files.
94
        # We uploaded successfully, remember it
95
        if self.get_upload_location() is None or remember:
96
            self.set_upload_location(self.to_transport.base)
97
98
    def set_upload_location(self, location):
99
        self.branch.get_config().set_user_option('upload_location', location)
100
101
    def get_upload_location(self):
102
        return self.branch.get_config().get_user_option('upload_location')
103
0.152.10 by Vincent Ladeuil
Fix incremental upload cheat.
104
    bzr_upload_revid_file_name = '.bzr-upload.revid'
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
105
106
    def set_uploaded_revid(self, rev_id):
0.152.10 by Vincent Ladeuil
Fix incremental upload cheat.
107
        # XXX: Add tests for concurrent updates, etc.
108
        self.to_transport.put_bytes(self.bzr_upload_revid_file_name, rev_id)
109
110
    def get_uploaded_revid(self):
111
        return self.to_transport.get_bytes(self.bzr_upload_revid_file_name)
0.152.7 by Vincent Ladeuil
Slight refactoring.
112
113
    def upload_file(self, relpath, id):
114
        self.to_transport.put_bytes(relpath, self.tree.get_file_text(id))
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
115
0.152.8 by Vincent Ladeuil
Handle uploading directories.
116
    def make_remote_dir(self, relpath):
117
        # XXX: handle mode
118
        self.to_transport.mkdir(relpath)
119
0.152.16 by Vincent Ladeuil
Handle renames. Robust implementation.
120
    def rename_remote(self, old_relpath, new_relpath):
121
        """Rename a remote file or directory taking care of collisions.
122
123
        To avoid collisions during bulk renames, each renamed target is
124
        temporarily assigned a unique name. When all renames have been done,
125
        each target get its proper name.
126
        """
127
        # We generate a sufficiently random name to *assume* that
128
        # no collisions will occur and don't worry about it (nor
129
        # handle it).
130
        import os
131
        import random
132
        import time
133
134
        stamp = '.tmp.%.9f.%d.%d' % (time.time(),
135
                                     os.getpid(),
136
                                     random.randint(0,0x7FFFFFFF))
137
        self.to_transport.rename(old_relpath, stamp)
138
        self._pending_renames.append((stamp, new_relpath))
139
140
    def finish_renames(self):
141
        for (stamp, new_path) in self._pending_renames:
142
            self.to_transport.rename(stamp, new_path)
143
        # The following shouldn't be needed since we use it once per upload,
144
        # but better safe than sorry ;-)
145
        self._pending_renames = []
146
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
147
    def upload_full_tree(self):
0.152.14 by Vincent Ladeuil
Handle renames (trivial implementation).
148
        self.to_transport.ensure_base() # XXX: Handle errors (add
149
                                        # --create-prefix option ?)
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
150
        self.tree.lock_read()
151
        try:
0.152.10 by Vincent Ladeuil
Fix incremental upload cheat.
152
            for dp, ie in self.tree.inventory.iter_entries():
153
                if dp in ('', '.bzrignore'):
154
                    # skip root ('')
155
                    # .bzrignore has no meaning outside of a working tree
156
                    # so do not upload it
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
157
                    continue
158
0.152.8 by Vincent Ladeuil
Handle uploading directories.
159
                if ie.kind == 'file':
160
                    self.upload_file(dp, ie.file_id)
161
                elif ie.kind == 'directory':
162
                    try:
163
                        self.make_remote_dir(dp)
164
                    except errors.FileExists:
165
                        # The directory existed before the upload
166
                        pass
167
                else:
168
                    raise NotImplementedError
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
169
            self.set_uploaded_revid(self.rev_id)
170
        finally:
171
            self.tree.unlock()
172
0.152.10 by Vincent Ladeuil
Fix incremental upload cheat.
173
    def upload_tree(self):
0.152.12 by Vincent Ladeuil
Implement 'upload_location' in config files.
174
        # XXX: errors out if NoSuchFile and recommand --full on first upload ?
175
        # Add tests.
0.152.10 by Vincent Ladeuil
Fix incremental upload cheat.
176
        rev_id = self.get_uploaded_revid()
177
        # XXX: errors out if rev_id not in branch history (probably someone
178
        # uploaded from a different branch).
179
        from_tree = self.branch.repository.revision_tree(rev_id)
0.152.14 by Vincent Ladeuil
Handle renames (trivial implementation).
180
        self.to_transport.ensure_base() # XXX: Handle errors (add
181
                                        # --create-prefix option ?)
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
182
        changes = self.tree.changes_from(from_tree)
183
        self.tree.lock_read()
184
        try:
0.152.14 by Vincent Ladeuil
Handle renames (trivial implementation).
185
            # XXX: handle removed, kind_changed
186
            for (old_path, new_path, id, kind,
187
                 content_change, exec_change) in changes.renamed:
0.152.16 by Vincent Ladeuil
Handle renames. Robust implementation.
188
                self.rename_remote(old_path, new_path)
189
            self.finish_renames()
190
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
191
            for (path, id, kind) in changes.added:
192
                if kind is 'file':
0.152.7 by Vincent Ladeuil
Slight refactoring.
193
                    self.upload_file(path, id)
0.152.8 by Vincent Ladeuil
Handle uploading directories.
194
                elif kind is 'directory':
195
                    self.make_remote_dir(path)
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
196
                else:
197
                    raise NotImplementedError
198
            # XXX: Add a test for exec_change
199
            for (path, id, kind,
200
                 content_change, exec_change) in changes.modified:
201
                if kind is 'file':
0.152.7 by Vincent Ladeuil
Slight refactoring.
202
                    self.upload_file(path, id)
0.152.5 by v.ladeuil+lp at free
Partial incremental upload implementationm tests pass.
203
                else:
204
                    raise NotImplementedError
205
            self.set_uploaded_revid(self.rev_id)
206
        finally:
207
            self.tree.unlock()
0.152.4 by v.ladeuil+lp at free
Implement a trivial implementation to make one test pass.
208
0.152.1 by Vincent Ladeuil
Empty shell
209
210
commands.register_command(cmd_upload)
211
0.152.4 by v.ladeuil+lp at free
Implement a trivial implementation to make one test pass.
212
0.152.1 by Vincent Ladeuil
Empty shell
213
def test_suite():
214
    from bzrlib.tests import TestUtil
215
216
    suite = TestUtil.TestSuite()
217
    loader = TestUtil.TestLoader()
218
    testmod_names = [
219
        'test_upload',
220
        ]
221
222
    suite.addTest(loader.loadTestsFromModuleNames(
223
            ["%s.%s" % (__name__, tmn) for tmn in testmod_names]))
224
    return suite