예제 #1
0
    def diff_2way(self, repo_name, f_path):
        diff1 = request.GET.get('diff1', '')
        diff2 = request.GET.get('diff2', '')

        nodes = []
        unknown_commits = []
        for commit in [diff1, diff2]:
            try:
                nodes.append(self._get_file_node(commit, f_path))
            except (RepositoryError, NodeError):
                log.exception('%(commit)s does not exist' % {'commit': commit})
                unknown_commits.append(commit)
                h.flash(h.literal(
                    _('Commit %(commit)s does not exist.') %
                    {'commit': commit}),
                        category='error')

        if unknown_commits:
            return redirect(
                url('files_home', repo_name=c.repo_name, f_path=f_path))

        if all(isinstance(node.commit, EmptyCommit) for node in nodes):
            raise HTTPNotFound

        node1, node2 = nodes

        f_gitdiff = diffs.get_gitdiff(node1, node2, ignore_whitespace=False)
        diff_processor = diffs.DiffProcessor(f_gitdiff, format='gitdiff')
        diff_data = diff_processor.prepare()

        if not diff_data or diff_data[0]['raw_diff'] == '':
            h.flash(h.literal(
                _('%(file_path)s has not changed '
                  'between %(commit_1)s and %(commit_2)s.') % {
                      'file_path': f_path,
                      'commit_1': node1.commit.id,
                      'commit_2': node2.commit.id
                  }),
                    category='error')
            return redirect(
                url('files_home', repo_name=c.repo_name, f_path=f_path))

        c.diff_data = diff_data[0]
        c.FID = h.FID(diff2, node2.path)
        # cleanup some unneeded data
        del c.diff_data['raw_diff']
        del c.diff_data['chunks']

        c.node1 = node1
        c.commit_1 = node1.commit
        c.node2 = node2
        c.commit_2 = node2.commit

        return render('files/diff_2way.html')
예제 #2
0
    def diff(self, repo_name, f_path):
        ignore_whitespace = request.GET.get('ignorews') == '1'
        line_context = request.GET.get('context', 3)
        diff1 = request.GET.get('diff1', '')
        diff2 = request.GET.get('diff2', '')
        c.action = request.GET.get('diff')
        c.no_changes = diff1 == diff2
        c.f_path = f_path
        c.big_diff = False
        c.anchor_url = anchor_url
        c.ignorews_url = _ignorews_url
        c.context_url = _context_url
        c.changes = OrderedDict()
        c.changes[diff2] = []

        #special case if we want a show rev only, it's impl here
        #to reduce JS and callbacks

        if request.GET.get('show_rev'):
            if str2bool(request.GET.get('annotate', 'False')):
                _url = url('files_annotate_home', repo_name=c.repo_name,
                           revision=diff1, f_path=c.f_path)
            else:
                _url = url('files_home', repo_name=c.repo_name,
                           revision=diff1, f_path=c.f_path)

            return redirect(_url)
        try:
            if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
                c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
                try:
                    node1 = c.changeset_1.get_node(f_path)
                    if node1.is_dir():
                        raise NodeError('%s path is a %s not a file'
                                        % (node1, type(node1)))
                except NodeDoesNotExistError:
                    c.changeset_1 = EmptyChangeset(cs=diff1,
                                                   revision=c.changeset_1.revision,
                                                   repo=c.rhodecode_repo)
                    node1 = FileNode(f_path, '', changeset=c.changeset_1)
            else:
                c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
                node1 = FileNode(f_path, '', changeset=c.changeset_1)

            if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
                c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
                try:
                    node2 = c.changeset_2.get_node(f_path)
                    if node2.is_dir():
                        raise NodeError('%s path is a %s not a file'
                                        % (node2, type(node2)))
                except NodeDoesNotExistError:
                    c.changeset_2 = EmptyChangeset(cs=diff2,
                                                   revision=c.changeset_2.revision,
                                                   repo=c.rhodecode_repo)
                    node2 = FileNode(f_path, '', changeset=c.changeset_2)
            else:
                c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
                node2 = FileNode(f_path, '', changeset=c.changeset_2)
        except (RepositoryError, NodeError):
            log.error(traceback.format_exc())
            return redirect(url('files_home', repo_name=c.repo_name,
                                f_path=f_path))

        if c.action == 'download':
            _diff = diffs.get_gitdiff(node1, node2,
                                      ignore_whitespace=ignore_whitespace,
                                      context=line_context)
            diff = diffs.DiffProcessor(_diff, format='gitdiff')

            diff_name = '%s_vs_%s.diff' % (diff1, diff2)
            response.content_type = 'text/plain'
            response.content_disposition = (
                'attachment; filename=%s' % diff_name
            )
            return diff.as_raw()

        elif c.action == 'raw':
            _diff = diffs.get_gitdiff(node1, node2,
                                      ignore_whitespace=ignore_whitespace,
                                      context=line_context)
            diff = diffs.DiffProcessor(_diff, format='gitdiff')
            response.content_type = 'text/plain'
            return diff.as_raw()

        else:
            fid = h.FID(diff2, node2.path)
            line_context_lcl = get_line_ctx(fid, request.GET)
            ign_whitespace_lcl = get_ignore_ws(fid, request.GET)

            lim = request.GET.get('fulldiff') or self.cut_off_limit
            _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
                                         filenode_new=node2,
                                         cut_off_limit=lim,
                                         ignore_whitespace=ign_whitespace_lcl,
                                         line_context=line_context_lcl,
                                         enable_comments=False)
            op = ''
            filename = node1.path
            cs_changes = {
                'fid': [cs1, cs2, op, filename, diff, st]
            }
            c.changes = cs_changes

        return render('files/file_diff.html')
