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)
        self.setup_tool(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)

        # Get the patch, the used patch ID and base dir for the diff
        diff_body, diff_revision, base_dir = self.get_patch(
            request_id, api_root, self.options.diff_revision)

        if self.options.patch_stdout:
            print(diff_body)
        else:
            try:
                if tool.has_pending_changes():
                    message = 'Working directory is not clean.'

                    if not self.options.commit:
                        print('Warning: %s' % message)
                    else:
                        raise CommandError(message)
            except NotImplementedError:
                pass

            tmp_patch_file = make_tempfile(diff_body)
            success = self.apply_patch(repository_info, tool, request_id,
                                       diff_revision, tmp_patch_file, base_dir)

            if success and (self.options.commit
                            or self.options.commit_no_edit):
                try:
                    review_request = api_root.get_review_request(
                        review_request_id=request_id, force_text_type='plain')
                except APIError as e:
                    raise CommandError('Error getting review request %s: %s' %
                                       (request_id, e))

                message = extract_commit_message(review_request)
                author = review_request.get_submitter()

                try:
                    tool.create_commit(message, author,
                                       not self.options.commit_no_edit)
                    print('Changes committed to current branch.')
                except NotImplementedError:
                    raise CommandError('--commit is not supported with %s' %
                                       tool.name)
Ejemplo n.º 2
0
    def land(self,
             destination_branch,
             review_request,
             source_branch=None,
             squash=False,
             edit=False,
             delete_branch=True,
             dry_run=False):
        """Land an individual review request."""
        if source_branch:
            review_commit_message = extract_commit_message(review_request)
            author = review_request.get_submitter()

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

            if not dry_run:
                try:
                    self.tool.merge(source_branch, destination_branch,
                                    review_commit_message, author, squash,
                                    edit)
                except MergeError as e:
                    raise CommandError(six.text_type(e))

            if delete_branch:
                print('Deleting merged branch "%s".' % source_branch)

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

            if not dry_run:
                self.patch(review_request.id)

        print('Review request %s has landed on "%s".' %
              (review_request.id, self.options.destination_branch))
Ejemplo n.º 3
0
    def land(self, destination_branch, review_request, source_branch=None,
             squash=False, edit=False, delete_branch=True, dry_run=False):
        """Land an individual review request."""
        if source_branch:
            review_commit_message = extract_commit_message(review_request)
            author = review_request.get_submitter()

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

            if not dry_run:
                try:
                    self.tool.merge(source_branch,
                                    destination_branch,
                                    review_commit_message,
                                    author,
                                    squash,
                                    edit)
                except MergeError as e:
                    raise CommandError(six.text_type(e))

            if delete_branch:
                print('Deleting merged branch "%s".' % source_branch)

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

            if not dry_run:
                self.patch(review_request.id)

        print('Review request %s has landed on "%s".' %
              (review_request.id, self.options.destination_branch))
Ejemplo n.º 4
0
    def main(self, request_id):
        """Run the command."""
        repository_info, tool = self.initialize_scm_tool(
            client_name=self.options.repository_type)

        if self.options.revert_patch and not tool.supports_patch_revert:
            raise CommandError('The %s backend does not support reverting '
                               'patches.' % tool.name)

        server_url = self.get_server_url(repository_info, tool)
        api_client, api_root = self.get_api(server_url)
        self.setup_tool(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)

        # Get the patch, the used patch ID and base dir for the diff
        diff_body, diff_revision, base_dir = self.get_patch(
            request_id,
            api_root,
            self.options.diff_revision)

        if self.options.patch_stdout:
            print(diff_body)
        else:
            try:
                if tool.has_pending_changes():
                    message = 'Working directory is not clean.'

                    if not self.options.commit:
                        print('Warning: %s' % message)
                    else:
                        raise CommandError(message)
            except NotImplementedError:
                pass

            tmp_patch_file = make_tempfile(diff_body)

            success = self.apply_patch(repository_info, tool, request_id,
                                       diff_revision, tmp_patch_file, base_dir,
                                       revert=self.options.revert_patch)

            if not success:
                raise CommandError('Could not apply patch')

            if self.options.commit or self.options.commit_no_edit:
                try:
                    review_request = api_root.get_review_request(
                        review_request_id=request_id,
                        force_text_type='plain')
                except APIError as e:
                    raise CommandError('Error getting review request %s: %s'
                                       % (request_id, e))

                message = extract_commit_message(review_request)
                author = review_request.get_submitter()

                try:
                    tool.create_commit(message, author,
                                       not self.options.commit_no_edit)
                    print('Changes committed to current branch.')
                except NotImplementedError:
                    raise CommandError('--commit is not supported with %s'
                                       % tool.name)
