def diff(self, revisions, include_files=[], exclude_patterns=[],
             extra_args=[]):
        repository_info = self.get_repository_info()

        # SVN paths are always relative to the root of the repository, so we
        # compute the current path we are checked out at and use that as the
        # current working directory. We use / for the base_dir because we do
        # not normalize the paths to be filesystem paths, but instead use SVN
        # paths.
        exclude_patterns = normalize_patterns(exclude_patterns,
                                              '/',
                                              repository_info.base_path)

        # Keep track of information needed for handling empty files later.
        empty_files_revisions = {
            'base': None,
            'tip': None,
        }

        base = str(revisions['base'])
        tip = str(revisions['tip'])

        diff_cmd = ['diff', '--diff-cmd=diff', '--notice-ancestry']
        changelist = None

        if tip == self.REVISION_WORKING_COPY:
            # Posting the working copy
            diff_cmd.extend(['-r', base])
        elif tip.startswith(self.REVISION_CHANGELIST_PREFIX):
            # Posting a changelist
            changelist = tip[len(self.REVISION_CHANGELIST_PREFIX):]
            diff_cmd.extend(['--changelist', changelist])
        else:
            # Diff between two separate revisions. Behavior depends on whether
            # or not there's a working copy
            if self.options.repository_url:
                # No working copy--create 'old' and 'new' URLs
                if len(include_files) == 1:
                    # If there's a single file or directory passed in, we use
                    # that as part of the URL instead of as a separate
                    # filename.
                    repository_info.set_base_path(include_files[0])
                    include_files = []

                new_url = (repository_info.path + repository_info.base_path +
                           '@' + tip)

                # When the source revision is '0', assume the user wants to
                # upload a diff containing all the files in 'base_path' as
                # new files. If the base path within the repository is added to
                # both the old and new URLs, `svn diff` will error out, since
                # the base_path didn't exist at revision 0. To avoid that
                # error, use the repository's root URL as the source for the
                # diff.
                if base == '0':
                    old_url = repository_info.path + '@' + base
                else:
                    old_url = (repository_info.path +
                               repository_info.base_path + '@' + base)

                diff_cmd.extend([old_url, new_url])

                empty_files_revisions['base'] = '(revision %s)' % base
                empty_files_revisions['tip'] = '(revision %s)' % tip
            else:
                # Working copy--do a normal range diff
                diff_cmd.extend(['-r', '%s:%s' % (base, tip)])

                empty_files_revisions['base'] = '(revision %s)' % base
                empty_files_revisions['tip'] = '(revision %s)' % tip

        diff_cmd.extend(include_files)

        # Check for and validate --svn-show-copies-as-adds option, or evaluate
        # working copy to determine if scheduled commit will contain
        # addition-with-history commit. When this case occurs then
        # --svn-show-copies-as-adds must be specified. Note: this only
        # pertains to local modifications in a working copy and not diffs
        # between specific numeric revisions.
        if (((tip == self.REVISION_WORKING_COPY) or changelist) and
            is_valid_version(self.subversion_client_version,
                             self.SHOW_COPIES_AS_ADDS_MIN_VERSION)):
            svn_show_copies_as_adds = getattr(
                self.options, 'svn_show_copies_as_adds', None)

            if svn_show_copies_as_adds is None:
                if self.history_scheduled_with_commit(changelist,
                                                      include_files,
                                                      exclude_patterns):
                    sys.stderr.write("One or more files in your changeset has "
                                     "history scheduled with commit. Please "
                                     "try again with "
                                     "'--svn-show-copies-as-adds=y/n'.\n")
                    sys.exit(1)
            else:
                if svn_show_copies_as_adds in 'Yy':
                    diff_cmd.append("--show-copies-as-adds")

        diff = self._run_svn(diff_cmd, split_lines=True, results_unicode=False,
                             log_output_on_error=False)
        diff = self.handle_renames(diff)

        if self.supports_empty_files():
            diff = self._handle_empty_files(diff, diff_cmd,
                                            empty_files_revisions)

        diff = self.convert_to_absolute_paths(diff, repository_info)

        if exclude_patterns:
            diff = filter_diff(diff, self.INDEX_FILE_RE, exclude_patterns)

        return {
            'diff': b''.join(diff),
        }
Performs a diff in a Subversion repository.
dataset/ETHPy150Open reviewboard/rbtools/rbtools/clients/svn.py/SVNClient.diff