예제 #3
0
    def compare(self, repo_name, source_ref_type, source_ref, target_ref_type,
                target_ref):
        # source_ref will be evaluated in source_repo
        source_repo_name = c.rhodecode_db_repo.repo_name
        source_path, source_id = parse_path_ref(source_ref)

        # target_ref will be evaluated in target_repo
        target_repo_name = request.GET.get('target_repo', source_repo_name)
        target_path, target_id = parse_path_ref(target_ref)

        c.commit_statuses = ChangesetStatus.STATUSES

        # if merge is True
        #   Show what changes since the shared ancestor commit of target/source
        #   the source would get if it was merged with target. Only commits
        #   which are in target but not in source will be shown.
        merge = str2bool(request.GET.get('merge'))
        # if merge is False
        #   Show a raw diff of source/target refs even if no ancestor exists

        # c.fulldiff disables cut_off_limit
        c.fulldiff = str2bool(request.GET.get('fulldiff'))

        # if partial, returns just compare_commits.html (commits log)
        partial = request.is_xhr

        # swap url for compare_diff page
        c.swap_url = h.url('compare_url',
                           repo_name=target_repo_name,
                           source_ref_type=target_ref_type,
                           source_ref=target_ref,
                           target_repo=source_repo_name,
                           target_ref_type=source_ref_type,
                           target_ref=source_ref,
                           merge=merge and '1' or '')

        source_repo = Repository.get_by_repo_name(source_repo_name)
        target_repo = Repository.get_by_repo_name(target_repo_name)

        if source_repo is None:
            msg = _('Could not find the original repo: %(repo)s') % {
                'repo': source_repo
            }

            log.error(msg)
            h.flash(msg, category='error')
            return redirect(url('compare_home', repo_name=c.repo_name))

        if target_repo is None:
            msg = _('Could not find the other repo: %(repo)s') % {
                'repo': target_repo_name
            }
            log.error(msg)
            h.flash(msg, category='error')
            return redirect(url('compare_home', repo_name=c.repo_name))

        source_alias = source_repo.scm_instance().alias
        target_alias = target_repo.scm_instance().alias
        if source_alias != target_alias:
            msg = _('The comparison of two different kinds of remote repos '
                    'is not available')
            log.error(msg)
            h.flash(msg, category='error')
            return redirect(url('compare_home', repo_name=c.repo_name))

        source_commit = self._get_commit_or_redirect(ref=source_id,
                                                     ref_type=source_ref_type,
                                                     repo=source_repo,
                                                     partial=partial)
        target_commit = self._get_commit_or_redirect(ref=target_id,
                                                     ref_type=target_ref_type,
                                                     repo=target_repo,
                                                     partial=partial)

        c.compare_home = False
        c.source_repo = source_repo
        c.target_repo = target_repo
        c.source_ref = source_ref
        c.target_ref = target_ref
        c.source_ref_type = source_ref_type
        c.target_ref_type = target_ref_type

        source_scm = source_repo.scm_instance()
        target_scm = target_repo.scm_instance()

        pre_load = ["author", "branch", "date", "message"]
        c.ancestor = None
        try:
            c.commit_ranges = source_scm.compare(source_commit.raw_id,
                                                 target_commit.raw_id,
                                                 target_scm,
                                                 merge,
                                                 pre_load=pre_load)
            if merge:
                c.ancestor = source_scm.get_common_ancestor(
                    source_commit.raw_id, target_commit.raw_id, target_scm)
        except RepositoryRequirementError:
            msg = _('Could not compare repos with different '
                    'large file settings')
            log.error(msg)
            if partial:
                return msg
            h.flash(msg, category='error')
            return redirect(url('compare_home', repo_name=c.repo_name))

        c.statuses = c.rhodecode_db_repo.statuses(
            [x.raw_id for x in c.commit_ranges])

        if partial:  # for PR ajax commits loader
            if not c.ancestor:
                return ''  # cannot merge if there is no ancestor
            return render('compare/compare_commits.html')

        if c.ancestor:
            # case we want a simple diff without incoming commits,
            # previewing what will be merged.
            # Make the diff on target repo (which is known to have target_ref)
            log.debug('Using ancestor %s as source_ref instead of %s' %
                      (c.ancestor, source_ref))
            source_repo = target_repo
            source_commit = target_repo.get_commit(commit_id=c.ancestor)

        # diff_limit will cut off the whole diff if the limit is applied
        # otherwise it will just hide the big files from the front-end
        diff_limit = self.cut_off_limit_diff
        file_limit = self.cut_off_limit_file

        log.debug(
            'calculating diff between '
            'source_ref:%s and target_ref:%s for repo `%s`', source_commit,
            target_commit, safe_unicode(source_repo.scm_instance().path))

        if source_commit.repository != target_commit.repository:
            msg = _(
                "Repositories unrelated. "
                "Cannot compare commit %(commit1)s from repository %(repo1)s "
                "with commit %(commit2)s from repository %(repo2)s.") % {
                    'commit1': h.show_id(source_commit),
                    'repo1': source_repo.repo_name,
                    'commit2': h.show_id(target_commit),
                    'repo2': target_repo.repo_name,
                }
            h.flash(msg, category='error')
            raise HTTPBadRequest()

        txtdiff = source_repo.scm_instance().get_diff(commit1=source_commit,
                                                      commit2=target_commit,
                                                      path1=source_path,
                                                      path=target_path)
        diff_processor = diffs.DiffProcessor(txtdiff,
                                             format='gitdiff',
                                             diff_limit=diff_limit,
                                             file_limit=file_limit,
                                             show_full_diff=c.fulldiff)
        _parsed = diff_processor.prepare()

        c.limited_diff = False
        if isinstance(_parsed, diffs.LimitedDiffContainer):
            c.limited_diff = True

        c.files = []
        c.changes = {}
        c.lines_added = 0
        c.lines_deleted = 0
        for f in _parsed:
            st = f['stats']
            if not st['binary']:
                c.lines_added += st['added']
                c.lines_deleted += st['deleted']
            fid = h.FID('', f['filename'])
            c.files.append([fid, f['operation'], f['filename'], f['stats'], f])
            htmldiff = diff_processor.as_html(enable_comments=False,
                                              parsed_lines=[f])
            c.changes[fid] = [f['operation'], f['filename'], htmldiff, f]

        c.preview_mode = merge

        return render('compare/compare_diff.html')
