Ejemplo n.º 1
0
    def main(self, request_id):
        """Run the command."""
        repository_info, tool = self.initialize_scm_tool(
            client_name=self.options.repository_type)
        server_url = self.get_server_url(repository_info, tool)
        api_client, api_root = self.get_api(server_url)

        request = get_review_request(request_id, api_root)

        self.setup_tool(tool, api_root)

        update_fields = {
            'public': True,
        }

        if (self.options.trivial_publish and
            tool.capabilities.has_capability('review_requests',
                                             'trivial_publish')):
            update_fields['trivial'] = True

        try:
            draft = request.get_draft()
            draft.update(**update_fields)
        except APIError as e:
            raise CommandError('Error publishing review request (it may '
                               'already be published): %s' % e)

        print('Review request #%s is published.' % request_id)
Ejemplo n.º 2
0
    def main(self, request_id):
        """Run the command."""
        close_type = self.options.close_type
        self.check_valid_type(close_type)
        if self.options.server:
            # Bypass getting the scm_tool to discover the server since it was
            # specified with --server or in .reviewboardrc
            repository_info, tool = None, None
        else:
            repository_info, tool = self.initialize_scm_tool(
                client_name=self.options.repository_type)
        server_url = self.get_server_url(repository_info, tool)
        api_client, api_root = self.get_api(server_url)
        request = get_review_request(request_id, api_root)

        if request.status == close_type:
            raise CommandError('Review request #%s is already %s.' %
                               (request_id, close_type))

        if self.options.description:
            request = request.update(status=close_type,
                                     description=self.options.description)
        else:
            request = request.update(status=close_type)

        print('Review request #%s is set to %s.' %
              (request_id, request.status))
Ejemplo n.º 3
0
    def main(self, request_id):
        """Run the command."""
        repository_info, tool = self.initialize_scm_tool(
            client_name=self.options.repository_type)
        server_url = self.get_server_url(repository_info, tool)
        api_client, api_root = self.get_api(server_url)

        request = get_review_request(request_id, api_root)

        self.setup_tool(tool, api_root)

        update_fields = {
            'public': True,
        }

        if (self.options.trivial_publish and tool.capabilities.has_capability(
                'review_requests', 'trivial_publish')):
            update_fields['trivial'] = True

        try:
            draft = request.get_draft()
            draft.update(**update_fields)
        except APIError as e:
            raise CommandError('Error publishing review request (it may '
                               'already be published): %s' % e)

        print('Review request #%s is published.' % request_id)
Ejemplo n.º 4
0
    def main(self, request_id):
        """Run the command."""
        close_type = self.options.close_type
        self.check_valid_type(close_type)
        if self.options.server:
            # Bypass getting the scm_tool to discover the server since it was
            # specified with --server or in .reviewboardrc
            repository_info, tool = None, None
        else:
            repository_info, tool = self.initialize_scm_tool(
                client_name=self.options.repository_type)
        server_url = self.get_server_url(repository_info, tool)
        api_client, api_root = self.get_api(server_url)
        request = get_review_request(request_id, api_root)

        if request.status == close_type:
            raise CommandError('Review request #%s is already %s.' % (
                request_id, close_type))

        if self.options.description:
            request = request.update(status=close_type,
                                     description=self.options.description)
        else:
            request = request.update(status=close_type)

        print('Review request #%s is set to %s.' %
              (request_id, request.status))
Ejemplo n.º 5
0
    def main(self, request_id, path_to_file):
        self.repository_info, self.tool = self.initialize_scm_tool(
            client_name=self.options.repository_type)
        server_url = self.get_server_url(self.repository_info, self.tool)
        api_client, api_root = self.get_api(server_url)

        request = get_review_request(request_id, api_root)

        try:
            with open(path_to_file, 'rb') as f:
                content = f.read()
        except IOError:
            raise CommandError('%s is not a valid file.' % path_to_file)

        # Check if the user specified a custom filename, otherwise
        # use the original filename.
        filename = self.options.filename or os.path.basename(path_to_file)

        try:
            request.get_file_attachments().upload_attachment(
                filename, content, self.options.caption)
        except APIError as e:
            raise CommandError('Error uploading file: %s' % e)

        print('Uploaded %s to review request %s.' % (path_to_file, request_id))
Ejemplo n.º 6
0
    def post_request(self, repository_info, server_url, api_root,
                     review_request_id=None, changenum=None, diff_content=None,
                     parent_diff_content=None, commit_id=None,
                     base_commit_id=None,
                     submit_as=None, retries=3):
        """Creates or updates a review request, and uploads a diff.

        On success the review request id and url are returned.
        """
        supports_posting_commit_ids = \
            self.tool.capabilities.has_capability('review_requests',
                                                  'commit_ids')

        if review_request_id:
            review_request = get_review_request(review_request_id, api_root)

            if review_request.status == 'submitted':
                raise CommandError(
                    "Review request %s is marked as %s. In order to update "
                    "it, please reopen the review request and try again."
                    % (review_request_id, review_request.status))
        else:
            # No review_request_id, so we will create a new review request.
            try:
                repository = (
                    self.options.repository_name or
                    self.options.repository_url or
                    self.get_repository_path(repository_info, api_root))
                request_data = {
                    'repository': repository
                }

                if changenum:
                    request_data['changenum'] = changenum
                elif commit_id and supports_posting_commit_ids:
                    request_data['commit_id'] = commit_id

                if submit_as:
                    request_data['submit_as'] = submit_as

                review_request = api_root.get_review_requests().create(
                    **request_data)
            except APIError, e:
                if e.error_code == 204 and changenum:  # Change number in use.
                    rid = e.rsp['review_request']['id']
                    review_request = api_root.get_review_request(
                        review_request_id=rid)

                    if not self.options.diff_only:
                        review_request = review_request.update(
                            changenum=changenum)
                else:
                    raise CommandError("Error creating review request: %s" % e)
Ejemplo n.º 7
0
    def main(self, *args):
        """Stamp the latest commit with corresponding review request URL"""
        self.cmd_args = list(args)

        repository_info, self.tool = self.initialize_scm_tool(
            client_name=self.options.repository_type)
        server_url = self.get_server_url(repository_info, self.tool)
        api_client, api_root = self.get_api(server_url)
        self.setup_tool(self.tool, api_root=api_root)

        if not self.tool.can_amend_commit:
            raise NotImplementedError('rbt stamp is not supported with %s.' %
                                      self.tool.name)

        try:
            if self.tool.has_pending_changes():
                raise CommandError('Working directory is not clean.')
        except NotImplementedError:
            pass

        revisions = get_revisions(self.tool, self.cmd_args)
        commit_message = self.tool.get_raw_commit_message(revisions)

        if '\nReviewed at http' in commit_message:
            raise CommandError('This commit is already stamped.')

        if not self.options.rid:
            self.options.rid = guess_existing_review_request_id(
                repository_info,
                self.options.repository_name,
                api_root,
                api_client,
                self.tool,
                revisions,
                guess_summary=False,
                guess_description=False,
                is_fuzzy_match_func=self._ask_review_request_match,
                no_commit_error=self.no_commit_error)

            if not self.options.rid:
                raise CommandError('Could not determine the existing review '
                                   'request URL to stamp with.')

        review_request = get_review_request(self.options.rid, api_root)
        stamp_url = review_request.absolute_url
        commit_message += '\n\nReviewed at %s' % stamp_url

        self.tool.amend_commit(commit_message)
        print('Changes committed to current branch.')
