~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/branch.py

(jelmer) Support upgrading between the 2a and development-colo formats.
 (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""Server-side branch related request implmentations."""
18
18
 
19
19
 
20
 
from bzrlib import errors
 
20
from bzrlib import (
 
21
    bencode,
 
22
    errors,
 
23
    )
21
24
from bzrlib.bzrdir import BzrDir
22
25
from bzrlib.smart.request import (
23
26
    FailedSmartServerResponse,
27
30
 
28
31
 
29
32
class SmartServerBranchRequest(SmartServerRequest):
30
 
    """Base class for handling common branch request logic."""
 
33
    """Base class for handling common branch request logic.
 
34
    """
31
35
 
32
36
    def do(self, path, *args):
33
37
        """Execute a request for a branch at path.
34
38
 
 
39
        All Branch requests take a path to the branch as their first argument.
 
40
 
35
41
        If the branch is a branch reference, NotBranchError is raised.
 
42
 
 
43
        :param path: The path for the repository as received from the
 
44
            client.
 
45
        :return: A SmartServerResponse from self.do_with_branch().
36
46
        """
37
 
        transport = self._backing_transport.clone(path)
 
47
        transport = self.transport_from_client_path(path)
38
48
        bzrdir = BzrDir.open_from_transport(transport)
39
49
        if bzrdir.get_branch_reference() is not None:
40
50
            raise errors.NotBranchError(transport.base)
41
 
        branch = bzrdir.open_branch()
 
51
        branch = bzrdir.open_branch(ignore_fallbacks=True)
42
52
        return self.do_with_branch(branch, *args)
43
53
 
44
54
 
67
77
 
68
78
 
69
79
class SmartServerBranchGetConfigFile(SmartServerBranchRequest):
70
 
    
 
80
 
71
81
    def do_with_branch(self, branch):
72
 
        """Return the content of branch.control_files.get('branch.conf').
73
 
        
 
82
        """Return the content of branch.conf
 
83
 
74
84
        The body is not utf8 decoded - its the literal bytestream from disk.
75
85
        """
76
86
        try:
77
 
            content = branch.control_files.get('branch.conf').read()
 
87
            content = branch._transport.get_bytes('branch.conf')
78
88
        except errors.NoSuchFile:
79
89
            content = ''
80
90
        return SuccessfulSmartServerResponse( ('ok', ), content)
81
91
 
82
92
 
 
93
class SmartServerBranchGetParent(SmartServerBranchRequest):
 
94
 
 
95
    def do_with_branch(self, branch):
 
96
        """Return the parent of branch."""
 
97
        parent = branch._get_parent_location() or ''
 
98
        return SuccessfulSmartServerResponse((parent,))
 
99
 
 
100
 
 
101
class SmartServerBranchGetTagsBytes(SmartServerBranchRequest):
 
102
 
 
103
    def do_with_branch(self, branch):
 
104
        """Return the _get_tags_bytes for a branch."""
 
105
        bytes = branch._get_tags_bytes()
 
106
        return SuccessfulSmartServerResponse((bytes,))
 
107
 
 
108
 
 
109
class SmartServerBranchSetTagsBytes(SmartServerLockedBranchRequest):
 
110
 
 
111
    def __init__(self, backing_transport, root_client_path='/', jail_root=None):
 
112
        SmartServerLockedBranchRequest.__init__(
 
113
            self, backing_transport, root_client_path, jail_root)
 
114
        self.locked = False
 
115
        
 
116
    def do_with_locked_branch(self, branch):
 
117
        """Call _set_tags_bytes for a branch.
 
118
 
 
119
        New in 1.18.
 
120
        """
 
121
        # We need to keep this branch locked until we get a body with the tags
 
122
        # bytes.
 
123
        self.branch = branch
 
124
        self.branch.lock_write()
 
125
        self.locked = True
 
126
 
 
127
    def do_body(self, bytes):
 
128
        self.branch._set_tags_bytes(bytes)
 
129
        return SuccessfulSmartServerResponse(())
 
130
 
 
131
    def do_end(self):
 
132
        # TODO: this request shouldn't have to do this housekeeping manually.
 
133
        # Some of this logic probably belongs in a base class.
 
134
        if not self.locked:
 
135
            # We never acquired the branch successfully in the first place, so
 
136
            # there's nothing more to do.
 
137
            return
 
138
        try:
 
139
            return SmartServerLockedBranchRequest.do_end(self)
 
140
        finally:
 
141
            # Only try unlocking if we locked successfully in the first place
 
142
            self.branch.unlock()
 
143
 
 
144
 
 
145
class SmartServerBranchHeadsToFetch(SmartServerBranchRequest):
 
146
 
 
147
    def do_with_branch(self, branch):
 
148
        """Return the heads-to-fetch for a Branch as two bencoded lists.
 
149
        
 
150
        See Branch.heads_to_fetch.
 
151
 
 
152
        New in 2.4.
 
153
        """
 
154
        must_fetch, if_present_fetch = branch.heads_to_fetch()
 
155
        return SuccessfulSmartServerResponse(
 
156
            (list(must_fetch), list(if_present_fetch)))
 
157
 
 
158
 
 
159
class SmartServerBranchRequestGetStackedOnURL(SmartServerBranchRequest):
 
160
 
 
161
    def do_with_branch(self, branch):
 
162
        stacked_on_url = branch.get_stacked_on_url()
 
163
        return SuccessfulSmartServerResponse(('ok', stacked_on_url))
 
164
 
 
165
 
83
166
class SmartServerRequestRevisionHistory(SmartServerBranchRequest):
84
167
 
85
168
    def do_with_branch(self, branch):
93
176
 
94
177
 
95
178
class SmartServerBranchRequestLastRevisionInfo(SmartServerBranchRequest):
96
 
    
 
179
 
97
180
    def do_with_branch(self, branch):
98
181
        """Return branch.last_revision_info().
99
 
        
 
182
 
100
183
        The revno is encoded in decimal, the revision_id is encoded as utf8.
101
184
        """
102
185
        revno, last_revision = branch.last_revision_info()
103
186
        return SuccessfulSmartServerResponse(('ok', str(revno), last_revision))
104
187
 
105
188
 
106
 
class SmartServerBranchRequestSetLastRevision(SmartServerLockedBranchRequest):
 
189
class SmartServerSetTipRequest(SmartServerLockedBranchRequest):
 
190
    """Base class for handling common branch request logic for requests that
 
191
    update the branch tip.
 
192
    """
 
193
 
 
194
    def do_with_locked_branch(self, branch, *args):
 
195
        try:
 
196
            return self.do_tip_change_with_locked_branch(branch, *args)
 
197
        except errors.TipChangeRejected, e:
 
198
            msg = e.msg
 
199
            if isinstance(msg, unicode):
 
200
                msg = msg.encode('utf-8')
 
201
            return FailedSmartServerResponse(('TipChangeRejected', msg))
 
202
 
 
203
 
 
204
class SmartServerBranchRequestSetConfigOption(SmartServerLockedBranchRequest):
 
205
    """Set an option in the branch configuration."""
 
206
 
 
207
    def do_with_locked_branch(self, branch, value, name, section):
 
208
        if not section:
 
209
            section = None
 
210
        branch._get_config().set_option(value.decode('utf8'), name, section)
 
211
        return SuccessfulSmartServerResponse(())
 
212
 
 
213
 
 
214
class SmartServerBranchRequestSetConfigOptionDict(SmartServerLockedBranchRequest):
 
215
    """Set an option in the branch configuration.
107
216
    
108
 
    def do_with_locked_branch(self, branch, new_last_revision_id):
 
217
    New in 2.2.
 
218
    """
 
219
 
 
220
    def do_with_locked_branch(self, branch, value_dict, name, section):
 
221
        utf8_dict = bencode.bdecode(value_dict)
 
222
        value_dict = {}
 
223
        for key, value in utf8_dict.items():
 
224
            value_dict[key.decode('utf8')] = value.decode('utf8')
 
225
        if not section:
 
226
            section = None
 
227
        branch._get_config().set_option(value_dict, name, section)
 
228
        return SuccessfulSmartServerResponse(())
 
229
 
 
230
 
 
231
class SmartServerBranchRequestSetLastRevision(SmartServerSetTipRequest):
 
232
 
 
233
    def do_tip_change_with_locked_branch(self, branch, new_last_revision_id):
109
234
        if new_last_revision_id == 'null:':
110
 
            branch.set_revision_history([])
 
235
            branch._set_revision_history([])
111
236
        else:
112
237
            if not branch.repository.has_revision(new_last_revision_id):
113
238
                return FailedSmartServerResponse(
114
239
                    ('NoSuchRevision', new_last_revision_id))
115
 
            branch.generate_revision_history(new_last_revision_id)
116
 
        return SuccessfulSmartServerResponse(('ok',))
 
240
            branch._set_revision_history(branch._lefthand_history(
 
241
                new_last_revision_id, None, None))
 
242
        return SuccessfulSmartServerResponse(('ok',))
 
243
 
 
244
 
 
245
class SmartServerBranchRequestSetLastRevisionEx(SmartServerSetTipRequest):
 
246
 
 
247
    def do_tip_change_with_locked_branch(self, branch, new_last_revision_id,
 
248
            allow_divergence, allow_overwrite_descendant):
 
249
        """Set the last revision of the branch.
 
250
 
 
251
        New in 1.6.
 
252
 
 
253
        :param new_last_revision_id: the revision ID to set as the last
 
254
            revision of the branch.
 
255
        :param allow_divergence: A flag.  If non-zero, change the revision ID
 
256
            even if the new_last_revision_id's ancestry has diverged from the
 
257
            current last revision.  If zero, a 'Diverged' error will be
 
258
            returned if new_last_revision_id is not a descendant of the current
 
259
            last revision.
 
260
        :param allow_overwrite_descendant:  A flag.  If zero and
 
261
            new_last_revision_id is not a descendant of the current last
 
262
            revision, then the last revision will not be changed.  If non-zero
 
263
            and there is no divergence, then the last revision is always
 
264
            changed.
 
265
 
 
266
        :returns: on success, a tuple of ('ok', revno, revision_id), where
 
267
            revno and revision_id are the new values of the current last
 
268
            revision info.  The revision_id might be different to the
 
269
            new_last_revision_id if allow_overwrite_descendant was not set.
 
270
        """
 
271
        do_not_overwrite_descendant = not allow_overwrite_descendant
 
272
        try:
 
273
            last_revno, last_rev = branch.last_revision_info()
 
274
            graph = branch.repository.get_graph()
 
275
            if not allow_divergence or do_not_overwrite_descendant:
 
276
                relation = branch._revision_relations(
 
277
                    last_rev, new_last_revision_id, graph)
 
278
                if relation == 'diverged' and not allow_divergence:
 
279
                    return FailedSmartServerResponse(('Diverged',))
 
280
                if relation == 'a_descends_from_b' and do_not_overwrite_descendant:
 
281
                    return SuccessfulSmartServerResponse(
 
282
                        ('ok', last_revno, last_rev))
 
283
            new_revno = graph.find_distance_to_null(
 
284
                new_last_revision_id, [(last_rev, last_revno)])
 
285
            branch.set_last_revision_info(new_revno, new_last_revision_id)
 
286
        except errors.GhostRevisionsHaveNoRevno:
 
287
            return FailedSmartServerResponse(
 
288
                ('NoSuchRevision', new_last_revision_id))
 
289
        return SuccessfulSmartServerResponse(
 
290
            ('ok', new_revno, new_last_revision_id))
 
291
 
 
292
 
 
293
class SmartServerBranchRequestSetLastRevisionInfo(SmartServerSetTipRequest):
 
294
    """Branch.set_last_revision_info.  Sets the revno and the revision ID of
 
295
    the specified branch.
 
296
 
 
297
    New in bzrlib 1.4.
 
298
    """
 
299
 
 
300
    def do_tip_change_with_locked_branch(self, branch, new_revno,
 
301
            new_last_revision_id):
 
302
        try:
 
303
            branch.set_last_revision_info(int(new_revno), new_last_revision_id)
 
304
        except errors.NoSuchRevision:
 
305
            return FailedSmartServerResponse(
 
306
                ('NoSuchRevision', new_last_revision_id))
 
307
        return SuccessfulSmartServerResponse(('ok',))
 
308
 
 
309
 
 
310
class SmartServerBranchRequestSetParentLocation(SmartServerLockedBranchRequest):
 
311
    """Set the parent location for a branch.
 
312
    
 
313
    Takes a location to set, which must be utf8 encoded.
 
314
    """
 
315
 
 
316
    def do_with_locked_branch(self, branch, location):
 
317
        branch._set_parent_location(location)
 
318
        return SuccessfulSmartServerResponse(())
117
319
 
118
320
 
119
321
class SmartServerBranchRequestLockWrite(SmartServerBranchRequest):
120
 
    
 
322
 
121
323
    def do_with_branch(self, branch, branch_token='', repo_token=''):
122
324
        if branch_token == '':
123
325
            branch_token = None
124
326
        if repo_token == '':
125
327
            repo_token = None
126
328
        try:
127
 
            repo_token = branch.repository.lock_write(token=repo_token)
 
329
            repo_token = branch.repository.lock_write(
 
330
                token=repo_token).repository_token
128
331
            try:
129
 
                branch_token = branch.lock_write(token=branch_token)
 
332
                branch_token = branch.lock_write(
 
333
                    token=branch_token).branch_token
130
334
            finally:
131
335
                # this leaves the repository with 1 lock
132
336
                branch.repository.unlock()
163
367
        branch.dont_leave_lock_in_place()
164
368
        branch.unlock()
165
369
        return SuccessfulSmartServerResponse(('ok',))
166
 
        
 
370