~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/branch.py

  • Committer: John Arbash Meinel
  • Date: 2006-10-16 04:50:19 UTC
  • mfrom: (2079 +trunk)
  • mto: This revision was merged to the branch mainline in revision 2080.
  • Revision ID: john@arbash-meinel.com-20061016045019-58fb4e339f93b1d7
[merge] bzr.dev 2079

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 SmartServerBranchRequestLockWrite(SmartServerBranchRequest):
241
 
 
242
 
    def do_with_branch(self, branch, branch_token='', repo_token=''):
243
 
        if branch_token == '':
244
 
            branch_token = None
245
 
        if repo_token == '':
246
 
            repo_token = None
247
 
        try:
248
 
            repo_token = branch.repository.lock_write(token=repo_token)
249
 
            try:
250
 
                branch_token = branch.lock_write(token=branch_token)
251
 
            finally:
252
 
                # this leaves the repository with 1 lock
253
 
                branch.repository.unlock()
254
 
        except errors.LockContention:
255
 
            return FailedSmartServerResponse(('LockContention',))
256
 
        except errors.TokenMismatch:
257
 
            return FailedSmartServerResponse(('TokenMismatch',))
258
 
        except errors.UnlockableTransport:
259
 
            return FailedSmartServerResponse(('UnlockableTransport',))
260
 
        except errors.LockFailed, e:
261
 
            return FailedSmartServerResponse(('LockFailed', str(e.lock), str(e.why)))
262
 
        if repo_token is None:
263
 
            repo_token = ''
264
 
        else:
265
 
            branch.repository.leave_lock_in_place()
266
 
        branch.leave_lock_in_place()
267
 
        branch.unlock()
268
 
        return SuccessfulSmartServerResponse(('ok', branch_token, repo_token))
269
 
 
270
 
 
271
 
class SmartServerBranchRequestUnlock(SmartServerBranchRequest):
272
 
 
273
 
    def do_with_branch(self, branch, branch_token, repo_token):
274
 
        try:
275
 
            branch.repository.lock_write(token=repo_token)
276
 
            try:
277
 
                branch.lock_write(token=branch_token)
278
 
            finally:
279
 
                branch.repository.unlock()
280
 
        except errors.TokenMismatch:
281
 
            return FailedSmartServerResponse(('TokenMismatch',))
282
 
        if repo_token:
283
 
            branch.repository.dont_leave_lock_in_place()
284
 
        branch.dont_leave_lock_in_place()
285
 
        branch.unlock()
286
 
        return SuccessfulSmartServerResponse(('ok',))
287