예제 #4
0
def anchor_url(revision, path, GET):
    fid = h.FID(revision, path)
    return h.url.current(anchor=fid, **dict(GET))
예제 #5
0
class ChangesetController(BaseRepoController):
    @LoginRequired()
    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
                                   'repository.admin')
    def __before__(self):
        super(ChangesetController, self).__before__()
        c.affected_files_cut_off = 60

    def index(self, revision):

        c.anchor_url = anchor_url
        c.ignorews_url = _ignorews_url
        c.context_url = _context_url
        limit_off = request.GET.get('fulldiff')
        #get ranges of revisions if preset
        rev_range = revision.split('...')[:2]
        enable_comments = True
        try:
            if len(rev_range) == 2:
                enable_comments = False
                rev_start = rev_range[0]
                rev_end = rev_range[1]
                rev_ranges = c.rhodecode_repo.get_changesets(start=rev_start,
                                                             end=rev_end)
            else:
                rev_ranges = [c.rhodecode_repo.get_changeset(revision)]

            c.cs_ranges = list(rev_ranges)
            if not c.cs_ranges:
                raise RepositoryError('Changeset range returned empty result')

        except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
            log.error(traceback.format_exc())
            h.flash(str(e), category='warning')
            return redirect(url('home'))

        c.changes = OrderedDict()

        c.lines_added = 0  # count of lines added
        c.lines_deleted = 0  # count of lines removes

        cumulative_diff = 0
        c.cut_off = False  # defines if cut off limit is reached

        c.comments = []
        c.inline_comments = []
        c.inline_cnt = 0
        # Iterate over ranges (default changeset view is always one changeset)
        for changeset in c.cs_ranges:
            c.comments.extend(ChangesetCommentsModel()\
                              .get_comments(c.rhodecode_db_repo.repo_id,
                                            changeset.raw_id))
            inlines = ChangesetCommentsModel()\
                        .get_inline_comments(c.rhodecode_db_repo.repo_id,
                                             changeset.raw_id)
            c.inline_comments.extend(inlines)
            c.changes[changeset.raw_id] = []
            try:
                changeset_parent = changeset.parents[0]
            except IndexError:
                changeset_parent = None

            #==================================================================
            # ADDED FILES
            #==================================================================
            for node in changeset.added:
                fid = h.FID(revision, node.path)
                line_context_lcl = get_line_ctx(fid, request.GET)
                ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
                lim = self.cut_off_limit
                if cumulative_diff > self.cut_off_limit:
                    lim = -1 if limit_off is None else None
                size, cs1, cs2, diff, st = wrapped_diff(
                    filenode_old=None,
                    filenode_new=node,
                    cut_off_limit=lim,
                    ignore_whitespace=ign_whitespace_lcl,
                    line_context=line_context_lcl,
                    enable_comments=enable_comments)
                cumulative_diff += size
                c.lines_added += st[0]
                c.lines_deleted += st[1]
                c.changes[changeset.raw_id].append(
                    ('added', node, diff, cs1, cs2, st))

            #==================================================================
            # CHANGED FILES
            #==================================================================
            for node in changeset.changed:
                try:
                    filenode_old = changeset_parent.get_node(node.path)
                except ChangesetError:
                    log.warning('Unable to fetch parent node for diff')
                    filenode_old = FileNode(node.path, '', EmptyChangeset())

                fid = h.FID(revision, node.path)
                line_context_lcl = get_line_ctx(fid, request.GET)
                ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
                lim = self.cut_off_limit
                if cumulative_diff > self.cut_off_limit:
                    lim = -1 if limit_off is None else None
                size, cs1, cs2, diff, st = wrapped_diff(
                    filenode_old=filenode_old,
                    filenode_new=node,
                    cut_off_limit=lim,
                    ignore_whitespace=ign_whitespace_lcl,
                    line_context=line_context_lcl,
                    enable_comments=enable_comments)
                cumulative_diff += size
                c.lines_added += st[0]
                c.lines_deleted += st[1]
                c.changes[changeset.raw_id].append(
                    ('changed', node, diff, cs1, cs2, st))
            #==================================================================
            # REMOVED FILES
            #==================================================================
            for node in changeset.removed:
                c.changes[changeset.raw_id].append(
                    ('removed', node, None, None, None, (0, 0)))

        # count inline comments
        for path, lines in c.inline_comments:
            for comments in lines.values():
                c.inline_cnt += len(comments)

        if len(c.cs_ranges) == 1:
            c.changeset = c.cs_ranges[0]
            c.changes = c.changes[c.changeset.raw_id]

            return render('changeset/changeset.html')
        else:
            return render('changeset/changeset_range.html')