Ejemplo n.º 5
0
    def land(self,
             destination_branch,
             review_request,
             source_branch=None,
             squash=False,
             edit=False,
             delete_branch=True,
             dry_run=False):
        """Land an individual review request.

        Args:
            destination_branch (unicode):
                The destination branch that the change will be committed or
                merged to.

            review_request (rbtools.api.resource.ReviewRequestResource):
                The review request containing the change to land.

            source_branch (unicode, optional):
                The source branch to land, if landing from a local branch.

            squash (bool, optional):
                Whether to squash the changes on the branch, for repositories
                that support it.

            edit (bool, optional):
                Whether to edit the commit message before landing.

            delete_branch (bool, optional):
                Whether to delete/close the branch, if landing from a local
                branch.

            dry_run (bool, optional):
                Whether to simulate landing without actually changing the
                repository.
        """
        if source_branch:
            review_commit_message = extract_commit_message(review_request)
            author = review_request.get_submitter()

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

            if not dry_run:
                try:
                    self.tool.merge(target=source_branch,
                                    destination=destination_branch,
                                    message=review_commit_message,
                                    author=author,
                                    squash=squash,
                                    run_editor=edit,
                                    close_branch=delete_branch)
                except MergeError as e:
                    raise CommandError(six.text_type(e))
        else:
            print('Applying patch from review request %s.' % review_request.id)

            if not dry_run:
                self.patch(review_request.id)

        print('Review request %s has landed on "%s".' %
              (review_request.id, self.options.destination_branch))
Ejemplo n.º 6
0
    def main(self, review_request_id):
        """Run the command."""
        repository_info, tool = self.initialize_scm_tool(
            client_name=self.options.repository_type)

        if self.options.patch_stdout and self.options.server:
            server_url = self.options.server
        else:
            server_url = self.get_server_url(repository_info, tool)
            if self.options.revert_patch and not tool.supports_patch_revert:
                raise CommandError('The %s backend does not support reverting '
                                   'patches.' % tool.name)

        api_client, api_root = self.get_api(server_url)

        if not self.options.patch_stdout:
            self.setup_tool(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)

        # Get the patch, the used patch ID and base dir for the diff
        patch_data = self.get_patch(tool, api_root, review_request_id,
                                    self.options.diff_revision,
                                    self.options.commit_id)

        diff_body = patch_data['diff']
        diff_revision = patch_data['revision']
        base_dir = patch_data['base_dir']

        if self.options.patch_stdout:
            if isinstance(diff_body, bytes):
                print(diff_body.decode('utf-8'))
            else:
                print(diff_body)
        else:
            try:
                if tool.has_pending_changes():
                    message = 'Working directory is not clean.'

                    if not self.options.commit:
                        print('Warning: %s' % message)
                    else:
                        raise CommandError(message)
            except NotImplementedError:
                pass

            tmp_patch_file = make_tempfile(diff_body)

            success = self.apply_patch(repository_info,
                                       tool,
                                       review_request_id,
                                       diff_revision,
                                       tmp_patch_file,
                                       base_dir,
                                       revert=self.options.revert_patch)

            if not success:
                raise CommandError('Could not apply patch')

            if self.options.commit or self.options.commit_no_edit:
                if patch_data['commit_meta'] is not None:
                    # We are patching a commit so we already have the metadata
                    # required without making additional HTTP requests.
                    meta = patch_data['commit_meta']
                    message = meta['message']

                    # Fun fact: object does not have a __dict__ so you cannot
                    # call setattr() on them. We need this ability so we are
                    # creating a type that does.
                    author = type('Author', (object, ), {})()
                    author.fullname = meta['author_name']
                    author.email = meta['author_email']

                else:
                    try:
                        review_request = api_root.get_review_request(
                            review_request_id=review_request_id,
                            force_text_type='plain')
                    except APIError as e:
                        raise CommandError(
                            'Error getting review request %s: %s' %
                            (request_id, e))

                    message = extract_commit_message(review_request)
                    author = review_request.get_submitter()

                try:
                    tool.create_commit(message, author,
                                       not self.options.commit_no_edit)
                    print('Changes committed to current branch.')
                except NotImplementedError:
                    raise CommandError('--commit is not supported with %s' %
                                       tool.name)