Ejemplo n.º 8
0
    def main(self, request_id):
        """Run the command."""
        repository_info, tool = self.initialize_scm_tool(
            client_name=self.options.repository_type)
        server_url = self.get_server_url(repository_info, tool)
        api_client, api_root = self.get_api(server_url)

        request = get_review_request(request_id, api_root)

        try:
            draft = request.get_draft()
            draft = draft.update(public=True)
        except APIError, e:
            raise CommandError("Error publishing review request (it may "
                               "already be published): %s" % e)
Ejemplo n.º 9
0
    def main(self, request_id):
        """Run the command."""
        repository_info, tool = self.initialize_scm_tool(
            client_name=self.options.repository_type)
        server_url = self.get_server_url(repository_info, tool)
        api_client, api_root = self.get_api(server_url)

        review_request = get_review_request(request_id, api_root,
                                            only_fields='public',
                                            only_links='draft')

        self.setup_tool(tool, api_root)

        update_fields = {
            'public': True,
        }

        if (self.options.trivial_publish and
            tool.capabilities.has_capability('review_requests',
                                             'trivial_publish')):
            update_fields['trivial'] = True

        if self.options.change_description is not None:
            if review_request.public:
                update_fields['changedescription'] = \
                    self.options.change_description

                if (self.options.markdown and
                    tool.capabilities.has_capability('text', 'markdown')):
                    update_fields['changedescription_text_type'] = 'markdown'
                else:
                    update_fields['changedescription_text_type'] = 'plain'
            else:
                logging.error(
                    'The change description field can only be set when '
                    'publishing an update.')

        try:
            draft = review_request.get_draft(only_fields='')
            draft.update(**update_fields)
        except APIError as e:
            raise CommandError('Error publishing review request (it may '
                               'already be published): %s' % e)

        print('Review request #%s is published.' % request_id)
Ejemplo n.º 10
0
    def main(self, *args):
        """Stamp the latest commit with corresponding review request URL"""
        self.cmd_args = list(args)

        repository_info, self.tool = self.initialize_scm_tool(
            client_name=self.options.repository_type)
        server_url = self.get_server_url(repository_info, self.tool)
        api_client, api_root = self.get_api(server_url)
        self.setup_tool(self.tool, api_root=api_root)

        if not self.tool.can_amend_commit:
            raise NotImplementedError('rbt stamp is not supported with %s.'
                                      % self.tool.name)

        try:
            if self.tool.has_pending_changes():
                raise CommandError('Working directory is not clean.')
        except NotImplementedError:
            pass

        revisions = get_revisions(self.tool, self.cmd_args)
        commit_message = self.tool.get_raw_commit_message(revisions)

        if '\nReviewed at http' in commit_message:
            raise CommandError('This commit is already stamped.')

        if not self.options.rid:
            self.options.rid = guess_existing_review_request_id(
                repository_info, self.options.repository_name, api_root,
                api_client, self.tool, revisions,
                guess_summary=False, guess_description=False,
                is_fuzzy_match_func=self._ask_review_request_match,
                no_commit_error=self.no_commit_error)

            if not self.options.rid:
                raise CommandError('Could not determine the existing review '
                                   'request URL to stamp with.')

        review_request = get_review_request(self.options.rid, api_root)
        stamp_url = review_request.absolute_url
        commit_message += '\n\nReviewed at %s' % stamp_url

        self.tool.amend_commit(commit_message)
        print('Changes committed to current branch.')
Ejemplo n.º 11
0
    def main(self, *args):
        """Add the review request URL to a commit message."""
        self.cmd_args = list(args)

        repository_info, self.tool = self.initialize_scm_tool(
            client_name=self.options.repository_type)
        server_url = self.get_server_url(repository_info, self.tool)
        api_client, api_root = self.get_api(server_url)
        self.setup_tool(self.tool, api_root=api_root)

        if not self.tool.can_amend_commit:
            raise NotImplementedError('rbt stamp is not supported with %s.'
                                      % self.tool.name)

        try:
            if self.tool.has_pending_changes():
                raise CommandError('Working directory is not clean.')
        except NotImplementedError:
            pass

        revisions = get_revisions(self.tool, self.cmd_args)

        # Use the ID from the command line options if present.
        if self.options.rid:
            review_request = get_review_request(self.options.rid, api_root)
            review_request_id = self.options.rid
            review_request_url = review_request.absolute_url
        else:
            review_request_id, review_request_url = \
                self. determine_review_request(
                    api_client, api_root, repository_info,
                    self.options.repository_name, revisions)

        if not review_request_url:
            raise CommandError('Could not determine the existing review '
                               'request URL to stamp with.')

        stamp_commit_with_review_url(revisions, review_request_url, self.tool)

        print('Successfully stamped change with the URL:')
        print(review_request_url)
Ejemplo n.º 12
0
    def main(self, *args):
        """Add the review request URL to a commit message."""
        self.cmd_args = list(args)

        repository_info, self.tool = self.initialize_scm_tool(
            client_name=self.options.repository_type)
        server_url = self.get_server_url(repository_info, self.tool)
        api_client, api_root = self.get_api(server_url)
        self.setup_tool(self.tool, api_root=api_root)

        if not self.tool.can_amend_commit:
            raise NotImplementedError('rbt stamp is not supported with %s.' %
                                      self.tool.name)

        try:
            if self.tool.has_pending_changes():
                raise CommandError('Working directory is not clean.')
        except NotImplementedError:
            pass

        revisions = get_revisions(self.tool, self.cmd_args)

        # Use the ID from the command line options if present.
        if self.options.rid:
            review_request = get_review_request(self.options.rid, api_root)
            review_request_id = self.options.rid
            review_request_url = review_request.absolute_url
        else:
            review_request_id, review_request_url = \
                self. determine_review_request(
                    api_client, api_root, repository_info,
                    self.options.repository_name, revisions)

        if not review_request_url:
            raise CommandError('Could not determine the existing review '
                               'request URL to stamp with.')

        stamp_commit_with_review_url(revisions, review_request_url, self.tool)

        print('Successfully stamped change with the URL:')
        print(review_request_url)
