~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/branch.py

  • Committer: Martin Pool
  • Date: 2007-04-04 06:17:31 UTC
  • mto: This revision was merged to the branch mainline in revision 2397.
  • Revision ID: mbp@sourcefrog.net-20070404061731-tt2xrzllqhbodn83
Contents of TODO file moved into bug tracker

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 Canonical Ltd
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
 
 
17
 
"""Server-side branch related request implmentations."""
18
 
 
19
 
 
20
 
from bzrlib import errors
21
 
from bzrlib.bzrdir import BzrDir
22
 
from bzrlib.smart.request import (
23
 
    FailedSmartServerResponse,
24
 
    SmartServerRequest,
25
 
    SuccessfulSmartServerResponse,
26
 
    )
27
 
 
28
 
 
29
 
class SmartServerBranchRequest(SmartServerRequest):
30
 
    """Base class for handling common branch request logic.
31
 
    """
32
 
 
33
 
    def do(self, path, *args):
34
 
        """Execute a request for a branch at path.
35
 
 
36
 
        All Branch requests take a path to the branch as their first argument.
37
 
 
38
 
        If the branch is a branch reference, NotBranchError is raised.
39
 
 
40
 
        :param path: The path for the repository as received from the
41
 
            client.
42
 
        :return: A SmartServerResponse from self.do_with_branch().
43
 
        """
44
 
        transport = self.transport_from_client_path(path)
45
 
        bzrdir = BzrDir.open_from_transport(transport)
46
 
        if bzrdir.get_branch_reference() is not None:
47
 
            raise errors.NotBranchError(transport.base)
48
 
        branch = bzrdir.open_branch(ignore_fallbacks=True)
49
 
        return self.do_with_branch(branch, *args)
50
 
 
51
 
 
52
 
class SmartServerLockedBranchRequest(SmartServerBranchRequest):
53
 
    """Base class for handling common branch request logic for requests that
54
 
    need a write lock.
55
 
    """
56
 
 
57
 
    def do_with_branch(self, branch, branch_token, repo_token, *args):
58
 
        """Execute a request for a branch.
59
 
 
60
 
        A write lock will be acquired with the given tokens for the branch and
61
 
        repository locks.  The lock will be released once the request is
62
 
        processed.  The physical lock state won't be changed.
63
 
        """
64
 
        # XXX: write a test for LockContention
65
 
        branch.repository.lock_write(token=repo_token)
66
 
        try:
67
 
            branch.lock_write(token=branch_token)
68
 
            try:
69
 
                return self.do_with_locked_branch(branch, *args)
70
 
            finally:
71
 
                branch.unlock()
72
 
        finally:
73
 
            branch.repository.unlock()
74
 
 
75
 
 
76
 
class SmartServerBranchGetConfigFile(SmartServerBranchRequest):
77
 
 
78
 
    def do_with_branch(self, branch):
79
 
        """Return the content of branch.conf
80
 
 
81
 
        The body is not utf8 decoded - its the literal bytestream from disk.
82
 
        """
83
 
        try:
84
 
            content = branch._transport.get_bytes('branch.conf')
85
 
        except errors.NoSuchFile:
86
 
            content = ''
87
 
        return SuccessfulSmartServerResponse( ('ok', ), content)
88
 
 
89
 
 
90
 
class SmartServerBranchGetParent(SmartServerBranchRequest):
91
 
 
92
 
    def do_with_branch(self, branch):
93
 
        """Return the parent of branch."""
94
 
        parent = branch._get_parent_location() or ''
95
 
        return SuccessfulSmartServerResponse((parent,))
96
 
 
97
 
 
98
 
class SmartServerBranchGetTagsBytes(SmartServerBranchRequest):
99
 
 
100
 
    def do_with_branch(self, branch):
101
 
        """Return the _get_tags_bytes for a branch."""
102
 
        bytes = branch._get_tags_bytes()
103
 
        return SuccessfulSmartServerResponse((bytes,))
104
 
 
105
 
 
106
 
class SmartServerBranchRequestGetStackedOnURL(SmartServerBranchRequest):
107
 
 
108
 
    def do_with_branch(self, branch):
109
 
        stacked_on_url = branch.get_stacked_on_url()
110
 
        return SuccessfulSmartServerResponse(('ok', stacked_on_url))
111
 
 
112
 
 
113
 
class SmartServerRequestRevisionHistory(SmartServerBranchRequest):
114
 
 
115
 
    def do_with_branch(self, branch):