Ejemplo n.º 7
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.º 8
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))
Ejemplo n.º 9
0
    def main(self, review_request_id):
        """Run the command.

        Args:
            review_request_id (int):
                The ID of the review request to patch from.

        Raises:
            rbtools.command.CommandError:
                Patching the tree has failed.
        """
        patch_stdout = self.options.patch_stdout
        revert = self.options.revert_patch

        if patch_stdout and revert:
            raise CommandError(_('--print and --revert cannot both be used.'))

        repository_info, tool = self.initialize_scm_tool(
            client_name=self.options.repository_type,
            require_repository_info=not patch_stdout)

        if revert and not tool.supports_patch_revert:
            raise CommandError(
                _('The %s backend does not support reverting patches.')
                % tool.name)

        server_url = self.get_server_url(repository_info, tool)

        api_client, api_root = self.get_api(server_url)
        self.setup_tool(tool, api_root=api_root)

        if not patch_stdout:
            # Check if the repository info on the Review Board server matches
            # the local checkout.
            repository_info = repository_info.find_server_repository_info(
                api_root)

        # Get the patch, the used patch ID and base dir for the diff
        patch_data = self.get_patch(
            tool,
            api_root,
            review_request_id,
            self.options.diff_revision,
            self.options.commit_id)

        diff_body = patch_data['diff']
        diff_revision = patch_data['revision']
        base_dir = patch_data['base_dir']

        if self.options.patch_stdout:
            if isinstance(diff_body, bytes):
                print(diff_body.decode('utf-8'))
            else:
                print(diff_body)
        else:
            try:
                if tool.has_pending_changes():
                    message = 'Working directory is not clean.'

                    if not self.options.commit:
                        print('Warning: %s' % message)
                    else:
                        raise CommandError(message)
            except NotImplementedError:
                pass

            tmp_patch_file = make_tempfile(diff_body)

            success = self.apply_patch(
                repository_info, tool, review_request_id, diff_revision,
                tmp_patch_file, base_dir, revert=self.options.revert_patch)

            if not success:
                raise CommandError('Could not apply patch')

            if self.options.commit or self.options.commit_no_edit:
                if patch_data['commit_meta'] is not None:
                    # We are patching a commit so we already have the metadata
                    # required without making additional HTTP requests.
                    meta = patch_data['commit_meta']
                    message = meta['message']

                    # Fun fact: object does not have a __dict__ so you cannot
                    # call setattr() on them. We need this ability so we are
                    # creating a type that does.
                    author = type('Author', (object,), {})()
                    author.fullname = meta['author_name']
                    author.email = meta['author_email']

                else:
                    try:
                        review_request = api_root.get_review_request(
                            review_request_id=review_request_id,
                            force_text_type='plain')
                    except APIError as e:
                        raise CommandError('Error getting review request %s: %s'
                                           % (request_id, e))

                    message = extract_commit_message(review_request)
                    author = review_request.get_submitter()

                try:
                    tool.create_commit(message, author,
                                       not self.options.commit_no_edit)
                    print('Changes committed to current branch.')
                except NotImplementedError:
                    raise CommandError('--commit is not supported with %s'
                                       % tool.name)