Ejemplo n.º 13
0
    def main(self, request_id, path_to_file):
        self.repository_info, self.tool = self.initialize_scm_tool(client_name=self.options.repository_type)
        server_url = self.get_server_url(self.repository_info, self.tool)
        api_client, api_root = self.get_api(server_url)

        request = get_review_request(request_id, api_root)

        try:
            with open(path_to_file, "rb") as f:
                content = f.read()
        except IOError:
            raise CommandError("%s is not a valid file." % path_to_file)

        # Check if the user specified a custom filename, otherwise
        # use the original filename.
        filename = self.options.filename or os.path.basename(path_to_file)

        try:
            request.get_file_attachments().upload_attachment(filename, content, self.options.caption)
        except APIError as e:
            raise CommandError("Error uploading file: %s" % e)

        print("Uploaded %s to review request %s." % (path_to_file, request_id))
Ejemplo n.º 14
0
    def main(self, request_id, path_to_file):
        self.repository_info, self.tool = self.initialize_scm_tool(
            client_name=self.options.repository_type)
        server_url = self.get_server_url(self.repository_info, self.tool)
        api_client, api_root = self.get_api(server_url)

        request = get_review_request(request_id, api_root)

        try:
            f = open(path_to_file, 'r')
            content = f.read()
            f.close()
        except IOError:
            raise CommandError("%s is not a valid file." % path_to_file)

        # Check if the user specified a custom filename, otherwise
        # use the original filename.
        filename = self.options.filename or os.path.basename(path_to_file)

        try:
            request.get_file_attachments() \
                .upload_attachment(filename, content, self.options.caption)
        except APIError, e:
            raise CommandError("Error uploading file: %s" % e)
Ejemplo n.º 15
0
    def post_request(self, repository_info, repository, server_url, api_root,
                     review_request_id=None, changenum=None, diff_content=None,
                     parent_diff_content=None, commit_id=None,
                     base_commit_id=None,
                     submit_as=None, retries=3, base_dir=None):
        """Creates or updates a review request, and uploads a diff.

        On success the review request id and url are returned.
        """
        supports_posting_commit_ids = \
            self.tool.capabilities.has_capability('review_requests',
                                                  'commit_ids')

        if review_request_id:
            review_request = get_review_request(
                review_request_id, api_root,
                only_fields='absolute_url,bugs_closed,id,status',
                only_links='diffs,draft')

            if review_request.status == 'submitted':
                raise CommandError(
                    'Review request %s is marked as %s. In order to update '
                    'it, please reopen the review request and try again.'
                    % (review_request_id, review_request.status))
        else:
            # No review_request_id, so we will create a new review request.
            try:
                request_data = {
                    'repository': repository
                }

                if changenum:
                    request_data['changenum'] = changenum
                elif commit_id and supports_posting_commit_ids:
                    request_data['commit_id'] = commit_id

                if submit_as:
                    request_data['submit_as'] = submit_as

                review_requests = api_root.get_review_requests(
                    only_fields='',
                    only_links='create')
                review_request = review_requests.create(**request_data)
            except APIError as e:
                if e.error_code == 204 and changenum:
                    # The change number is already in use. Get the review
                    # request for that change and update it instead.
                    rid = e.rsp['review_request']['id']
                    review_request = api_root.get_review_request(
                        review_request_id=rid,
                        only_fields='absolute_url,bugs_closed,id,status',
                        only_links='diffs,draft')

                    if not self.options.diff_only:
                        review_request = review_request.update(
                            changenum=changenum)
                else:
                    raise CommandError('Error creating review request: %s' % e)

        if (not repository_info.supports_changesets or
            not self.options.change_only):
            try:
                diff_kwargs = {
                    'parent_diff': parent_diff_content,
                    'base_dir': base_dir,
                }

                if (base_commit_id and
                    self.tool.capabilities.has_capability('diffs',
                                                          'base_commit_ids')):
                    # Both the Review Board server and SCMClient support
                    # base commit IDs, so pass that along when creating
                    # the diff.
                    diff_kwargs['base_commit_id'] = base_commit_id

                review_request.get_diffs(only_fields='').upload_diff(
                    diff_content, **diff_kwargs)
            except APIError as e:
                error_msg = [
                    u'Error uploading diff\n\n',
                ]

                if e.error_code == 101 and e.http_status == 403:
                    error_msg.append(
                        u'You do not have permissions to modify '
                        u'this review request\n')
                elif e.error_code == 219:
                    error_msg.append(
                        u'The generated diff file was empty. This '
                        u'usually means no files were\n'
                        u'modified in this change.\n')
                else:
                    error_msg.append(str(e).decode('utf-8') + u'\n')

                error_msg.append(
                    u'Your review request still exists, but the diff is '
                    u'not attached.\n')

                error_msg.append(u'%s\n' % review_request.absolute_url)

                raise CommandError(u'\n'.join(error_msg))

        try:
            draft = review_request.get_draft(only_fields='commit_id')
        except APIError as e:
            raise CommandError('Error retrieving review request draft: %s' % e)

        # Stamp the commit message with the review request URL before posting
        # the review, so that we can use the stamped commit message when
        # guessing the description. This enables the stamped message to be
        # present on the review if the user has chosen to publish immediately
        # upon posting.
        if self.options.stamp_when_posting:
            if not self.tool.can_amend_commit:
                print('Cannot stamp review URL onto the commit message; '
                      'stamping is not supported with %s.' % self.tool.name)

            else:
                try:
                    stamp_commit_with_review_url(self.revisions,
                                                 review_request.absolute_url,
                                                 self.tool)
                    print('Stamped review URL onto the commit message.')
                except AlreadyStampedError:
                    print('Commit message has already been stamped')
                except Exception as e:
                    logging.debug('Caught exception while stamping the '
                                  'commit message. Proceeding to post '
                                  'without stamping.', exc_info=True)
                    print('Could not stamp review URL onto the commit '
                          'message.')

        # If the user has requested to guess the summary or description,
        # get the commit message and override the summary and description
        # options. The guessing takes place after stamping so that the
        # guessed description matches the commit when rbt exits.
        if not self.options.diff_filename:
            self.check_guess_fields()

        # Update the review request draft fields based on options set
        # by the user, or configuration.
        update_fields = {}

        if self.options.target_groups:
            update_fields['target_groups'] = self.options.target_groups

        if self.options.target_people:
            update_fields['target_people'] = self.options.target_people

        if self.options.depends_on:
            update_fields['depends_on'] = self.options.depends_on

        if self.options.summary:
            update_fields['summary'] = self.options.summary

        if self.options.branch:
            update_fields['branch'] = self.options.branch

        if self.options.bugs_closed:
            # Append to the existing list of bugs.
            self.options.bugs_closed = self.options.bugs_closed.strip(', ')
            bug_set = (set(re.split('[, ]+', self.options.bugs_closed)) |
                       set(review_request.bugs_closed))
            self.options.bugs_closed = ','.join(bug_set)
            update_fields['bugs_closed'] = self.options.bugs_closed

        if self.options.description:
            update_fields['description'] = self.options.description

        if self.options.testing_done:
            update_fields['testing_done'] = self.options.testing_done

        if ((self.options.description or self.options.testing_done) and
            self.options.markdown and
            self.tool.capabilities.has_capability('text', 'markdown')):
            # The user specified that their Description/Testing Done are
            # valid Markdown, so tell the server so it won't escape the text.
            update_fields['text_type'] = 'markdown'

        if self.options.change_description:
            update_fields['changedescription'] = \
                self.options.change_description

        if self.options.publish:
            update_fields['public'] = True

        if supports_posting_commit_ids and commit_id != draft.commit_id:
            update_fields['commit_id'] = commit_id or ''

        if update_fields:
            try:
                draft = draft.update(**update_fields)
            except APIError as e:
                raise CommandError(u'\n'.join([
                    u'Error updating review request draft: %s\n' % e,
                    u'Your review request still exists, but the diff is '
                    u'not attached.\n',
                    u'%s\n' % review_request.absolute_url,
                ]))

        return review_request.id, review_request.absolute_url