예제 #6
0
class ChangesetController(BaseRepoController):

    @LoginRequired()
    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
                                   'repository.admin')
    def __before__(self):
        super(ChangesetController, self).__before__()
        c.affected_files_cut_off = 60
        repo_model = RepoModel()
        c.users_array = repo_model.get_users_js()
        c.users_groups_array = repo_model.get_users_groups_js()

    def index(self, revision, method='show'):
        c.anchor_url = anchor_url
        c.ignorews_url = _ignorews_url
        c.context_url = _context_url
        c.fulldiff = fulldiff = request.GET.get('fulldiff')
        #get ranges of revisions if preset
        rev_range = revision.split('...')[:2]
        enable_comments = True
        try:
            if len(rev_range) == 2:
                enable_comments = False
                rev_start = rev_range[0]
                rev_end = rev_range[1]
                rev_ranges = c.rhodecode_repo.get_changesets(start=rev_start,
                                                             end=rev_end)
            else:
                rev_ranges = [c.rhodecode_repo.get_changeset(revision)]

            c.cs_ranges = list(rev_ranges)
            if not c.cs_ranges:
                raise RepositoryError('Changeset range returned empty result')

        except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
            log.error(traceback.format_exc())
            h.flash(str(e), category='error')
            raise HTTPNotFound()

        c.changes = OrderedDict()

        c.lines_added = 0  # count of lines added
        c.lines_deleted = 0  # count of lines removes

        c.changeset_statuses = ChangesetStatus.STATUSES
        c.comments = []
        c.statuses = []
        c.inline_comments = []
        c.inline_cnt = 0

        # Iterate over ranges (default changeset view is always one changeset)
        for changeset in c.cs_ranges:
            inlines = []
            if method == 'show':
                c.statuses.extend([ChangesetStatusModel().get_status(
                            c.rhodecode_db_repo.repo_id, changeset.raw_id)])

                c.comments.extend(ChangesetCommentsModel()\
                                  .get_comments(c.rhodecode_db_repo.repo_id,
                                                revision=changeset.raw_id))

                #comments from PR
                st = ChangesetStatusModel().get_statuses(
                            c.rhodecode_db_repo.repo_id, changeset.raw_id,
                            with_revisions=True)
                # from associated statuses, check the pull requests, and
                # show comments from them

                prs = set([x.pull_request for x in
                           filter(lambda x: x.pull_request != None, st)])

                for pr in prs:
                    c.comments.extend(pr.comments)
                inlines = ChangesetCommentsModel()\
                            .get_inline_comments(c.rhodecode_db_repo.repo_id,
                                                 revision=changeset.raw_id)
                c.inline_comments.extend(inlines)

            c.changes[changeset.raw_id] = []

            cs2 = changeset.raw_id
            cs1 = changeset.parents[0].raw_id if changeset.parents else EmptyChangeset()
            context_lcl = get_line_ctx('', request.GET)
            ign_whitespace_lcl = ign_whitespace_lcl = get_ignore_ws('', request.GET)

            _diff = c.rhodecode_repo.get_diff(cs1, cs2,
                ignore_whitespace=ign_whitespace_lcl, context=context_lcl)
            diff_limit = self.cut_off_limit if not fulldiff else None
            diff_processor = diffs.DiffProcessor(_diff,
                                                 vcs=c.rhodecode_repo.alias,
                                                 format='gitdiff',
                                                 diff_limit=diff_limit)
            cs_changes = OrderedDict()
            if method == 'show':
                _parsed = diff_processor.prepare()
                c.limited_diff = False
                if isinstance(_parsed, LimitedDiffContainer):
                    c.limited_diff = True
                for f in _parsed:
                    st = f['stats']
                    if st[0] != 'b':
                        c.lines_added += st[0]
                        c.lines_deleted += st[1]
                    fid = h.FID(changeset.raw_id, f['filename'])
                    diff = diff_processor.as_html(enable_comments=enable_comments,
                                                  parsed_lines=[f])
                    cs_changes[fid] = [cs1, cs2, f['operation'], f['filename'],
                                       diff, st]
            else:
                # downloads/raw we only need RAW diff nothing else
                diff = diff_processor.as_raw()
                cs_changes[''] = [None, None, None, None, diff, None]
            c.changes[changeset.raw_id] = cs_changes

        #sort comments by how they were generated
        c.comments = sorted(c.comments, key=lambda x: x.comment_id)

        # count inline comments
        for __, lines in c.inline_comments:
            for comments in lines.values():
                c.inline_cnt += len(comments)

        if len(c.cs_ranges) == 1:
            c.changeset = c.cs_ranges[0]
            c.parent_tmpl = ''.join(['# Parent  %s\n' % x.raw_id
                                     for x in c.changeset.parents])
        if method == 'download':
            response.content_type = 'text/plain'
            response.content_disposition = 'attachment; filename=%s.diff' \
                                            % revision[:12]
            return diff
        elif method == 'patch':
            response.content_type = 'text/plain'
            c.diff = safe_unicode(diff)
            return render('changeset/patch_changeset.html')
        elif method == 'raw':
            response.content_type = 'text/plain'
            return diff
        elif method == 'show':
            if len(c.cs_ranges) == 1:
                return render('changeset/changeset.html')
            else:
                return render('changeset/changeset_range.html')
