def do(self, path, *args): """Open a BzrDir at path, and return `self.do_bzrdir_request(*args)`.""" try: self._bzrdir = BzrDir.open_from_transport( self.transport_from_client_path(path)) except errors.NotBranchError, e: return FailedSmartServerResponse(('nobranch', ))
def do_readlocked_repository_request(self, repository, revision_id): """Return the result of repository.get_revision_graph(revision_id). Deprecated as of bzr 1.4, but supported for older clients. :param repository: The repository to query in. :param revision_id: The utf8 encoded revision_id to get a graph from. :return: A smart server response where the body contains an utf8 encoded flattened list of the revision graph. """ if not revision_id: revision_id = None lines = [] graph = repository.get_graph() if revision_id: search_ids = [revision_id] else: search_ids = repository.all_revision_ids() search = graph._make_breadth_first_searcher(search_ids) transitive_ids = set() map(transitive_ids.update, list(search)) parent_map = graph.get_parent_map(transitive_ids) revision_graph = _strip_NULL_ghosts(parent_map) if revision_id and revision_id not in revision_graph: # Note that we return an empty body, rather than omitting the body. # This way the client knows that it can always expect to find a body # in the response for this method, even in the error case. return FailedSmartServerResponse(('nosuchrevision', revision_id), '') for revision, parents in revision_graph.items(): lines.append(' '.join((revision, ) + tuple(parents))) return SuccessfulSmartServerResponse(('ok', ), '\n'.join(lines))
def recreate_search(self, repository, recipe_bytes): lines = recipe_bytes.split('\n') start_keys = set(lines[0].split(' ')) exclude_keys = set(lines[1].split(' ')) revision_count = int(lines[2]) repository.lock_read() try: search = repository.get_graph()._make_breadth_first_searcher( start_keys) while True: try: next_revs = search.next() except StopIteration: break search.stop_searching_any(exclude_keys.intersection(next_revs)) search_result = search.get_result() if search_result.get_recipe()[2] != revision_count: # we got back a different amount of data than expected, this # gets reported as NoSuchRevision, because less revisions # indicates missing revisions, and more should never happen as # the excludes list considers ghosts and ensures that ghost # filling races are not a problem. return (None, FailedSmartServerResponse(('NoSuchRevision', ))) return (search, None) finally: repository.unlock()
def test_NoSuchRevision(self): """If the revision_id is not present, the verb returns NoSuchRevision. """ revision_id = 'non-existent revision' self.assertEqual( FailedSmartServerResponse(('NoSuchRevision', revision_id)), self.set_last_revision(revision_id, 1))
def do_bzrdir_request(self): """Open a branch at path and return the reference or format. This version introduced in 2.1. Differences to SmartServerRequestOpenBranchV2: * can return 2-element ('nobranch', extra), where 'extra' is a string with an explanation like 'location is a repository'. Previously a 'nobranch' response would never have more than one element. """ try: reference_url = self._bzrdir.get_branch_reference() if reference_url is None: br = self._bzrdir.open_branch(ignore_fallbacks=True) format = br._format.network_name() return SuccessfulSmartServerResponse(('branch', format)) else: return SuccessfulSmartServerResponse(('ref', reference_url)) except errors.NotBranchError, e: # Stringify the exception so that its .detail attribute will be # filled out. str(e) resp = ('nobranch', ) detail = e.detail if detail: if detail.startswith(': '): detail = detail[2:] resp += (detail, ) return FailedSmartServerResponse(resp)
def recreate_search_from_recipe(self, repository, lines, discard_excess=False): """Recreate a specific revision search (vs a from-tip search). :param discard_excess: If True, and the search refers to data we don't have, just silently accept that fact - the verb calling recreate_search trusts that clients will look for missing things they expected and get it from elsewhere. """ start_keys = set(lines[0].split(' ')) exclude_keys = set(lines[1].split(' ')) revision_count = int(lines[2]) repository.lock_read() try: search = repository.get_graph()._make_breadth_first_searcher( start_keys) while True: try: next_revs = search.next() except StopIteration: break search.stop_searching_any(exclude_keys.intersection(next_revs)) (started_keys, excludes, included_keys) = search.get_state() if (not discard_excess and len(included_keys) != revision_count): # we got back a different amount of data than expected, this # gets reported as NoSuchRevision, because less revisions # indicates missing revisions, and more should never happen as # the excludes list considers ghosts and ensures that ghost # filling races are not a problem. return (None, FailedSmartServerResponse(('NoSuchRevision',))) search_result = vf_search.SearchResult(started_keys, excludes, len(included_keys), included_keys) return (search_result, None) finally: repository.unlock()
def do_with_locked_branch(self, branch, *args): try: return self.do_tip_change_with_locked_branch(branch, *args) except errors.TipChangeRejected, e: msg = e.msg if isinstance(msg, unicode): msg = msg.encode('utf-8') return FailedSmartServerResponse(('TipChangeRejected', msg))
def test__str__(self): """SmartServerResponses can be stringified.""" self.assertEqual( "<SmartServerResponse status=OK args=('args',) body='body'>", str(SuccessfulSmartServerResponse(('args',), 'body'))) self.assertEqual( "<SmartServerResponse status=ERR args=('args',) body='body'>", str(FailedSmartServerResponse(('args',), 'body')))
def do_tip_change_with_locked_branch(self, branch, new_last_revision_id, allow_divergence, allow_overwrite_descendant): """Set the last revision of the branch. New in 1.6. :param new_last_revision_id: the revision ID to set as the last revision of the branch. :param allow_divergence: A flag. If non-zero, change the revision ID even if the new_last_revision_id's ancestry has diverged from the current last revision. If zero, a 'Diverged' error will be returned if new_last_revision_id is not a descendant of the current last revision. :param allow_overwrite_descendant: A flag. If zero and new_last_revision_id is not a descendant of the current last revision, then the last revision will not be changed. If non-zero and there is no divergence, then the last revision is always changed. :returns: on success, a tuple of ('ok', revno, revision_id), where revno and revision_id are the new values of the current last revision info. The revision_id might be different to the new_last_revision_id if allow_overwrite_descendant was not set. """ do_not_overwrite_descendant = not allow_overwrite_descendant try: last_revno, last_rev = branch.last_revision_info() graph = branch.repository.get_graph() if not allow_divergence or do_not_overwrite_descendant: relation = branch._revision_relations(last_rev, new_last_revision_id, graph) if relation == 'diverged' and not allow_divergence: return FailedSmartServerResponse(('Diverged', )) if relation == 'a_descends_from_b' and do_not_overwrite_descendant: return SuccessfulSmartServerResponse( ('ok', last_revno, last_rev)) new_revno = graph.find_distance_to_null(new_last_revision_id, [(last_rev, last_revno)]) branch.set_last_revision_info(new_revno, new_last_revision_id) except errors.GhostRevisionsHaveNoRevno: return FailedSmartServerResponse( ('NoSuchRevision', new_last_revision_id)) return SuccessfulSmartServerResponse( ('ok', new_revno, new_last_revision_id))
def do_tip_change_with_locked_branch(self, branch, new_revno, new_last_revision_id): try: branch.set_last_revision_info(int(new_revno), new_last_revision_id) except errors.NoSuchRevision: return FailedSmartServerResponse( ('NoSuchRevision', new_last_revision_id)) return SuccessfulSmartServerResponse(('ok', ))
def do_repository_request(self, repository, token=''): # XXX: this probably should not have a token. if token == '': token = None try: token = repository.lock_write(token=token).repository_token except errors.LockContention, e: return FailedSmartServerResponse(('LockContention',))
def do_tip_change_with_locked_branch(self, branch, new_last_revision_id): if new_last_revision_id == 'null:': branch.set_revision_history([]) else: if not branch.repository.has_revision(new_last_revision_id): return FailedSmartServerResponse( ('NoSuchRevision', new_last_revision_id)) branch.generate_revision_history(new_last_revision_id) return SuccessfulSmartServerResponse(('ok', ))
def body_stream(self, stream, repository): byte_stream = _stream_to_byte_stream(stream, repository._format) try: for bytes in byte_stream: yield bytes except errors.RevisionNotPresent, e: # This shouldn't be able to happen, but as we don't buffer # everything it can in theory happen. repository.unlock() yield FailedSmartServerResponse(('NoSuchRevision', e.revision_id))
def do_bzrdir_request(self): """open a branch at path and return the branch reference or branch.""" try: reference_url = self._bzrdir.get_branch_reference() if reference_url is None: return SuccessfulSmartServerResponse(('ok', '')) else: return SuccessfulSmartServerResponse(('ok', reference_url)) except errors.NotBranchError, e: return FailedSmartServerResponse(('nobranch', ))
def do_bzrdir_request(self, name=None): """Destroy the branch with the specified name. New in 2.5.0. :return: On success, 'ok'. """ try: self._bzrdir.destroy_branch(name) except errors.NotBranchError, e: return FailedSmartServerResponse(('nobranch', ))
def do_repository_request(self, repository, lock_token, write_group_tokens): """Abort a write group.""" repository.lock_write(token=lock_token) try: try: repository.resume_write_group(write_group_tokens) except errors.UnresumableWriteGroup, e: return FailedSmartServerResponse( ('UnresumableWriteGroup', e.write_groups, e.reason)) else:
def test_not_allow_diverged(self): """If allow_diverged is not passed, then setting a divergent history returns a Diverged error. """ self.make_branch_with_divergent_history() self.assertEqual( FailedSmartServerResponse(('Diverged',)), self.set_last_revision('child-1', 2)) # The branch tip was not changed. self.assertEqual('child-2', self.tree.branch.last_revision())
def do_bzrdir_request(self, name=None): """Destroy the repository. New in 2.5.0. :return: On success, 'ok'. """ try: self._bzrdir.destroy_repository() except errors.NoRepositoryPresent, e: return FailedSmartServerResponse(('norepository', ))
class SmartServerRepositoryLockWrite(SmartServerRepositoryRequest): def do_repository_request(self, repository, token=''): # XXX: this probably should not have a token. if token == '': token = None try: token = repository.lock_write(token=token) except errors.LockContention, e: return FailedSmartServerResponse(('LockContention', )) except errors.UnlockableTransport: return FailedSmartServerResponse(('UnlockableTransport', ))
def do_repository_request(self, repository, lock_token): """Start a write group.""" repository.lock_write(token=lock_token) try: repository.start_write_group() try: tokens = repository.suspend_write_group() except errors.UnsuspendableWriteGroup: return FailedSmartServerResponse(('UnsuspendableWriteGroup',)) finally: repository.unlock() return SuccessfulSmartServerResponse(('ok', tokens))
def do_with_branch(self, branch, branch_token='', repo_token=''): if branch_token == '': branch_token = None if repo_token == '': repo_token = None try: repo_token = branch.repository.lock_write(token=repo_token) try: branch_token = branch.lock_write(token=branch_token) finally: # this leaves the repository with 1 lock branch.repository.unlock() except errors.LockContention: return FailedSmartServerResponse(('LockContention', )) except errors.TokenMismatch: return FailedSmartServerResponse(('TokenMismatch', )) except errors.UnlockableTransport: return FailedSmartServerResponse(('UnlockableTransport', )) except errors.LockFailed, e: return FailedSmartServerResponse( ('LockFailed', str(e.lock), str(e.why)))
def do_repository_request(self, repository, revision_id): """Return the result of repository.get_signature_text(). :param repository: The repository to query in. :return: A smart server response of with the signature text as body. """ try: text = repository.get_signature_text(revision_id) except errors.NoSuchRevision, err: return FailedSmartServerResponse( ('nosuchrevision', err.revision))
def do_bzrdir_request(self): """open a branch at path and return the reference or format.""" try: reference_url = self._bzrdir.get_branch_reference() if reference_url is None: br = self._bzrdir.open_branch(ignore_fallbacks=True) format = br._format.network_name() return SuccessfulSmartServerResponse(('branch', format)) else: return SuccessfulSmartServerResponse(('ref', reference_url)) except errors.NotBranchError, e: return FailedSmartServerResponse(('nobranch', ))
def do_with_branch(self, branch, revid): """Return branch.revision_id_to_revno(). New in 2.5. The revno is encoded in decimal, the revision_id is encoded as utf8. """ try: dotted_revno = branch.revision_id_to_dotted_revno(revid) except errors.NoSuchRevision: return FailedSmartServerResponse(('NoSuchRevision', revid)) return SuccessfulSmartServerResponse(('ok', ) + tuple(map(str, dotted_revno)))
def test_TipChangeRejected(self): """If a pre_change_branch_tip hook raises TipChangeRejected, the verb returns TipChangeRejected. """ rejection_message = u'rejection message\N{INTERROBANG}' def hook_that_rejects(params): raise errors.TipChangeRejected(rejection_message) Branch.hooks.install_named_hook( 'pre_change_branch_tip', hook_that_rejects, None) self.assertEqual( FailedSmartServerResponse( ('TipChangeRejected', rejection_message.encode('utf-8'))), self.set_last_revision('null:', 0))
def do_with_branch(self, branch, branch_token, repo_token): try: branch.repository.lock_write(token=repo_token) try: branch.lock_write(token=branch_token) finally: branch.repository.unlock() except errors.TokenMismatch: return FailedSmartServerResponse(('TokenMismatch', )) if repo_token: branch.repository.dont_leave_lock_in_place() branch.dont_leave_lock_in_place() branch.unlock() return SuccessfulSmartServerResponse(('ok', ))
def do_readlocked_repository_request(self, repository, revno, known_pair): """Find the revid for a given revno, given a known revno/revid pair. New in 1.17. """ try: found_flag, result = repository.get_rev_id_for_revno(revno, known_pair) except errors.RevisionNotPresent, err: if err.revision_id != known_pair[1]: raise AssertionError( 'get_rev_id_for_revno raised RevisionNotPresent for ' 'non-initial revision: ' + err.revision_id) return FailedSmartServerResponse( ('nosuchrevision', err.revision_id))
def do(self, path): """try to open a branch at path and return ok/nobranch. If a bzrdir is not present, an exception is propogated rather than 'no branch' because these are different conditions. """ bzrdir = BzrDir.open_from_transport( self.transport_from_client_path(path)) try: reference_url = bzrdir.get_branch_reference() if reference_url is None: return SuccessfulSmartServerResponse(('ok', '')) else: return SuccessfulSmartServerResponse(('ok', reference_url)) except errors.NotBranchError: return FailedSmartServerResponse(('nobranch', ))
def do_repository_request(self, repository, lock_token, write_group_tokens): """Commit a write group.""" repository.lock_write(token=lock_token) try: try: repository.resume_write_group(write_group_tokens) except errors.UnresumableWriteGroup, e: return FailedSmartServerResponse( ('UnresumableWriteGroup', e.write_groups, e.reason)) try: repository.commit_write_group() except: write_group_tokens = repository.suspend_write_group() # FIXME JRV 2011-11-19: What if the write_group_tokens # have changed? raise
def do_repository_request(self, repository, to_network_name): """Get a stream for inserting into a to_format repository. The request body is 'search_bytes', a description of the revisions being requested. In 2.3 this verb added support for search_bytes == 'everything'. Older implementations will respond with a BadSearch error, and clients should catch this and fallback appropriately. :param repository: The repository to stream from. :param to_network_name: The network name of the format of the target repository. """ self._to_format = network_format_registry.get(to_network_name) if self._should_fake_unknown(): return FailedSmartServerResponse( ('UnknownMethod', 'Repository.get_stream')) return None # Signal that we want a body.