Ejemplo n.º 16
0
    def main(self, branch_name=None, *args):
        """Run the command."""
        self.cmd_args = list(args)

        if branch_name:
            self.cmd_args.insert(0, branch_name)

        repository_info, self.tool = self.initialize_scm_tool(
            client_name=self.options.repository_type)
        server_url = self.get_server_url(repository_info, self.tool)
        api_client, api_root = self.get_api(server_url)
        self.setup_tool(self.tool, api_root=api_root)

        # Check if repository info on reviewboard server match local ones.
        repository_info = repository_info.find_server_repository_info(api_root)

        if (not self.tool.can_merge or
            not self.tool.can_push_upstream or
            not self.tool.can_delete_branch):
            raise CommandError('This command does not support %s repositories.'
                               % self.tool.name)

        if self.tool.has_pending_changes():
            raise CommandError('Working directory is not clean.')

        if not self.options.destination_branch:
            raise CommandError('Please specify a destination branch.')

        if self.options.rid:
            is_local = branch_name is not None
            review_request_id = self.options.rid
        else:
            review_request = guess_existing_review_request(
                repository_info,
                self.options.repository_name,
                api_root,
                api_client,
                self.tool,
                get_revisions(self.tool, self.cmd_args),
                guess_summary=False,
                guess_description=False,
                is_fuzzy_match_func=self._ask_review_request_match)

            if not review_request or not review_request.id:
                raise CommandError('Could not determine the existing review '
                                   'request URL to land.')

            review_request_id = review_request.id
            is_local = True

        review_request = get_review_request(review_request_id, api_root)

        if self.options.is_local is not None:
            is_local = self.options.is_local

        if is_local:
            if branch_name is None:
                branch_name = self.tool.get_current_branch()

            if branch_name == self.options.destination_branch:
                raise CommandError('The local branch cannot be merged onto '
                                   'itself. Try a different local branch or '
                                   'destination branch.')
        else:
            branch_name = None

        land_error = self.can_land(review_request)

        if land_error is not None:
            raise CommandError('Cannot land review request %s: %s'
                               % (review_request_id, land_error))

        if self.options.recursive:
            # The dependency graph shows us which review requests depend on
            # which other ones. What we are actually after is the order to land
            # them in, which is the topological sorting order of the converse
            # graph. It just so happens that if we reverse the topological sort
            # of a graph, it is a valid topological sorting of the converse
            # graph, so we don't have to compute the converse graph.
            dependency_graph = review_request.build_dependency_graph()
            dependencies = toposort(dependency_graph)[1:]

            if dependencies:
                print('Recursively landing dependencies of review request %s.'
                      % review_request_id)

                for dependency in dependencies:
                    land_error = self.can_land(dependency)

                    if land_error is not None:
                        raise CommandError(
                            'Aborting recursive land of review request %s.\n'
                            'Review request %s cannot be landed: %s'
                            % (review_request_id, dependency.id, land_error))

                for dependency in reversed(dependencies):
                    self.land(self.options.destination_branch,
                              dependency,
                              None,
                              self.options.squash,
                              self.options.edit,
                              self.options.delete_branch,
                              self.options.dry_run)

        self.land(self.options.destination_branch,
                  review_request,
                  branch_name,
                  self.options.squash,
                  self.options.edit,
                  self.options.delete_branch,
                  self.options.dry_run)

        if self.options.push:
            print('Pushing branch "%s" upstream'
                  % self.options.destination_branch)

            if not self.options.dry_run:
                try:
                    self.tool.push_upstream(self.options.destination_branch)
                except PushError as e:
                    raise CommandError(six.text_type(e))