예제 #7
0
    def _load_compare_data(self, pull_request, enable_comments=True):
        """
        Load context data needed for generating compare diff

        :param pull_request:
        """
        org_repo = pull_request.org_repo
        (org_ref_type, org_ref_name,
         org_ref_rev) = pull_request.org_ref.split(':')

        other_repo = org_repo
        (other_ref_type, other_ref_name,
         other_ref_rev) = pull_request.other_ref.split(':')

        # despite opening revisions for bookmarks/branches/tags, we always
        # convert this to rev to prevent changes after bookmark or branch change
        org_ref = ('rev', org_ref_rev)
        other_ref = ('rev', other_ref_rev)

        c.org_repo = org_repo
        c.other_repo = other_repo

        c.fulldiff = fulldiff = request.GET.get('fulldiff')

        c.cs_ranges = [
            org_repo.get_changeset(x) for x in pull_request.revisions
        ]

        c.statuses = org_repo.statuses([x.raw_id for x in c.cs_ranges])

        c.org_ref = org_ref[1]
        c.org_ref_type = org_ref[0]
        c.other_ref = other_ref[1]
        c.other_ref_type = other_ref[0]

        diff_limit = self.cut_off_limit if not fulldiff else None

        # we swap org/other ref since we run a simple diff on one repo
        log.debug('running diff between %s and %s in %s' %
                  (other_ref, org_ref, org_repo.scm_instance.path))
        txtdiff = org_repo.scm_instance.get_diff(rev1=safe_str(other_ref[1]),
                                                 rev2=safe_str(org_ref[1]))

        diff_processor = diffs.DiffProcessor(txtdiff or '',
                                             format='gitdiff',
                                             diff_limit=diff_limit)
        _parsed = diff_processor.prepare()

        c.limited_diff = False
        if isinstance(_parsed, LimitedDiffContainer):
            c.limited_diff = True

        c.files = []
        c.changes = {}
        c.lines_added = 0
        c.lines_deleted = 0

        for f in _parsed:
            st = f['stats']
            c.lines_added += st['added']
            c.lines_deleted += st['deleted']
            fid = h.FID('', f['filename'])
            c.files.append([fid, f['operation'], f['filename'], f['stats']])
            htmldiff = diff_processor.as_html(enable_comments=enable_comments,
                                              parsed_lines=[f])
            c.changes[fid] = [f['operation'], f['filename'], htmldiff]
예제 #8
0
    def _index(self, commit_id_range, method):
        c.ignorews_url = _ignorews_url
        c.context_url = _context_url
        c.fulldiff = fulldiff = request.GET.get('fulldiff')
        # get ranges of commit ids if preset
        commit_range = commit_id_range.split('...')[:2]
        enable_comments = True
        try:
            pre_load = [
                'affected_files', 'author', 'branch', 'date', 'message',
                'parents'
            ]

            if len(commit_range) == 2:
                enable_comments = False
                commits = c.rhodecode_repo.get_commits(
                    start_id=commit_range[0],
                    end_id=commit_range[1],
                    pre_load=pre_load)
                commits = list(commits)
            else:
                commits = [
                    c.rhodecode_repo.get_commit(commit_id=commit_id_range,
                                                pre_load=pre_load)
                ]

            c.commit_ranges = commits
            if not c.commit_ranges:
                raise RepositoryError(
                    'The commit range returned an empty result')
        except CommitDoesNotExistError:
            msg = _('No such commit exists for this repository')
            h.flash(msg, category='error')
            raise HTTPNotFound()
        except Exception:
            log.exception("General failure")
            raise HTTPNotFound()

        c.changes = OrderedDict()
        c.lines_added = 0
        c.lines_deleted = 0

        c.commit_statuses = ChangesetStatus.STATUSES
        c.comments = []
        c.statuses = []
        c.inline_comments = []
        c.inline_cnt = 0
        c.files = []

        # Iterate over ranges (default commit view is always one commit)
        for commit in c.commit_ranges:
            if method == 'show':
                c.statuses.extend([
                    ChangesetStatusModel().get_status(
                        c.rhodecode_db_repo.repo_id, commit.raw_id)
                ])

                c.comments.extend(ChangesetCommentsModel().get_comments(
                    c.rhodecode_db_repo.repo_id, revision=commit.raw_id))

                # comments from PR
                st = ChangesetStatusModel().get_statuses(
                    c.rhodecode_db_repo.repo_id,
                    commit.raw_id,
                    with_revisions=True)

                # from associated statuses, check the pull requests, and
                # show comments from them

                prs = set(
                    x.pull_request
                    for x in filter(lambda x: x.pull_request is not None, st))
                for pr in prs:
                    c.comments.extend(pr.comments)

                inlines = ChangesetCommentsModel().get_inline_comments(
                    c.rhodecode_db_repo.repo_id, revision=commit.raw_id)
                c.inline_comments.extend(inlines.iteritems())

            c.changes[commit.raw_id] = []

            commit2 = commit
            commit1 = commit.parents[0] if commit.parents else EmptyCommit()

            # fetch global flags of ignore ws or context lines
            context_lcl = get_line_ctx('', request.GET)
            ign_whitespace_lcl = get_ignore_ws('', request.GET)

            _diff = c.rhodecode_repo.get_diff(
                commit1,
                commit2,
                ignore_whitespace=ign_whitespace_lcl,
                context=context_lcl)

            # diff_limit will cut off the whole diff if the limit is applied
            # otherwise it will just hide the big files from the front-end
            diff_limit = self.cut_off_limit_diff
            file_limit = self.cut_off_limit_file

            diff_processor = diffs.DiffProcessor(_diff,
                                                 format='gitdiff',
                                                 diff_limit=diff_limit,
                                                 file_limit=file_limit,
                                                 show_full_diff=fulldiff)
            commit_changes = OrderedDict()
            if method == 'show':
                _parsed = diff_processor.prepare()
                c.limited_diff = isinstance(_parsed,
                                            diffs.LimitedDiffContainer)
                for f in _parsed:
                    c.files.append(f)
                    st = f['stats']
                    c.lines_added += st['added']
                    c.lines_deleted += st['deleted']
                    fid = h.FID(commit.raw_id, f['filename'])
                    diff = diff_processor.as_html(
                        enable_comments=enable_comments, parsed_lines=[f])
                    commit_changes[fid] = [
                        commit1.raw_id, commit2.raw_id, f['operation'],
                        f['filename'], diff, st, f
                    ]
            else:
                # downloads/raw we only need RAW diff nothing else
                diff = diff_processor.as_raw()
                commit_changes[''] = [None, None, None, None, diff, None, None]
            c.changes[commit.raw_id] = commit_changes

        # sort comments by how they were generated
        c.comments = sorted(c.comments, key=lambda x: x.comment_id)

        # count inline comments
        for __, lines in c.inline_comments:
            for comments in lines.values():
                c.inline_cnt += len(comments)

        if len(c.commit_ranges) == 1:
            c.commit = c.commit_ranges[0]
            c.parent_tmpl = ''.join('# Parent  %s\n' % x.raw_id
                                    for x in c.commit.parents)
        if method == 'download':
            response.content_type = 'text/plain'
            response.content_disposition = ('attachment; filename=%s.diff' %
                                            commit_id_range[:12])
            return diff
        elif method == 'patch':
            response.content_type = 'text/plain'
            c.diff = safe_unicode(diff)
            return render('changeset/patch_changeset.html')
        elif method == 'raw':
            response.content_type = 'text/plain'
            return diff
        elif method == 'show':
            if len(c.commit_ranges) == 1:
                return render('changeset/changeset.html')
            else:
                c.ancestor = None
                c.target_repo = c.rhodecode_db_repo
                return render('changeset/changeset_range.html')