116
 
        """Get the revision history for the branch.
117
 
 
118
 
        The revision list is returned as the body content,
119
 
        with each revision utf8 encoded and \x00 joined.
120
 
        """
121
 
        return SuccessfulSmartServerResponse(
122
 
            ('ok', ), ('\x00'.join(branch.revision_history())))
123
 
 
124
 
 
125
 
class SmartServerBranchRequestLastRevisionInfo(SmartServerBranchRequest):
126
 
 
127
 
    def do_with_branch(self, branch):
128
 
        """Return branch.last_revision_info().
129
 
 
130
 
        The revno is encoded in decimal, the revision_id is encoded as utf8.
131
 
        """
132
 
        revno, last_revision = branch.last_revision_info()
133
 
        return SuccessfulSmartServerResponse(('ok', str(revno), last_revision))
134
 
 
135
 
 
136
 
class SmartServerSetTipRequest(SmartServerLockedBranchRequest):
137
 
    """Base class for handling common branch request logic for requests that
138
 
    update the branch tip.
139
 
    """
140
 
 
141
 
    def do_with_locked_branch(self, branch, *args):
142
 
        try:
143
 
            return self.do_tip_change_with_locked_branch(branch, *args)
144
 
        except errors.TipChangeRejected, e:
145
 
            msg = e.msg
146
 
            if isinstance(msg, unicode):
147
 
                msg = msg.encode('utf-8')
148
 
            return FailedSmartServerResponse(('TipChangeRejected', msg))
149
 
 
150
 
 
151
 
class SmartServerBranchRequestSetConfigOption(SmartServerLockedBranchRequest):
152
 
    """Set an option in the branch configuration."""
153
 
 
154
 
    def do_with_locked_branch(self, branch, value, name, section):
155
 
        if not section:
156
 
            section = None
157
 
        branch._get_config().set_option(value.decode('utf8'), name, section)
158
 
        return SuccessfulSmartServerResponse(())
159
 
 
160
 
 
161
 
class SmartServerBranchRequestSetLastRevision(SmartServerSetTipRequest):
162
 
 
163
 
    def do_tip_change_with_locked_branch(self, branch, new_last_revision_id):
164
 
        if new_last_revision_id == 'null:':
165
 
            branch.set_revision_history([])
166
 
        else:
167
 
            if not branch.repository.has_revision(new_last_revision_id):
168
 
                return FailedSmartServerResponse(
169
 
                    ('NoSuchRevision', new_last_revision_id))
170
 
            branch.set_revision_history(branch._lefthand_history(
171
 
                new_last_revision_id, None, None))
172
 
        return SuccessfulSmartServerResponse(('ok',))
173
 
 
174
 
 
175
 
class SmartServerBranchRequestSetLastRevisionEx(SmartServerSetTipRequest):
176
 
 
177
 
    def do_tip_change_with_locked_branch(self, branch, new_last_revision_id,
178
 
            allow_divergence, allow_overwrite_descendant):
179
 
        """Set the last revision of the branch.
180
 
 
181
 
        New in 1.6.
182
 
 
183
 
        :param new_last_revision_id: the revision ID to set as the last
184
 
            revision of the branch.
185
 
        :param allow_divergence: A flag.  If non-zero, change the revision ID
186
 
            even if the new_last_revision_id's ancestry has diverged from the
187
 
            current last revision.  If zero, a 'Diverged' error will be
188
 
            returned if new_last_revision_id is not a descendant of the current
189
 
            last revision.
190
 
        :param allow_overwrite_descendant:  A flag.  If zero and
191
 
            new_last_revision_id is not a descendant of the current last
192
 
            revision, then the last revision will not be changed.  If non-zero
193
 
            and there is no divergence, then the last revision is always
194
 
            changed.
195
 
 
196
 
        :returns: on success, a tuple of ('ok', revno, revision_id), where
197
 
            revno and revision_id are the new values of the current last
198
 
            revision info.  The revision_id might be different to the
199
 
            new_last_revision_id if allow_overwrite_descendant was not set.
200
 
        """
201
 
        do_not_overwrite_descendant = not allow_overwrite_descendant
202
 
        try:
203
 
            last_revno, last_rev = branch.last_revision_info()
204
 
            graph = branch.repository.get_graph()
205
 
            if not allow_divergence or do_not_overwrite_descendant:
206
 
                relation = branch._revision_relations(
207
 
                    last_rev, new_last_revision_id, graph)
208
 
                if relation == 'diverged' and not allow_divergence:
209
 
                    return FailedSmartServerResponse(('Diverged',))
210
 
                if relation == 'a_descends_from_b' and do_not_overwrite_descendant:
211
 
                    return SuccessfulSmartServerResponse(
212
 
                        ('ok', last_revno, last_rev))
213
 
            new_revno = graph.find_distance_to_null(
214
 
                new_last_revision_id, [(last_rev, last_revno)])
215
 
            branch.set_last_revision_info(new_revno, new_last_revision_id)
216
 
        except errors.GhostRevisionsHaveNoRevno:
217
 
            return FailedSmartServerResponse(
218
 
                ('NoSuchRevision', new_last_revision_id))
219
 
        return SuccessfulSmartServerResponse(
220
 
            ('ok', new_revno, new_last_revision_id))
221
 
 
222
 
 
223
 
class SmartServerBranchRequestSetLastRevisionInfo(SmartServerSetTipRequest):
224
 
    """Branch.set_last_revision_info.  Sets the revno and the revision ID of
225
 
    the specified branch.
226
 
 
227
 
    New in bzrlib 1.4.
228
 
    """
229
 
 
230
 
    def do_tip_change_with_locked_branch(self, branch, new_revno,
231
 
            new_last_revision_id):
232
 
        try:
233
 
            branch.set_last_revision_info(int(new_revno), new_last_revision_id)
234
 
        except errors.NoSuchRevision:
235
 
            return FailedSmartServerResponse(
236
 
                ('NoSuchRevision', new_last_revision_id))
237
 
        return SuccessfulSmartServerResponse(('ok',))
238
 
 
239
 
 
240
 
class SmartServerBranchRequestSetParentLocation(SmartServerLockedBranchRequest):
241
 
    """Set the parent location for a branch.
242
 
    
243
 
    Takes a location to set, which must be utf8 encoded.
244
 
    """
245
 
 
246
 
    def do_with_locked_branch(self, branch, location):
247
 
        branch._set_parent_location(location)
248
 
        return SuccessfulSmartServerResponse(())
249
 
 
250
 
 
251
 
class SmartServerBranchRequestLockWrite(SmartServerBranchRequest):
252
 
 
253
 
    def do_with_branch(self, branch, branch_token='', repo_token=''):
254
 
        if branch_token == '':
255
 
            branch_token = None
256
 
        if repo_token == '':
257
 
            repo_token = None
258
 
        try:
259
 
            repo_token = branch.repository.lock_write(token=repo_token)
260
 
            try:
261
 
                branch_token = branch.lock_write(token=branch_token)
262
 
            finally:
263
 
                # this leaves the repository with 1 lock
264
 
                branch.repository.unlock()
265
 
        except errors.LockContention:
266
 
            return FailedSmartServerResponse(('LockContention',))
267
 
        except errors.TokenMismatch:
268
 
            return FailedSmartServerResponse(('TokenMismatch',))
269
 
        except errors.UnlockableTransport:
270
 
            return FailedSmartServerResponse(('UnlockableTransport',))
271
 
        except errors.LockFailed, e:
272
 
            return FailedSmartServerResponse(('LockFailed', str(e.lock), str(e.why)))
273
 
        if repo_token is None:
274
 
            repo_token = ''
275
 
        else:
276
 
            branch.repository.leave_lock_in_place()
277
 
        branch.leave_lock_in_place()
278
 
        branch.unlock()
279
 
        return SuccessfulSmartServerResponse(('ok', branch_token, repo_token))
280
 
 
281
 
 
282
 
class SmartServerBranchRequestUnlock(SmartServerBranchRequest):
283
 
 
284
 
    def do_with_branch(self, branch, branch_token, repo_token):
285
 
        try:
286
 
            branch.repository.lock_write(token=repo_token)
287
 
            try:
288
 
                branch.lock_write(token=branch_token)
289
 
            finally:
290
 
                branch.repository.unlock()
291
 
        except errors.TokenMismatch:
292
 
            return FailedSmartServerResponse(('TokenMismatch',))
293
 
        if repo_token:
294
 
            branch.repository.dont_leave_lock_in_place()
295
 
        branch.dont_leave_lock_in_place()
296
 
        branch.unlock()
297
 
        return SuccessfulSmartServerResponse(('ok',))
298