Ejemplo n.º 17
0
    def post_request(self, repository_info, repository, server_url, api_root,
                     review_request_id=None, changenum=None, diff_content=None,
                     parent_diff_content=None, commit_id=None,
                     base_commit_id=None,
                     submit_as=None, retries=3, base_dir=None):
        """Creates or updates a review request, and uploads a diff.

        On success the review request id and url are returned.
        """
        supports_posting_commit_ids = \
            self.tool.capabilities.has_capability('review_requests',
                                                  'commit_ids')

        if review_request_id:
            review_request = get_review_request(
                review_request_id, api_root,
                only_fields='absolute_url,bugs_closed,id,status',
                only_links='diffs,draft')

            if review_request.status == 'submitted':
                raise CommandError(
                    'Review request %s is marked as %s. In order to update '
                    'it, please reopen the review request and try again.'
                    % (review_request_id, review_request.status))
        else:
            # No review_request_id, so we will create a new review request.
            try:
                request_data = {
                    'repository': repository
                }

                if changenum:
                    request_data['changenum'] = changenum
                elif commit_id and supports_posting_commit_ids:
                    request_data['commit_id'] = commit_id

                if submit_as:
                    request_data['submit_as'] = submit_as

                review_requests = api_root.get_review_requests(
                    only_fields='',
                    only_links='create')
                review_request = review_requests.create(**request_data)
            except APIError as e:
                if e.error_code == 204 and changenum:  # Change number in use.
                    rid = e.rsp['review_request']['id']
                    review_request = api_root.get_review_request(
                        review_request_id=rid,
                        only_fields='absolute_url,bugs_closed,id,status',
                        only_links='diffs,draft')

                    if not self.options.diff_only:
                        review_request = review_request.update(
                            changenum=changenum)
                else:
                    raise CommandError('Error creating review request: %s' % e)

        if (not repository_info.supports_changesets or
            not self.options.change_only):
            try:
                diff_kwargs = {
                    'parent_diff': parent_diff_content,
                    'base_dir': base_dir,
                }

                if (base_commit_id and
                    self.tool.capabilities.has_capability('diffs',
                                                          'base_commit_ids')):
                    # Both the Review Board server and SCMClient support
                    # base commit IDs, so pass that along when creating
                    # the diff.
                    diff_kwargs['base_commit_id'] = base_commit_id

                review_request.get_diffs(only_fields='').upload_diff(
                    diff_content, **diff_kwargs)
            except APIError as e:
                error_msg = [
                    u'Error uploading diff\n\n',
                ]

                if e.error_code == 101 and e.http_status == 403:
                    error_msg.append(
                        u'You do not have permissions to modify '
                        u'this review request\n')
                elif e.error_code == 219:
                    error_msg.append(
                        u'The generated diff file was empty. This '
                        u'usually means no files were\n'
                        u'modified in this change.\n')
                else:
                    error_msg.append(str(e).decode('utf-8') + u'\n')

                error_msg.append(
                    u'Your review request still exists, but the diff is '
                    u'not attached.\n')

                error_msg.append(u'%s\n' % review_request.absolute_url)

                raise CommandError(u'\n'.join(error_msg))

        try:
            draft = review_request.get_draft(only_fields='commit_id')
        except APIError as e:
            raise CommandError('Error retrieving review request draft: %s' % e)

        # Update the review request draft fields based on options set
        # by the user, or configuration.
        update_fields = {}

        if self.options.target_groups:
            update_fields['target_groups'] = self.options.target_groups

        if self.options.target_people:
            update_fields['target_people'] = self.options.target_people

        if self.options.depends_on:
            update_fields['depends_on'] = self.options.depends_on

        if self.options.summary:
            update_fields['summary'] = self.options.summary

        if self.options.branch:
            update_fields['branch'] = self.options.branch

        if self.options.bugs_closed:
            # Append to the existing list of bugs.
            self.options.bugs_closed = self.options.bugs_closed.strip(', ')
            bug_set = (set(re.split('[, ]+', self.options.bugs_closed)) |
                       set(review_request.bugs_closed))
            self.options.bugs_closed = ','.join(bug_set)
            update_fields['bugs_closed'] = self.options.bugs_closed

        if self.options.description:
            update_fields['description'] = self.options.description

        if self.options.testing_done:
            update_fields['testing_done'] = self.options.testing_done

        if ((self.options.description or self.options.testing_done) and
            self.options.markdown and
            self.tool.capabilities.has_capability('text', 'markdown')):
            # The user specified that their Description/Testing Done are
            # valid Markdown, so tell the server so it won't escape the text.
            update_fields['text_type'] = 'markdown'

        if self.options.change_description:
            update_fields['changedescription'] = \
                self.options.change_description

        if self.options.publish:
            update_fields['public'] = True

        if supports_posting_commit_ids and commit_id != draft.commit_id:
            update_fields['commit_id'] = commit_id or ''

        if update_fields:
            try:
                draft = draft.update(**update_fields)
            except APIError as e:
                raise CommandError(u'\n'.join([
                    u'Error updating review request draft: %s\n' % e,
                    u'Your review request still exists, but the diff is '
                    u'not attached.\n',
                    u'%s\n' % review_request.absolute_url,
                ]))

        return review_request.id, review_request.absolute_url
