51
53
transport = self.transport_from_client_path(path)
52
54
bzrdir = BzrDir.open_from_transport(transport)
53
repository = bzrdir.open_repository()
54
return self.do_repository_request(repository, *args)
55
# Save the repository for use with do_body.
56
self._repository = bzrdir.open_repository()
57
return self.do_repository_request(self._repository, *args)
59
def do_repository_request(self, repository, *args):
60
"""Override to provide an implementation for a verb."""
61
# No-op for verbs that take bodies (None as a result indicates a body
65
def recreate_search(self, repository, recipe_bytes):
66
lines = recipe_bytes.split('\n')
67
start_keys = set(lines[0].split(' '))
68
exclude_keys = set(lines[1].split(' '))
69
revision_count = int(lines[2])
70
repository.lock_read()
72
search = repository.get_graph()._make_breadth_first_searcher(
76
next_revs = search.next()
79
search.stop_searching_any(exclude_keys.intersection(next_revs))
80
search_result = search.get_result()
81
if search_result.get_recipe()[2] != revision_count:
82
# we got back a different amount of data than expected, this
83
# gets reported as NoSuchRevision, because less revisions
84
# indicates missing revisions, and more should never happen as
85
# the excludes list considers ghosts and ensures that ghost
86
# filling races are not a problem.
87
return (None, FailedSmartServerResponse(('NoSuchRevision',)))
93
class SmartServerRepositoryGetParentMap(SmartServerRepositoryRequest):
94
"""Bzr 1.2+ - get parent data for revisions during a graph search."""
96
def do_repository_request(self, repository, *revision_ids):
97
"""Get parent details for some revisions.
99
All the parents for revision_ids are returned. Additionally up to 64KB
100
of additional parent data found by performing a breadth first search
101
from revision_ids is returned. The verb takes a body containing the
102
current search state, see do_body for details.
104
:param repository: The repository to query in.
105
:param revision_ids: The utf8 encoded revision_id to answer for.
107
self._revision_ids = revision_ids
108
return None # Signal that we want a body.
110
def do_body(self, body_bytes):
111
"""Process the current search state and perform the parent lookup.
113
:return: A smart server response where the body contains an utf8
114
encoded flattened list of the parents of the revisions (the same
115
format as Repository.get_revision_graph) which has been bz2
118
repository = self._repository
119
repository.lock_read()
121
return self._do_repository_request(body_bytes)
125
def _do_repository_request(self, body_bytes):
126
repository = self._repository
127
revision_ids = set(self._revision_ids)
128
search, error = self.recreate_search(repository, body_bytes)
129
if error is not None:
131
# TODO might be nice to start up the search again; but thats not
132
# written or tested yet.
133
client_seen_revs = set(search.get_result().get_keys())
134
# Always include the requested ids.
135
client_seen_revs.difference_update(revision_ids)
137
repo_graph = repository.get_graph()
141
next_revs = revision_ids
142
first_loop_done = False
144
queried_revs.update(next_revs)
145
parent_map = repo_graph.get_parent_map(next_revs)
147
for revision_id, parents in parent_map.iteritems():
148
# adjust for the wire
149
if parents == (_mod_revision.NULL_REVISION,):
151
# prepare the next query
152
next_revs.update(parents)
153
if revision_id not in client_seen_revs:
154
# Client does not have this revision, give it to it.
155
# add parents to the result
156
result[revision_id] = parents
157
# Approximate the serialized cost of this revision_id.
158
size_so_far += 2 + len(revision_id) + sum(map(len, parents))
159
# get all the directly asked for parents, and then flesh out to
160
# 64K (compressed) or so. We do one level of depth at a time to
161
# stay in sync with the client. The 250000 magic number is
162
# estimated compression ratio taken from bzr.dev itself.
163
if first_loop_done and size_so_far > 250000:
166
# don't query things we've already queried
167
next_revs.difference_update(queried_revs)
168
first_loop_done = True
170
# sorting trivially puts lexographically similar revision ids together.
172
for revision, parents in sorted(result.items()):
173
lines.append(' '.join((revision, ) + tuple(parents)))
175
return SuccessfulSmartServerResponse(
176
('ok', ), bz2.compress('\n'.join(lines)))
57
179
class SmartServerRepositoryGetRevisionGraph(SmartServerRepositoryRequest):