예제 #9
0
파일: files.py 프로젝트: elfixit/rhodecode
    def diff(self, repo_name, f_path):
        ignore_whitespace = request.GET.get('ignorews') == '1'
        line_context = request.GET.get('context', 3)
        diff1 = request.GET.get('diff1', '')
        diff2 = request.GET.get('diff2', '')
        c.action = request.GET.get('diff')
        c.no_changes = diff1 == diff2
        c.f_path = f_path
        c.big_diff = False
        c.anchor_url = anchor_url
        c.ignorews_url = _ignorews_url
        c.context_url = _context_url
        c.changes = OrderedDict()
        c.changes[diff2] = []
        try:
            if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
                c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
                node1 = c.changeset_1.get_node(f_path)
            else:
                c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
                node1 = FileNode('.', '', changeset=c.changeset_1)

            if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
                c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
                node2 = c.changeset_2.get_node(f_path)
            else:
                c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
                node2 = FileNode('.', '', changeset=c.changeset_2)
        except RepositoryError:
            return redirect(
                url('files_home', repo_name=c.repo_name, f_path=f_path))

        if c.action == 'download':
            _diff = diffs.get_gitdiff(node1,
                                      node2,
                                      ignore_whitespace=ignore_whitespace,
                                      context=line_context)
            diff = diffs.DiffProcessor(_diff, format='gitdiff')

            diff_name = '%s_vs_%s.diff' % (diff1, diff2)
            response.content_type = 'text/plain'
            response.content_disposition = ('attachment; filename=%s' %
                                            diff_name)
            return diff.raw_diff()

        elif c.action == 'raw':
            _diff = diffs.get_gitdiff(node1,
                                      node2,
                                      ignore_whitespace=ignore_whitespace,
                                      context=line_context)
            diff = diffs.DiffProcessor(_diff, format='gitdiff')
            response.content_type = 'text/plain'
            return diff.raw_diff()

        else:
            fid = h.FID(diff2, node2.path)
            line_context_lcl = get_line_ctx(fid, request.GET)
            ign_whitespace_lcl = get_ignore_ws(fid, request.GET)

            lim = request.GET.get('fulldiff') or self.cut_off_limit
            _, cs1, cs2, diff, st = diffs.wrapped_diff(
                filenode_old=node1,
                filenode_new=node2,
                cut_off_limit=lim,
                ignore_whitespace=ign_whitespace_lcl,
                line_context=line_context_lcl,
                enable_comments=False)

            c.changes = [(
                '',
                node2,
                diff,
                cs1,
                cs2,
                st,
            )]

        return render('files/file_diff.html')