Ejemplo n.º 18
0
    def post_request(self,
                     repository_info,
                     repository,
                     server_url,
                     api_root,
                     review_request_id=None,
                     changenum=None,
                     diff_content=None,
                     parent_diff_content=None,
                     commit_id=None,
                     base_commit_id=None,
                     submit_as=None,
                     retries=3,
                     base_dir=None):
        """Creates or updates a review request, and uploads a diff.

        On success the review request id and url are returned.
        """
        supports_posting_commit_ids = \
            self.tool.capabilities.has_capability('review_requests',
                                                  'commit_ids')

        if review_request_id:
            review_request = get_review_request(
                review_request_id,
                api_root,
                only_fields='absolute_url,bugs_closed,id,status,public',
                only_links='diffs,draft')

            if review_request.status == 'submitted':
                raise CommandError(
                    'Review request %s is marked as %s. In order to update '
                    'it, please reopen the review request and try again.' %
                    (review_request_id, review_request.status))
        else:
            # No review_request_id, so we will create a new review request.
            try:
                # Until we are Python 2.7+ only, the keys in request_data have
                # to be bytes. See bug 3753 for details.
                request_data = {b'repository': repository}

                if changenum:
                    request_data[b'changenum'] = changenum
                elif commit_id and supports_posting_commit_ids:
                    request_data[b'commit_id'] = commit_id

                if submit_as:
                    request_data[b'submit_as'] = submit_as

                if self.tool.can_bookmark:
                    bookmark = self.tool.get_current_bookmark()
                    request_data[b'extra_data__local_bookmark'] = bookmark
                elif self.tool.can_branch:
                    branch = self.tool.get_current_branch()
                    request_data[b'extra_data__local_branch'] = branch

                review_requests = api_root.get_review_requests(
                    only_fields='', only_links='create')
                review_request = review_requests.create(**request_data)
            except APIError as e:
                if e.error_code == 204 and changenum:
                    # The change number is already in use. Get the review
                    # request for that change and update it instead.
                    rid = e.rsp['review_request']['id']
                    review_request = api_root.get_review_request(
                        review_request_id=rid,
                        only_fields='absolute_url,bugs_closed,id,status',
                        only_links='diffs,draft')
                else:
                    raise CommandError('Error creating review request: %s' % e)

        if (not repository_info.supports_changesets
                or not self.options.change_only):
            try:
                diff_kwargs = {
                    'parent_diff': parent_diff_content,
                    'base_dir': base_dir,
                }

                if (base_commit_id and self.tool.capabilities.has_capability(
                        'diffs', 'base_commit_ids')):
                    # Both the Review Board server and SCMClient support
                    # base commit IDs, so pass that along when creating
                    # the diff.
                    diff_kwargs['base_commit_id'] = base_commit_id

                review_request.get_diffs(only_fields='').upload_diff(
                    diff_content, **diff_kwargs)
            except APIError as e:
                error_msg = [
                    u'Error uploading diff\n\n',
                ]

                if e.error_code == 101 and e.http_status == 403:
                    error_msg.append(u'You do not have permissions to modify '
                                     u'this review request\n')
                elif e.error_code == 219:
                    error_msg.append(
                        u'The generated diff file was empty. This '
                        u'usually means no files were\n'
                        u'modified in this change.\n')
                else:
                    error_msg.append(str(e).decode('utf-8') + u'\n')

                error_msg.append(
                    u'Your review request still exists, but the diff is '
                    u'not attached.\n')

                error_msg.append(u'%s\n' % review_request.absolute_url)

                raise CommandError(u'\n'.join(error_msg))

        try:
            draft = review_request.get_draft(only_fields='commit_id')
        except APIError as e:
            raise CommandError('Error retrieving review request draft: %s' % e)

        # Stamp the commit message with the review request URL before posting
        # the review, so that we can use the stamped commit message when
        # guessing the description. This enables the stamped message to be
        # present on the review if the user has chosen to publish immediately
        # upon posting.
        if self.options.stamp_when_posting:
            if not self.tool.can_amend_commit:
                print('Cannot stamp review URL onto the commit message; '
                      'stamping is not supported with %s.' % self.tool.name)

            else:
                try:
                    stamp_commit_with_review_url(self.revisions,
                                                 review_request.absolute_url,
                                                 self.tool)
                    print('Stamped review URL onto the commit message.')
                except AlreadyStampedError:
                    print('Commit message has already been stamped')
                except Exception as e:
                    logging.debug(
                        'Caught exception while stamping the '
                        'commit message. Proceeding to post '
                        'without stamping.',
                        exc_info=True)
                    print('Could not stamp review URL onto the commit '
                          'message.')

        # If the user has requested to guess the summary or description,
        # get the commit message and override the summary and description
        # options. The guessing takes place after stamping so that the
        # guessed description matches the commit when rbt exits.
        if not self.options.diff_filename:
            self.check_guess_fields()

        # Update the review request draft fields based on options set
        # by the user, or configuration.
        update_fields = {}

        if self.options.target_groups:
            update_fields['target_groups'] = self.options.target_groups

        if self.options.target_people:
            update_fields['target_people'] = self.options.target_people

        if self.options.depends_on:
            update_fields['depends_on'] = self.options.depends_on

        if self.options.summary:
            update_fields['summary'] = self.options.summary

        if self.options.branch:
            update_fields['branch'] = self.options.branch

        if self.options.bugs_closed:
            # Append to the existing list of bugs.
            self.options.bugs_closed = self.options.bugs_closed.strip(', ')
            bug_set = (set(re.split('[, ]+', self.options.bugs_closed))
                       | set(review_request.bugs_closed))
            self.options.bugs_closed = ','.join(bug_set)
            update_fields['bugs_closed'] = self.options.bugs_closed

        if self.options.description:
            update_fields['description'] = self.options.description

        if self.options.testing_done:
            update_fields['testing_done'] = self.options.testing_done

        if ((self.options.description or self.options.testing_done)
                and self.options.markdown
                and self.tool.capabilities.has_capability('text', 'markdown')):
            # The user specified that their Description/Testing Done are
            # valid Markdown, so tell the server so it won't escape the text.
            update_fields['text_type'] = 'markdown'

        if self.options.publish:
            update_fields['public'] = True

            if (self.options.trivial_publish
                    and self.tool.capabilities.has_capability(
                        'review_requests', 'trivial_publish')):
                update_fields['trivial'] = True

        if self.options.change_description is not None:
            if review_request.public:
                update_fields['changedescription'] = \
                    self.options.change_description

                if (self.options.markdown
                        and self.tool.capabilities.has_capability(
                            'text', 'markdown')):
                    update_fields['changedescription_text_type'] = 'markdown'
                else:
                    update_fields['changedescription_text_type'] = 'plain'
            else:
                logging.error(
                    'The change description field can only be set when '
                    'publishing an update. Use --description instead.')

        if supports_posting_commit_ids and commit_id != draft.commit_id:
            update_fields['commit_id'] = commit_id or ''

        if update_fields:
            try:
                draft = draft.update(**update_fields)
            except APIError as e:
                raise CommandError(
                    'Error updating review request draft: %s\n\n'
                    'Your review request still exists, but the diff is not '
                    'attached.\n\n'
                    '%s\n' % (e, review_request.absolute_url))

        return review_request.id, review_request.absolute_url
