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
17
17
"""Server-side repository related request implmentations."""
98
98
search.stop_searching_any(exclude_keys.intersection(next_revs))
99
99
search_result = search.get_result()
100
if search_result.get_recipe()[2] != revision_count:
100
if search_result.get_recipe()[3] != revision_count:
101
101
# we got back a different amount of data than expected, this
102
102
# gets reported as NoSuchRevision, because less revisions
103
103
# indicates missing revisions, and more should never happen as
134
134
from revision_ids is returned. The verb takes a body containing the
135
135
current search state, see do_body for details.
137
If 'include-missing:' is in revision_ids, ghosts encountered in the
138
graph traversal for getting parent data are included in the result with
139
a prefix of 'missing:'.
137
141
:param repository: The repository to query in.
138
142
:param revision_ids: The utf8 encoded revision_id to answer for.
158
162
def _do_repository_request(self, body_bytes):
159
163
repository = self._repository
160
164
revision_ids = set(self._revision_ids)
165
include_missing = 'include-missing:' in revision_ids
167
revision_ids.remove('include-missing:')
161
168
body_lines = body_bytes.split('\n')
162
169
search_result, error = self.recreate_search_from_recipe(
163
170
repository, body_lines)
179
186
queried_revs.update(next_revs)
180
187
parent_map = repo_graph.get_parent_map(next_revs)
188
current_revs = next_revs
181
189
next_revs = set()
182
for revision_id, parents in parent_map.iteritems():
183
# adjust for the wire
184
if parents == (_mod_revision.NULL_REVISION,):
186
# prepare the next query
187
next_revs.update(parents)
188
if revision_id not in client_seen_revs:
190
for revision_id in current_revs:
192
parents = parent_map.get(revision_id)
193
if parents is not None:
194
# adjust for the wire
195
if parents == (_mod_revision.NULL_REVISION,):
197
# prepare the next query
198
next_revs.update(parents)
199
encoded_id = revision_id
202
encoded_id = "missing:" + revision_id
204
if (revision_id not in client_seen_revs and
205
(not missing_rev or include_missing)):
189
206
# Client does not have this revision, give it to it.
190
207
# add parents to the result
191
result[revision_id] = parents
208
result[encoded_id] = parents
192
209
# Approximate the serialized cost of this revision_id.
193
size_so_far += 2 + len(revision_id) + sum(map(len, parents))
210
size_so_far += 2 + len(encoded_id) + sum(map(len, parents))
194
211
# get all the directly asked for parents, and then flesh out to
195
212
# 64K (compressed) or so. We do one level of depth at a time to
196
213
# stay in sync with the client. The 250000 magic number is
527
class SmartServerRepositoryInsertStream(SmartServerRepositoryRequest):
544
class SmartServerRepositoryInsertStreamLocked(SmartServerRepositoryRequest):
528
545
"""Insert a record stream from a RemoteSink into a repository.
530
547
This gets bytes pushed to it by the network infrastructure and turns that
531
548
into a bytes iterator using a thread. That is then processed by
532
549
_byte_stream_to_stream.
535
def do_repository_request(self, repository, resume_tokens):
554
def do_repository_request(self, repository, resume_tokens, lock_token):
536
555
"""StreamSink.insert_stream for a remote repository."""
537
repository.lock_write()
556
repository.lock_write(token=lock_token)
557
self.do_insert_stream_request(repository, resume_tokens)
559
def do_insert_stream_request(self, repository, resume_tokens):
538
560
tokens = [token for token in resume_tokens.split(' ') if token]
539
561
self.tokens = tokens
540
562
self.repository = repository
583
605
self.repository.unlock()
584
606
return SuccessfulSmartServerResponse(('ok', ))
609
class SmartServerRepositoryInsertStream(SmartServerRepositoryInsertStreamLocked):
610
"""Insert a record stream from a RemoteSink into an unlocked repository.
612
This is the same as SmartServerRepositoryInsertStreamLocked, except it
613
takes no lock_tokens; i.e. it works with an unlocked (or lock-free, e.g.
614
like pack format) repository.
619
def do_repository_request(self, repository, resume_tokens):
620
"""StreamSink.insert_stream for a remote repository."""
621
repository.lock_write()
622
self.do_insert_stream_request(repository, resume_tokens)