Ejemplo n.º 10
0
    def _apply_patches(self, patches):
        """Apply a list of patches to the tree.

        Args:
            patches (list of dict):
                The list of patches to apply.

        Raises:
            rbtools.command.CommandError:
                Patching the tree has failed.
        """
        squash = self.options.squash
        revert = self.options.revert_patch
        commit_no_edit = self.options.commit_no_edit
        will_commit = self.options.commit or commit_no_edit
        total_patches = len(patches)

        # Check if we're planning to commit and have any patch without
        # metadata, in which case we'll need to fetch the review request so we
        # can generate a commit message.
        needs_review_request = will_commit and (
            squash or total_patches == 1
            or any(patch_data['commit_meta'] is None
                   for patch_data in patches))

        if needs_review_request:
            # Fetch the review request to use as a description. We only
            # want to fetch this once.
            try:
                review_request = self._api_root.get_review_request(
                    review_request_id=self._review_request_id,
                    force_text_type='plain')
            except APIError as e:
                raise CommandError(
                    _('Error getting review request %(review_request_id)d: '
                      '%(error)s') % {
                          'review_request_id': self._review_request_id,
                          'error': e,
                      })

            default_author = review_request.get_submitter()
            default_commit_message = extract_commit_message(review_request)
        else:
            default_author = None
            default_commit_message = None

        # Display a summary of what's about to be applied.
        diff_revision = patches[0]['revision']

        if revert:
            summary = ngettext(
                ('Reverting 1 patch from review request '
                 '%(review_request_id)s (diff revision %(diff_revision)s)'),
                ('Reverting %(num)d patches from review request '
                 '%(review_request_id)s (diff revision %(diff_revision)s)'),
                total_patches)
        else:
            summary = ngettext(
                ('Applying 1 patch from review request '
                 '%(review_request_id)s (diff revision %(diff_revision)s)'),
                ('Applying %(num)d patches from review request '
                 '%(review_request_id)s (diff revision %(diff_revision)s)'),
                total_patches)

        logger.info(
            summary, {
                'num': total_patches,
                'review_request_id': self._review_request_id,
                'diff_revision': diff_revision,
            })

        # Start applying all the patches.
        for patch_data in patches:
            patch_num = patch_data['patch_num']
            tmp_patch_file = make_tempfile(patch_data['diff'])

            success = self.apply_patch(diff_file_path=tmp_patch_file,
                                       base_dir=patch_data['base_dir'],
                                       patch_num=patch_num,
                                       total_patches=total_patches,
                                       revert=revert)

            os.unlink(tmp_patch_file)

            if not success:
                if revert:
                    error = _('Could not apply patch %(num)d of %(total)d')
                else:
                    error = _('Could not revert patch %(num)d of %(total)d')

                raise CommandError(error % {
                    'num': patch_num,
                    'total': total_patches,
                })

            # If the user wants to commit, then we'll be committing every
            # patch individually, unless the user wants to squash commits in
            # which case we'll only do this on the final commit.
            if will_commit and (not squash or patch_num == total_patches):
                meta = patch_data.get('commit_meta')

                if meta is not None and not squash and total_patches > 1:
                    # We are patching a commit so we already have the metadata
                    # required without making additional HTTP requests.
                    message = meta['message']
                    author = meta['author']
                else:
                    # We'll build this based on the summary/description from
                    # the review request and the patch number.
                    message = default_commit_message
                    author = default_author

                    assert message is not None
                    assert author is not None

                    if total_patches > 1:
                        # Record the patch number to help differentiate, in
                        # case we only have review request information and
                        # not commit messages. In practice, this shouldn't
                        # happen, as we should always have commit messages,
                        # but it's a decent safeguard.
                        message = '[%s/%s] %s' % (patch_num, total_patches,
                                                  message)

                if revert:
                    # Make it clear that this commit is reverting a prior
                    # patch, so it's easy to identify.
                    message = '[Revert] %s' % message

                try:
                    self._tool.create_commit(message=message,
                                             author=author,
                                             run_editor=not commit_no_edit)
                except CreateCommitError as e:
                    raise CommandError(six.text_type(e))
                except NotImplementedError:
                    raise CommandError('--commit is not supported with %s' %
                                       self._tool.name)