예제 #10
0
    def _load_compare_data(self, pull_request, enable_comments=True):
        """
        Load context data needed for generating compare diff

        :param pull_request: object related to the request
        :param enable_comments: flag to determine if comments are included
        """
        source_repo = pull_request.source_repo
        source_ref_id = pull_request.source_ref_parts.commit_id

        target_repo = pull_request.target_repo
        target_ref_id = pull_request.target_ref_parts.commit_id

        # despite opening commits for bookmarks/branches/tags, we always
        # convert this to rev to prevent changes after bookmark or branch change
        c.source_ref_type = 'rev'
        c.source_ref = source_ref_id

        c.target_ref_type = 'rev'
        c.target_ref = target_ref_id

        c.source_repo = source_repo
        c.target_repo = target_repo

        c.fulldiff = bool(request.GET.get('fulldiff'))

        # diff_limit is the old behavior, will cut off the whole diff
        # if the limit is applied  otherwise will just hide the
        # big files from the front-end
        diff_limit = self.cut_off_limit_diff
        file_limit = self.cut_off_limit_file

        pre_load = ["author", "branch", "date", "message"]

        c.commit_ranges = []
        source_commit = EmptyCommit()
        target_commit = EmptyCommit()
        c.missing_requirements = False
        try:
            c.commit_ranges = [
                source_repo.get_commit(commit_id=rev, pre_load=pre_load)
                for rev in pull_request.revisions
            ]

            c.statuses = source_repo.statuses(
                [x.raw_id for x in c.commit_ranges])

            target_commit = source_repo.get_commit(
                commit_id=safe_str(target_ref_id))
            source_commit = source_repo.get_commit(
                commit_id=safe_str(source_ref_id))
        except RepositoryRequirementError:
            c.missing_requirements = True

        c.missing_commits = False
        if (c.missing_requirements or isinstance(source_commit, EmptyCommit)
                or source_commit == target_commit):
            _parsed = []
            c.missing_commits = True
        else:
            vcs_diff = PullRequestModel().get_diff(pull_request)
            diff_processor = diffs.DiffProcessor(vcs_diff,
                                                 format='gitdiff',
                                                 diff_limit=diff_limit,
                                                 file_limit=file_limit,
                                                 show_full_diff=c.fulldiff)
            _parsed = diff_processor.prepare()

        c.limited_diff = isinstance(_parsed, LimitedDiffContainer)

        c.files = []
        c.changes = {}
        c.lines_added = 0
        c.lines_deleted = 0
        c.included_files = []
        c.deleted_files = []

        for f in _parsed:
            st = f['stats']
            c.lines_added += st['added']
            c.lines_deleted += st['deleted']

            fid = h.FID('', f['filename'])
            c.files.append([fid, f['operation'], f['filename'], f['stats']])
            c.included_files.append(f['filename'])
            html_diff = diff_processor.as_html(enable_comments=enable_comments,
                                               parsed_lines=[f])
            c.changes[fid] = [f['operation'], f['filename'], html_diff, f]
예제 #11
0
    def diff(self, repo_name, f_path):
        ignore_whitespace = request.GET.get('ignorews') == '1'
        line_context = request.GET.get('context', 3)
        diff1 = request.GET.get('diff1', '')

        path1, diff1 = parse_path_ref(diff1, default_path=f_path)

        diff2 = request.GET.get('diff2', '')
        c.action = request.GET.get('diff')
        c.no_changes = diff1 == diff2
        c.f_path = f_path
        c.big_diff = False
        c.ignorews_url = _ignorews_url
        c.context_url = _context_url
        c.changes = OrderedDict()
        c.changes[diff2] = []

        if not any((diff1, diff2)):
            h.flash(
                'Need query parameter "diff1" or "diff2" to generate a diff.',
                category='error')
            raise HTTPBadRequest()

        # special case if we want a show commit_id only, it's impl here
        # to reduce JS and callbacks

        if request.GET.get('show_rev') and diff1:
            if str2bool(request.GET.get('annotate', 'False')):
                _url = url('files_annotate_home',
                           repo_name=c.repo_name,
                           revision=diff1,
                           f_path=path1)
            else:
                _url = url('files_home',
                           repo_name=c.repo_name,
                           revision=diff1,
                           f_path=path1)

            return redirect(_url)

        try:
            node1 = self._get_file_node(diff1, path1)
            node2 = self._get_file_node(diff2, f_path)
        except (RepositoryError, NodeError):
            log.exception("Exception while trying to get node from repository")
            return redirect(
                url('files_home', repo_name=c.repo_name, f_path=f_path))

        if all(
                isinstance(node.commit, EmptyCommit)
                for node in (node1, node2)):
            raise HTTPNotFound

        c.commit_1 = node1.commit
        c.commit_2 = node2.commit

        if c.action == 'download':
            _diff = diffs.get_gitdiff(node1,
                                      node2,
                                      ignore_whitespace=ignore_whitespace,
                                      context=line_context)
            diff = diffs.DiffProcessor(_diff, format='gitdiff')

            diff_name = '%s_vs_%s.diff' % (diff1, diff2)
            response.content_type = 'text/plain'
            response.content_disposition = ('attachment; filename=%s' %
                                            (diff_name, ))
            charset = self._get_default_encoding()
            if charset:
                response.charset = charset
            return diff.as_raw()

        elif c.action == 'raw':
            _diff = diffs.get_gitdiff(node1,
                                      node2,
                                      ignore_whitespace=ignore_whitespace,
                                      context=line_context)
            diff = diffs.DiffProcessor(_diff, format='gitdiff')
            response.content_type = 'text/plain'
            charset = self._get_default_encoding()
            if charset:
                response.charset = charset
            return diff.as_raw()

        else:
            fid = h.FID(diff2, node2.path)
            line_context_lcl = get_line_ctx(fid, request.GET)
            ign_whitespace_lcl = get_ignore_ws(fid, request.GET)

            __, commit1, commit2, diff, st, data = diffs.wrapped_diff(
                filenode_old=node1,
                filenode_new=node2,
                diff_limit=self.cut_off_limit_diff,
                file_limit=self.cut_off_limit_file,
                show_full_diff=request.GET.get('fulldiff'),
                ignore_whitespace=ign_whitespace_lcl,
                line_context=line_context_lcl,
            )

            c.lines_added = data['stats']['added'] if data else 0
            c.lines_deleted = data['stats']['deleted'] if data else 0
            c.files = [data]
            c.commit_ranges = [c.commit_1, c.commit_2]
            c.ancestor = None
            c.statuses = []
            c.target_repo = c.rhodecode_db_repo
            c.filename1 = node1.path
            c.filename = node2.path
            c.binary_file = node1.is_binary or node2.is_binary
            operation = data['operation'] if data else ''

            commit_changes = {
                # TODO: it's passing the old file to the diff to keep the
                # standard but this is not being used for this template,
                # but might need both files in the future or a more standard
                # way to work with that
                'fid':
                [commit1, commit2, operation, c.filename, diff, st, data]
            }

            c.changes = commit_changes

        return render('files/file_diff.html')