Ejemplo n.º 19
0
    def post_request(self,
                     repository_info,
                     repository,
                     server_url,
                     api_root,
                     review_request_id=None,
                     changenum=None,
                     diff_content=None,
                     parent_diff_content=None,
                     commit_id=None,
                     base_commit_id=None,
                     submit_as=None,
                     retries=3,
                     base_dir=None):
        """Creates or updates a review request, and uploads a diff.

        On success the review request id and url are returned.
        """
        supports_posting_commit_ids = \
            self.tool.capabilities.has_capability('review_requests',
                                                  'commit_ids')

        if review_request_id:
            review_request = get_review_request(review_request_id, api_root)

            if review_request.status == 'submitted':
                raise CommandError(
                    'Review request %s is marked as %s. In order to update '
                    'it, please reopen the review request and try again.' %
                    (review_request_id, review_request.status))
        else:
            # No review_request_id, so we will create a new review request.
            try:
                request_data = {'repository': repository}

                if changenum:
                    request_data['changenum'] = changenum
                elif commit_id and supports_posting_commit_ids:
                    request_data['commit_id'] = commit_id

                if submit_as:
                    request_data['submit_as'] = submit_as

                review_request = api_root.get_review_requests().create(
                    **request_data)
            except APIError as e:
                if e.error_code == 204 and changenum:  # Change number in use.
                    rid = e.rsp['review_request']['id']
                    review_request = api_root.get_review_request(
                        review_request_id=rid)

                    if not self.options.diff_only:
                        review_request = review_request.update(
                            changenum=changenum)
                else:
                    raise CommandError('Error creating review request: %s' % e)

        if (not repository_info.supports_changesets
                or not self.options.change_only):
            try:
                diff_kwargs = {
                    'parent_diff': parent_diff_content,
                    'base_dir': base_dir,
                }

                if (base_commit_id and self.tool.capabilities.has_capability(
                        'diffs', 'base_commit_ids')):
                    # Both the Review Board server and SCMClient support
                    # base commit IDs, so pass that along when creating
                    # the diff.
                    diff_kwargs['base_commit_id'] = base_commit_id

                review_request.get_diffs().upload_diff(diff_content,
                                                       **diff_kwargs)
            except APIError as e:
                error_msg = [
                    u'Error uploading diff\n\n',
                ]

                if e.error_code == 101 and e.http_status == 403:
                    error_msg.append(u'You do not have permissions to modify '
                                     u'this review request\n')
                elif e.error_code == 219:
                    error_msg.append(
                        u'The generated diff file was empty. This '
                        u'usually means no files were\n'
                        u'modified in this change.\n')
                else:
                    error_msg.append(str(e).decode('utf-8') + u'\n')

                error_msg.append(
                    u'Your review request still exists, but the diff is '
                    u'not attached.\n')

                error_msg.append(u'%s\n' % review_request.absolute_url)

                raise CommandError(u'\n'.join(error_msg))

        try:
            draft = review_request.get_draft()
        except APIError as e:
            raise CommandError('Error retrieving review request draft: %s' % e)

        # Update the review request draft fields based on options set
        # by the user, or configuration.
        update_fields = {}

        if self.options.target_groups:
            update_fields['target_groups'] = self.options.target_groups

        if self.options.target_people:
            update_fields['target_people'] = self.options.target_people

        if self.options.depends_on:
            update_fields['depends_on'] = self.options.depends_on

        if self.options.summary:
            update_fields['summary'] = self.options.summary

        if self.options.branch:
            update_fields['branch'] = self.options.branch

        if self.options.bugs_closed:
            # Append to the existing list of bugs.
            self.options.bugs_closed = self.options.bugs_closed.strip(', ')
            bug_set = (set(re.split('[, ]+', self.options.bugs_closed))
                       | set(review_request.bugs_closed))
            self.options.bugs_closed = ','.join(bug_set)
            update_fields['bugs_closed'] = self.options.bugs_closed

        if self.options.description:
            update_fields['description'] = self.options.description

        if self.options.testing_done:
            update_fields['testing_done'] = self.options.testing_done

        if ((self.options.description or self.options.testing_done)
                and self.options.markdown
                and self.tool.capabilities.has_capability('text', 'markdown')):
            # The user specified that their Description/Testing Done are
            # valid Markdown, so tell the server so it won't escape the text.
            update_fields['text_type'] = 'markdown'

        if self.options.change_description:
            update_fields['changedescription'] = \
                self.options.change_description

        if self.options.publish:
            update_fields['public'] = True

        if supports_posting_commit_ids and commit_id != draft.commit_id:
            update_fields['commit_id'] = commit_id or ''

        if update_fields:
            try:
                draft = draft.update(**update_fields)
            except APIError as e:
                raise CommandError(u'\n'.join([
                    u'Error updating review request draft: %s\n' % e,
                    u'Your review request still exists, but the diff is '
                    u'not attached.\n',
                    u'%s\n' % review_request.absolute_url,
                ]))

        return review_request.id, review_request.absolute_url
Ejemplo n.º 20
0
    def main(self, branch_name=None, *args):
        """Run the command."""
        self.cmd_args = list(args)

        if branch_name:
            self.cmd_args.insert(0, branch_name)

        repository_info, self.tool = self.initialize_scm_tool(
            client_name=self.options.repository_type)
        server_url = self.get_server_url(repository_info, self.tool)
        api_client, api_root = self.get_api(server_url)
        self.setup_tool(self.tool, api_root=api_root)

        # Check if repository info on reviewboard server match local ones.
        repository_info = repository_info.find_server_repository_info(api_root)

        if (not self.tool.can_merge or
            not self.tool.can_push_upstream or
            not self.tool.can_delete_branch):
            raise CommandError('This command does not support %s repositories.'
                               % self.tool.name)

        if self.tool.has_pending_changes():
            raise CommandError('Working directory is not clean.')

        if not self.options.destination_branch:
            raise CommandError('Please specify a destination branch.')

        if self.options.rid:
            is_local = branch_name is not None
            review_request_id = self.options.rid
        else:
            review_request = guess_existing_review_request(
                repository_info,
                self.options.repository_name,
                api_root,
                api_client,
                self.tool,
                get_revisions(self.tool, self.cmd_args),
                guess_summary=False,
                guess_description=False,
                is_fuzzy_match_func=self._ask_review_request_match)

            if not review_request or not review_request.id:
                raise CommandError('Could not determine the existing review '
                                   'request URL to land.')

            review_request_id = review_request.id
            is_local = True

        review_request = get_review_request(review_request_id, api_root)

        if self.options.is_local is not None:
            is_local = self.options.is_local

        if is_local:
            if branch_name is None:
                branch_name = self.tool.get_current_branch()

            if branch_name == self.options.destination_branch:
                raise CommandError('The local branch cannot be merged onto '
                                   'itself. Try a different local branch or '
                                   'destination branch.')
        else:
            branch_name = None

        land_error = self.can_land(review_request)

        if land_error is not None:
            raise CommandError('Cannot land review request %s: %s'
                               % (review_request_id, land_error))

        if self.options.recursive:
            # The dependency graph shows us which review requests depend on
            # which other ones. What we are actually after is the order to land
            # them in, which is the topological sorting order of the converse
            # graph. It just so happens that if we reverse the topological sort
            # of a graph, it is a valid topological sorting of the converse
            # graph, so we don't have to compute the converse graph.
            dependency_graph = review_request.build_dependency_graph()
            dependencies = toposort(dependency_graph)[1:]

            if dependencies:
                print('Recursively landing dependencies of review request %s.'
                      % review_request_id)

                for dependency in dependencies:
                    land_error = self.can_land(dependency)

                    if land_error is not None:
                        raise CommandError(
                            'Aborting recursive land of review request %s.\n'
                            'Review request %s cannot be landed: %s'
                            % (review_request_id, dependency.id, land_error))

                for dependency in reversed(dependencies):
                    self.land(self.options.destination_branch,
                              dependency,
                              None,
                              self.options.squash,
                              self.options.edit,
                              self.options.delete_branch,
                              self.options.dry_run)

        self.land(self.options.destination_branch,
                  review_request,
                  branch_name,
                  self.options.squash,
                  self.options.edit,
                  self.options.delete_branch,
                  self.options.dry_run)

        if self.options.push:
            print('Pushing branch "%s" upstream'
                  % self.options.destination_branch)

            if not self.options.dry_run:
                try:
                    self.tool.push_upstream(self.options.destination_branch)
                except PushError as e:
                    raise CommandError(six.text_type(e))
Ejemplo n.º 21
0
Archivo: land.py Proyecto: drbr/rbtools
    def main(self, branch_name=None, *args):
        """Run the command."""
        self.cmd_args = list(args)

        if branch_name:
            self.cmd_args.insert(0, branch_name)

        repository_info, self.tool = self.initialize_scm_tool(
            client_name=self.options.repository_type)
        server_url = self.get_server_url(repository_info, self.tool)
        api_client, api_root = self.get_api(server_url)
        self.setup_tool(self.tool, api_root=api_root)

        dry_run = self.options.dry_run

        # Check if repository info on reviewboard server match local ones.
        repository_info = repository_info.find_server_repository_info(api_root)

        if (not self.tool.can_merge or
            not self.tool.can_push_upstream or
            not self.tool.can_delete_branch):
            raise CommandError(
                "This command does not support %s repositories."
                % self.tool.name)

        if self.tool.has_pending_changes():
            raise CommandError('Working directory is not clean.')

        if self.options.rid:
            request_id = self.options.rid
            is_local = branch_name is not None
        else:
            request = guess_existing_review_request(
                repository_info,
                self.options.repository_name,
                api_root,
                api_client,
                self.tool,
                get_revisions(self.tool, self.cmd_args),
                guess_summary=False,
                guess_description=False,
                is_fuzzy_match_func=self._ask_review_request_match)

            if not request or not request.id:
                raise CommandError('Could not determine the existing review '
                                   'request URL to land.')

            request_id = request.id
            is_local = True

        if self.options.is_local is not None:
            is_local = self.options.is_local

        destination_branch = self.options.destination_branch

        if not destination_branch:
            raise CommandError('Please specify a destination branch.')

        if is_local:
            if branch_name is None:
                branch_name = self.tool.get_current_branch()

            if branch_name == destination_branch:
                raise CommandError('The local branch cannot be merged onto '
                                   'itself. Try a different local branch or '
                                   'destination branch.')

        review_request = get_review_request(request_id, api_root)

        try:
            is_rr_approved = review_request.approved
            approval_failure = review_request.approval_failure
        except AttributeError:
            # The Review Board server is an old version (pre-2.0) that
            # doesn't support the `approved` field. Determining it manually.
            if review_request.ship_it_count == 0:
                is_rr_approved = False
                approval_failure = \
                    'The review request has not been marked "Ship It!"'
            else:
                is_rr_approved = True
        finally:
            if not is_rr_approved:
                raise CommandError(approval_failure)

        if is_local:
            review_commit_message = extract_commit_message(review_request)
            author = review_request.get_submitter()

            if self.options.squash:
                print('Squashing branch "%s" into "%s"'
                      % (branch_name, destination_branch))
            else:
                print('Merging branch "%s" into "%s"'
                      % (branch_name, destination_branch))

            if not dry_run:
                try:
                    self.tool.merge(
                        branch_name,
                        destination_branch,
                        review_commit_message,
                        author,
                        self.options.squash,
                        self.options.edit)
                except MergeError as e:
                    raise CommandError(str(e))

            if self.options.delete_branch:
                print('Deleting merged branch "%s"' % branch_name)

                if not dry_run:
                    self.tool.delete_branch(branch_name, merged_only=False)
        else:
            print('Applying patch from review request %s' % request_id)

            if not dry_run:
                self.patch(request_id)

        if self.options.push:
            print('Pushing branch "%s" upstream' % destination_branch)

            if not dry_run:
                try:
                    self.tool.push_upstream(destination_branch)
                except PushError as e:
                    raise CommandError(str(e))

        print('Review request %s has landed on "%s".' %
              (request_id, destination_branch))
Ejemplo n.º 22
0
    def main(self, branch_name=None, *args):
        """Run the command."""
        self.cmd_args = list(args)

        if branch_name:
            self.cmd_args.insert(0, branch_name)

        repository_info, self.tool = self.initialize_scm_tool(
            client_name=self.options.repository_type)
        server_url = self.get_server_url(repository_info, self.tool)
        api_client, api_root = self.get_api(server_url)
        self.setup_tool(self.tool, api_root=api_root)

        dry_run = self.options.dry_run

        # Check if repository info on reviewboard server match local ones.
        repository_info = repository_info.find_server_repository_info(api_root)

        if (not self.tool.can_merge or not self.tool.can_push_upstream
                or not self.tool.can_delete_branch):
            raise CommandError(
                "This command does not support %s repositories." %
                self.tool.name)

        if self.tool.has_pending_changes():
            raise CommandError('Working directory is not clean.')

        if self.options.rid:
            request_id = self.options.rid
            is_local = branch_name is not None
        else:
            request = guess_existing_review_request(
                repository_info,
                self.options.repository_name,
                api_root,
                api_client,
                self.tool,
                get_revisions(self.tool, self.cmd_args),
                guess_summary=False,
                guess_description=False,
                is_fuzzy_match_func=self._ask_review_request_match)

            if not request or not request.id:
                raise CommandError('Could not determine the existing review '
                                   'request URL to land.')

            request_id = request.id
            is_local = True

        if self.options.is_local is not None:
            is_local = self.options.is_local

        destination_branch = self.options.destination_branch

        if not destination_branch:
            raise CommandError('Please specify a destination branch.')

        if is_local:
            if branch_name is None:
                branch_name = self.tool.get_current_branch()

            if branch_name == destination_branch:
                raise CommandError('The local branch cannot be merged onto '
                                   'itself. Try a different local branch or '
                                   'destination branch.')

        review_request = get_review_request(request_id, api_root)

        try:
            is_rr_approved = review_request.approved
            approval_failure = review_request.approval_failure
        except AttributeError:
            # The Review Board server is an old version (pre-2.0) that
            # doesn't support the `approved` field. Determining it manually.
            if review_request.ship_it_count == 0:
                is_rr_approved = False
                approval_failure = \
                    'The review request has not been marked "Ship It!"'
            else:
                is_rr_approved = True
        finally:
            if not is_rr_approved:
                raise CommandError(approval_failure)

        if is_local:
            review_commit_message = extract_commit_message(review_request)
            author = review_request.get_submitter()

            if self.options.squash:
                print('Squashing branch "%s" into "%s"' %
                      (branch_name, destination_branch))
            else:
                print('Merging branch "%s" into "%s"' %
                      (branch_name, destination_branch))

            if not dry_run:
                try:
                    self.tool.merge(branch_name, destination_branch,
                                    review_commit_message, author,
                                    self.options.squash, self.options.edit)
                except MergeError as e:
                    raise CommandError(str(e))

            if self.options.delete_branch:
                print('Deleting merged branch "%s"' % branch_name)

                if not dry_run:
                    self.tool.delete_branch(branch_name, merged_only=False)
        else:
            print('Applying patch from review request %s' % request_id)

            if not dry_run:
                self.patch(request_id)

        if self.options.push:
            print('Pushing branch "%s" upstream' % destination_branch)

            if not dry_run:
                try:
                    self.tool.push_upstream(destination_branch)
                except PushError as e:
                    raise CommandError(str(e))

        print('Review request %s has landed on "%s".' %
              (request_id, destination_branch))