예제 #12
0
    def index(self, org_ref_type, org_ref, other_ref_type, other_ref):
        # org_ref will be evaluated in org_repo
        org_repo = c.rhodecode_db_repo.repo_name
        org_ref = (org_ref_type, org_ref)
        # other_ref will be evaluated in other_repo
        other_ref = (other_ref_type, other_ref)
        other_repo = request.GET.get('other_repo', org_repo)
        # If merge is True:
        #   Show what org would get if merged with other:
        #   List changesets that are ancestors of other but not of org.
        #   New changesets in org is thus ignored.
        #   Diff will be from common ancestor, and merges of org to other will thus be ignored.
        # If merge is False:
        #   Make a raw diff from org to other, no matter if related or not.
        #   Changesets in one and not in the other will be ignored
        merge = bool(request.GET.get('merge'))
        # fulldiff disables cut_off_limit
        c.fulldiff = request.GET.get('fulldiff')
        # partial uses compare_cs.html template directly
        partial = request.environ.get('HTTP_X_PARTIAL_XHR')
        # as_form puts hidden input field with changeset revisions
        c.as_form = partial and request.GET.get('as_form')
        # swap url for compare_diff page - never partial and never as_form
        c.swap_url = h.url('compare_url',
                           repo_name=other_repo,
                           org_ref_type=other_ref[0],
                           org_ref=other_ref[1],
                           other_repo=org_repo,
                           other_ref_type=org_ref[0],
                           other_ref=org_ref[1],
                           merge=merge or '')

        org_repo = Repository.get_by_repo_name(org_repo)
        other_repo = Repository.get_by_repo_name(other_repo)

        if org_repo is None:
            log.error('Could not find org repo %s' % org_repo)
            raise HTTPNotFound
        if other_repo is None:
            log.error('Could not find other repo %s' % other_repo)
            raise HTTPNotFound

        if org_repo != other_repo and h.is_git(org_repo):
            log.error(
                'compare of two remote repos not available for GIT REPOS')
            raise HTTPNotFound

        if org_repo.scm_instance.alias != other_repo.scm_instance.alias:
            log.error(
                'compare of two different kind of remote repos not available')
            raise HTTPNotFound

        org_rev = self.__get_rev_or_redirect(ref=org_ref,
                                             repo=org_repo,
                                             partial=partial)
        other_rev = self.__get_rev_or_redirect(ref=other_ref,
                                               repo=other_repo,
                                               partial=partial)

        c.org_repo = org_repo
        c.other_repo = other_repo
        c.org_ref = org_ref[1]
        c.other_ref = other_ref[1]
        c.org_ref_type = org_ref[0]
        c.other_ref_type = other_ref[0]

        c.cs_ranges, c.ancestor = self._get_changesets(
            org_repo.scm_instance.alias, org_repo.scm_instance, org_rev,
            other_repo.scm_instance, other_rev, merge)

        c.statuses = c.rhodecode_db_repo.statuses(
            [x.raw_id for x in c.cs_ranges])
        if merge and not c.ancestor:
            log.error('Unable to find ancestor revision')

        if partial:
            return render('compare/compare_cs.html')

        if c.ancestor:
            assert merge
            # case we want a simple diff without incoming changesets,
            # previewing what will be merged.
            # Make the diff on the other repo (which is known to have other_ref)
            log.debug('Using ancestor %s as org_ref instead of %s' %
                      (c.ancestor, org_ref))
            org_rev = c.ancestor
            org_repo = other_repo

        diff_limit = self.cut_off_limit if not c.fulldiff else None

        log.debug('running diff between %s and %s in %s' %
                  (org_rev, other_rev, org_repo.scm_instance.path))
        txtdiff = org_repo.scm_instance.get_diff(rev1=org_rev, rev2=other_rev)

        diff_processor = diffs.DiffProcessor(txtdiff or '',
                                             format='gitdiff',
                                             diff_limit=diff_limit)
        _parsed = diff_processor.prepare()

        c.limited_diff = False
        if isinstance(_parsed, LimitedDiffContainer):
            c.limited_diff = True

        c.files = []
        c.changes = {}
        c.lines_added = 0
        c.lines_deleted = 0
        for f in _parsed:
            st = f['stats']
            if not st['binary']:
                c.lines_added += st['added']
                c.lines_deleted += st['deleted']
            fid = h.FID('', f['filename'])
            c.files.append([fid, f['operation'], f['filename'], f['stats']])
            htmldiff = diff_processor.as_html(enable_comments=False,
                                              parsed_lines=[f])
            c.changes[fid] = [f['operation'], f['filename'], htmldiff]

        return render('compare/compare_diff.html')