Esempio n. 1
0
class ChangesetController(BaseRepoController):
    def __before__(self):
        super(ChangesetController, self).__before__()
        c.affected_files_cut_off = 60

    def __load_data(self):
        repo_model = RepoModel()
        c.users_array = repo_model.get_users_js()
        c.user_groups_array = repo_model.get_user_groups_js()

    def _index(self, revision, method):
        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
        c.cs_repo = c.db_repo
        try:
            if len(rev_range) == 2:
                enable_comments = False
                rev_start = rev_range[0]
                rev_end = rev_range[1]
                rev_ranges = c.db_repo_scm_instance.get_changesets(
                    start=rev_start, end=rev_end)
            else:
                rev_ranges = [c.db_repo_scm_instance.get_changeset(revision)]

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

        except (ChangesetDoesNotExistError, ), e:
            log.error(traceback.format_exc())
            msg = _('Such revision does not exist for this repository')
            h.flash(msg, 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
        comments = dict()
        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.db_repo.repo_id,
                                                      changeset.raw_id)
                ])

                # Changeset comments
                comments.update(
                    (com.comment_id, com)
                    for com in ChangesetCommentsModel().get_comments(
                        c.db_repo.repo_id, revision=changeset.raw_id))

                # Status change comments - mostly from pull requests
                comments.update((
                    st.changeset_comment_id, st.comment
                ) for st in ChangesetStatusModel().get_statuses(
                    c.db_repo.repo_id, changeset.raw_id, with_revisions=True))

                inlines = ChangesetCommentsModel()\
                            .get_inline_comments(c.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().raw_id
            context_lcl = get_line_ctx('', request.GET)
            ign_whitespace_lcl = ign_whitespace_lcl = get_ignore_ws(
                '', request.GET)

            _diff = c.db_repo_scm_instance.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.db_repo_scm_instance.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']
                    c.lines_added += st['added']
                    c.lines_deleted += st['deleted']
                    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 in creation order
        c.comments = [com for com_id, com in sorted(comments.items())]

        # 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':
            self.__load_data()
            if len(c.cs_ranges) == 1:
                return render('changeset/changeset.html')
            else:
                c.cs_ranges_org = None
                c.cs_comments = {}
                revs = [ctx.revision for ctx in reversed(c.cs_ranges)]
                c.jsdata = json.dumps(graph_data(c.db_repo_scm_instance, revs))
                return render('changeset/changeset_range.html')
Esempio n. 2
0
    def compare(self, repo_name, org_ref_type, org_ref_name, other_ref_type,
                other_ref_name):
        org_ref_name = org_ref_name.strip()
        other_ref_name = other_ref_name.strip()

        # 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
        fulldiff = request.GET.get('fulldiff')
        # partial uses compare_cs.html template directly
        partial = request.environ.get('HTTP_X_PARTIAL_XHR')
        # is_ajax_preview puts hidden input field with changeset revisions
        c.is_ajax_preview = partial and request.GET.get('is_ajax_preview')
        # swap url for compare_diff page - never partial and never is_ajax_preview
        c.swap_url = h.url('compare_url',
                           repo_name=c.cs_repo.repo_name,
                           org_ref_type=other_ref_type,
                           org_ref_name=other_ref_name,
                           other_repo=c.a_repo.repo_name,
                           other_ref_type=org_ref_type,
                           other_ref_name=org_ref_name,
                           merge=merge or '')

        # set callbacks for generating markup for icons
        c.ignorews_url = _ignorews_url
        c.context_url = _context_url
        ignore_whitespace = request.GET.get('ignorews') == '1'
        line_context = safe_int(request.GET.get('context'), 3)

        c.a_rev = self._get_ref_rev(c.a_repo,
                                    org_ref_type,
                                    org_ref_name,
                                    returnempty=True)
        c.cs_rev = self._get_ref_rev(c.cs_repo, other_ref_type, other_ref_name)

        c.compare_home = False
        c.a_ref_name = org_ref_name
        c.a_ref_type = org_ref_type
        c.cs_ref_name = other_ref_name
        c.cs_ref_type = other_ref_type

        c.cs_ranges, c.cs_ranges_org, c.ancestors = self._get_changesets(
            c.a_repo.scm_instance.alias, c.a_repo.scm_instance, c.a_rev,
            c.cs_repo.scm_instance, c.cs_rev)
        raw_ids = [x.raw_id for x in c.cs_ranges]
        c.cs_comments = c.cs_repo.get_comments(raw_ids)
        c.cs_statuses = c.cs_repo.statuses(raw_ids)

        revs = [ctx.revision for ctx in reversed(c.cs_ranges)]
        c.jsdata = graph_data(c.cs_repo.scm_instance, revs)

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

        org_repo = c.a_repo
        other_repo = c.cs_repo

        if merge:
            rev1 = msg = None
            if not c.cs_ranges:
                msg = _('Cannot show empty diff')
            elif not c.ancestors:
                msg = _('No ancestor found for merge diff')
            elif len(c.ancestors) == 1:
                rev1 = c.ancestors[0]
            else:
                msg = _('Multiple merge ancestors found for merge compare')
            if rev1 is None:
                h.flash(msg, category='error')
                log.error(msg)
                raise HTTPNotFound

            # 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_rev)
            log.debug('Using ancestor %s as rev1 instead of %s', rev1, c.a_rev)
            org_repo = other_repo
        else:  # comparing tips, not necessarily linearly related
            if org_repo != other_repo:
                # TODO: we could do this by using hg unionrepo
                log.error('cannot compare across repos %s and %s', org_repo,
                          other_repo)
                h.flash(_(
                    'Cannot compare repositories without using common ancestor'
                ),
                        category='error')
                raise HTTPBadRequest
            rev1 = c.a_rev

        diff_limit = None if fulldiff else self.cut_off_limit

        log.debug('running diff between %s and %s in %s', rev1, c.cs_rev,
                  org_repo.scm_instance.path)
        raw_diff = diffs.get_diff(org_repo.scm_instance,
                                  rev1=rev1,
                                  rev2=c.cs_rev,
                                  ignore_whitespace=ignore_whitespace,
                                  context=line_context)

        diff_processor = diffs.DiffProcessor(raw_diff, diff_limit=diff_limit)
        c.limited_diff = diff_processor.limited_diff
        c.file_diff_data = []
        c.lines_added = 0
        c.lines_deleted = 0
        for f in diff_processor.parsed:
            st = f['stats']
            c.lines_added += st['added']
            c.lines_deleted += st['deleted']
            filename = f['filename']
            fid = h.FID('', filename)
            html_diff = diffs.as_html(enable_comments=False, parsed_lines=[f])
            c.file_diff_data.append(
                (fid, None, f['operation'], f['old_filename'], filename,
                 html_diff, st))

        return render('compare/compare_diff.html')
Esempio n. 3
0
    def _index(self, revision, method):
        c.pull_request = None
        c.anchor_url = anchor_url
        c.ignorews_url = _ignorews_url
        c.context_url = _context_url
        c.fulldiff = request.GET.get(
            'fulldiff')  # for reporting number of changed files
        # get ranges of revisions if preset
        rev_range = revision.split('...')[:2]
        enable_comments = True
        c.cs_repo = c.db_repo
        try:
            if len(rev_range) == 2:
                enable_comments = False
                rev_start = rev_range[0]
                rev_end = rev_range[1]
                rev_ranges = c.db_repo_scm_instance.get_changesets(
                    start=rev_start, end=rev_end)
            else:
                rev_ranges = [c.db_repo_scm_instance.get_changeset(revision)]

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

        except (ChangesetDoesNotExistError, EmptyRepositoryError):
            log.debug(traceback.format_exc())
            msg = _('Such revision does not exist for this repository')
            h.flash(msg, 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
        comments = dict()
        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:
            if method == 'show':
                c.statuses.extend([
                    ChangesetStatusModel().get_status(c.db_repo.repo_id,
                                                      changeset.raw_id)
                ])

                # Changeset comments
                comments.update(
                    (com.comment_id, com)
                    for com in ChangesetCommentsModel().get_comments(
                        c.db_repo.repo_id, revision=changeset.raw_id))

                # Status change comments - mostly from pull requests
                comments.update((
                    st.comment_id, st.comment
                ) for st in ChangesetStatusModel().get_statuses(
                    c.db_repo.repo_id, changeset.raw_id, with_revisions=True)
                                if st.comment_id is not None)

                inlines = ChangesetCommentsModel() \
                            .get_inline_comments(c.db_repo.repo_id,
                                                 revision=changeset.raw_id)
                c.inline_comments.extend(inlines)

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

            raw_diff = diffs.get_diff(c.db_repo_scm_instance,
                                      cs1,
                                      cs2,
                                      ignore_whitespace=ign_whitespace_lcl,
                                      context=context_lcl)
            diff_limit = None if c.fulldiff else self.cut_off_limit
            file_diff_data = []
            if method == 'show':
                diff_processor = diffs.DiffProcessor(
                    raw_diff,
                    vcs=c.db_repo_scm_instance.alias,
                    diff_limit=diff_limit)
                c.limited_diff = diff_processor.limited_diff
                for f in diff_processor.parsed:
                    st = f['stats']
                    c.lines_added += st['added']
                    c.lines_deleted += st['deleted']
                    filename = f['filename']
                    fid = h.FID(changeset.raw_id, filename)
                    url_fid = h.FID('', filename)
                    html_diff = diffs.as_html(enable_comments=enable_comments,
                                              parsed_lines=[f])
                    file_diff_data.append(
                        (fid, url_fid, f['operation'], f['old_filename'],
                         filename, html_diff, st))
            else:
                # downloads/raw we only need RAW diff nothing else
                file_diff_data.append(('', None, None, None, raw_diff, None))
            c.changes[changeset.raw_id] = (cs1, cs2, file_diff_data)

        # sort comments in creation order
        c.comments = [com for com_id, com in sorted(comments.items())]

        # 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])
            c.changeset_graft_source_hash = ascii_str(
                c.changeset.extra.get(b'source', b''))
            c.changeset_transplant_source_hash = ascii_str(
                binascii.hexlify(
                    c.changeset.extra.get(b'transplant_source', b'')))
        if method == 'download':
            response.content_type = 'text/plain'
            response.content_disposition = 'attachment; filename=%s.diff' \
                                            % revision[:12]
            return raw_diff
        elif method == 'patch':
            response.content_type = 'text/plain'
            c.diff = safe_str(raw_diff)
            return render('changeset/patch_changeset.html')
        elif method == 'raw':
            response.content_type = 'text/plain'
            return raw_diff
        elif method == 'show':
            if len(c.cs_ranges) == 1:
                return render('changeset/changeset.html')
            else:
                c.cs_ranges_org = None
                c.cs_comments = {}
                revs = [ctx.revision for ctx in reversed(c.cs_ranges)]
                c.jsdata = graph_data(c.db_repo_scm_instance, revs)
                return render('changeset/changeset_range.html')
Esempio n. 4
0
    def show(self, repo_name, pull_request_id, extra=None):
        repo_model = RepoModel()
        c.users_array = repo_model.get_users_js()
        c.user_groups_array = repo_model.get_user_groups_js()
        c.pull_request = PullRequest.get_or_404(pull_request_id)
        c.allowed_to_change_status = self._get_is_allowed_change_status(c.pull_request)
        cc_model = ChangesetCommentsModel()
        cs_model = ChangesetStatusModel()

        # pull_requests repo_name we opened it against
        # ie. other_repo must match
        if repo_name != c.pull_request.other_repo.repo_name:
            raise HTTPNotFound

        # load compare data into template context
        c.cs_repo = c.pull_request.org_repo
        (c.cs_ref_type,
         c.cs_ref_name,
         c.cs_rev) = c.pull_request.org_ref.split(':')

        c.a_repo = c.pull_request.other_repo
        (c.a_ref_type,
         c.a_ref_name,
         c.a_rev) = c.pull_request.other_ref.split(':') # a_rev is ancestor

        org_scm_instance = c.cs_repo.scm_instance # property with expensive cache invalidation check!!!
        try:
            c.cs_ranges = [org_scm_instance.get_changeset(x)
                           for x in c.pull_request.revisions]
        except ChangesetDoesNotExistError:
            c.cs_ranges = []
            h.flash(_('Revision %s not found in %s') % (x, c.cs_repo.repo_name),
                'error')
        c.cs_ranges_org = None # not stored and not important and moving target - could be calculated ...
        revs = [ctx.revision for ctx in reversed(c.cs_ranges)]
        c.jsdata = graph_data(org_scm_instance, revs)

        c.is_range = False
        try:
            if c.a_ref_type == 'rev': # this looks like a free range where target is ancestor
                cs_a = org_scm_instance.get_changeset(c.a_rev)
                root_parents = c.cs_ranges[0].parents
                c.is_range = cs_a in root_parents
                #c.merge_root = len(root_parents) > 1 # a range starting with a merge might deserve a warning
        except ChangesetDoesNotExistError: # probably because c.a_rev not found
            pass
        except IndexError: # probably because c.cs_ranges is empty, probably because revisions are missing
            pass

        avail_revs = set()
        avail_show = []
        c.cs_branch_name = c.cs_ref_name
        c.a_branch_name = None
        other_scm_instance = c.a_repo.scm_instance
        c.update_msg = ""
        c.update_msg_other = ""
        try:
            if not c.cs_ranges:
                c.update_msg = _('Error: changesets not found when displaying pull request from %s.') % c.cs_rev
            elif org_scm_instance.alias == 'hg' and c.a_ref_name != 'ancestor':
                if c.cs_ref_type != 'branch':
                    c.cs_branch_name = org_scm_instance.get_changeset(c.cs_ref_name).branch # use ref_type ?
                c.a_branch_name = c.a_ref_name
                if c.a_ref_type != 'branch':
                    try:
                        c.a_branch_name = other_scm_instance.get_changeset(c.a_ref_name).branch # use ref_type ?
                    except EmptyRepositoryError:
                        c.a_branch_name = 'null' # not a branch name ... but close enough
                # candidates: descendants of old head that are on the right branch
                #             and not are the old head itself ...
                #             and nothing at all if old head is a descendant of target ref name
                if not c.is_range and other_scm_instance._repo.revs('present(%s)::&%s', c.cs_ranges[-1].raw_id, c.a_branch_name):
                    c.update_msg = _('This pull request has already been merged to %s.') % c.a_branch_name
                elif c.pull_request.is_closed():
                    c.update_msg = _('This pull request has been closed and can not be updated.')
                else: # look for descendants of PR head on source branch in org repo
                    avail_revs = org_scm_instance._repo.revs('%s:: & branch(%s)',
                                                             revs[0], c.cs_branch_name)
                    if len(avail_revs) > 1: # more than just revs[0]
                        # also show changesets that not are descendants but would be merged in
                        targethead = other_scm_instance.get_changeset(c.a_branch_name).raw_id
                        if org_scm_instance.path != other_scm_instance.path:
                            # Note: org_scm_instance.path must come first so all
                            # valid revision numbers are 100% org_scm compatible
                            # - both for avail_revs and for revset results
                            hgrepo = unionrepo.unionrepository(org_scm_instance.baseui,
                                                               org_scm_instance.path,
                                                               other_scm_instance.path)
                        else:
                            hgrepo = org_scm_instance._repo
                        show = set(hgrepo.revs('::%ld & !::parents(%s) & !::%s',
                                               avail_revs, revs[0], targethead))
                        c.update_msg = _('The following additional changes are available on %s:') % c.cs_branch_name
                    else:
                        show = set()
                        avail_revs = set() # drop revs[0]
                        c.update_msg = _('No additional changesets found for iterating on this pull request.')

                    # TODO: handle branch heads that not are tip-most
                    brevs = org_scm_instance._repo.revs('%s - %ld - %s', c.cs_branch_name, avail_revs, revs[0])
                    if brevs:
                        # also show changesets that are on branch but neither ancestors nor descendants
                        show.update(org_scm_instance._repo.revs('::%ld - ::%ld - ::%s', brevs, avail_revs, c.a_branch_name))
                        show.add(revs[0]) # make sure graph shows this so we can see how they relate
                        c.update_msg_other = _('Note: Branch %s has another head: %s.') % (c.cs_branch_name,
                            h.short_id(org_scm_instance.get_changeset((max(brevs))).raw_id))

                    avail_show = sorted(show, reverse=True)

            elif org_scm_instance.alias == 'git':
                c.cs_repo.scm_instance.get_changeset(c.cs_rev) # check it exists - raise ChangesetDoesNotExistError if not
                c.update_msg = _("Git pull requests don't support iterating yet.")
        except ChangesetDoesNotExistError:
            c.update_msg = _('Error: some changesets not found when displaying pull request from %s.') % c.cs_rev

        c.avail_revs = avail_revs
        c.avail_cs = [org_scm_instance.get_changeset(r) for r in avail_show]
        c.avail_jsdata = graph_data(org_scm_instance, avail_show)

        raw_ids = [x.raw_id for x in c.cs_ranges]
        c.cs_comments = c.cs_repo.get_comments(raw_ids)
        c.statuses = c.cs_repo.statuses(raw_ids)

        ignore_whitespace = request.GET.get('ignorews') == '1'
        line_context = safe_int(request.GET.get('context'), 3)
        c.ignorews_url = _ignorews_url
        c.context_url = _context_url
        c.fulldiff = request.GET.get('fulldiff')
        diff_limit = self.cut_off_limit if not c.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',
                  c.a_rev, c.cs_rev, org_scm_instance.path)
        try:
            txtdiff = org_scm_instance.get_diff(rev1=safe_str(c.a_rev), rev2=safe_str(c.cs_rev),
                                                ignore_whitespace=ignore_whitespace,
                                                context=line_context)
        except ChangesetDoesNotExistError:
            txtdiff =  _("The diff can't be shown - the PR revisions could not be found.")
        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.file_diff_data = []
        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']
            filename = f['filename']
            fid = h.FID('', filename)
            diff = diff_processor.as_html(enable_comments=True,
                                          parsed_lines=[f])
            c.file_diff_data.append((fid, None, f['operation'], f['old_filename'], filename, diff, st))

        # inline comments
        c.inline_cnt = 0
        c.inline_comments = cc_model.get_inline_comments(
                                c.db_repo.repo_id,
                                pull_request=pull_request_id)
        # count inline comments
        for __, lines in c.inline_comments:
            for comments in lines.values():
                c.inline_cnt += len(comments)
        # comments
        c.comments = cc_model.get_comments(c.db_repo.repo_id,
                                           pull_request=pull_request_id)

        # (badly named) pull-request status calculation based on reviewer votes
        (c.pull_request_reviewers,
         c.pull_request_pending_reviewers,
         c.current_voting_result,
         ) = cs_model.calculate_pull_request_result(c.pull_request)
        c.changeset_statuses = ChangesetStatus.STATUSES

        c.is_ajax_preview = False
        c.ancestors = None # [c.a_rev] ... but that is shown in an other way
        return render('/pullrequests/pullrequest_show.html')
Esempio n. 5
0
    def compare(self, repo_name, org_ref_type, org_ref_name, other_ref_type,
                other_ref_name):
        org_repo = c.db_repo.repo_name
        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_type,
                           org_ref_name=other_ref_name,
                           other_repo=org_repo,
                           other_ref_type=org_ref_type,
                           other_ref_name=org_ref_name,
                           merge=merge or '')

        # set callbacks for generating markup for icons
        c.ignorews_url = _ignorews_url
        c.context_url = _context_url
        ignore_whitespace = request.GET.get('ignorews') == '1'
        line_context = request.GET.get('context', 3)

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

        if org_repo is None:
            msg = 'Could not find org repo %s' % org_repo
            log.error(msg)
            h.flash(msg, category='error')
            return redirect(url('compare_home', repo_name=c.repo_name))

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

        if org_repo.scm_instance.alias != other_repo.scm_instance.alias:
            msg = 'compare of two different kind of remote repos not available'
            log.error(msg)
            h.flash(msg, category='error')
            return redirect(url('compare_home', repo_name=c.repo_name))

        c.a_rev = self._get_ref_rev(org_repo,
                                    org_ref_type,
                                    org_ref_name,
                                    returnempty=True)
        c.cs_rev = self._get_ref_rev(other_repo, other_ref_type,
                                     other_ref_name)

        c.compare_home = False
        c.a_repo = org_repo
        c.a_ref_name = org_ref_name
        c.a_ref_type = org_ref_type
        c.cs_repo = other_repo
        c.cs_ref_name = other_ref_name
        c.cs_ref_type = other_ref_type

        c.cs_ranges, c.cs_ranges_org, c.ancestor = self._get_changesets(
            org_repo.scm_instance.alias, org_repo.scm_instance, c.a_rev,
            other_repo.scm_instance, c.cs_rev)
        raw_ids = [x.raw_id for x in c.cs_ranges]
        c.cs_comments = other_repo.get_comments(raw_ids)
        c.statuses = other_repo.statuses(raw_ids)

        revs = [ctx.revision for ctx in reversed(c.cs_ranges)]
        c.jsdata = json.dumps(graph_data(c.cs_repo.scm_instance, revs))

        if partial:
            return render('compare/compare_cs.html')
        if merge and c.ancestor:
            # 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_rev)
            log.debug('Using ancestor %s as rev1 instead of %s' %
                      (c.ancestor, c.a_rev))
            rev1 = c.ancestor
            org_repo = other_repo
        else:  # comparing tips, not necessarily linearly related
            if merge:
                log.error('Unable to find ancestor revision')
            if org_repo != other_repo:
                # TODO: we could do this by using hg unionrepo
                log.error('cannot compare across repos %s and %s', org_repo,
                          other_repo)
                h.flash(_(
                    'Cannot compare repositories without using common ancestor'
                ),
                        category='error')
                raise HTTPBadRequest
            rev1 = c.a_rev

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

        log.debug('running diff between %s and %s in %s' %
                  (rev1, c.cs_rev, org_repo.scm_instance.path))
        txtdiff = org_repo.scm_instance.get_diff(
            rev1=rev1,
            rev2=c.cs_rev,
            ignore_whitespace=ignore_whitespace,
            context=line_context)

        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')
Esempio n. 6
0
    def diff(self, repo_name, f_path):
        ignore_whitespace = request.GET.get('ignorews') == '1'
        line_context = safe_int(request.GET.get('context'), 3)
        diff2 = request.GET.get('diff2', '')
        diff1 = request.GET.get('diff1', '') or 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)

            raise HTTPFound(location=_url)
        try:
            if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
                c.changeset_1 = c.db_repo_scm_instance.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.db_repo_scm_instance)
                    node1 = FileNode(f_path, '', changeset=c.changeset_1)
            else:
                c.changeset_1 = EmptyChangeset(repo=c.db_repo_scm_instance)
                node1 = FileNode(f_path, '', changeset=c.changeset_1)

            if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
                c.changeset_2 = c.db_repo_scm_instance.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.db_repo_scm_instance)
                    node2 = FileNode(f_path, '', changeset=c.changeset_2)
            else:
                c.changeset_2 = EmptyChangeset(repo=c.db_repo_scm_instance)
                node2 = FileNode(f_path, '', changeset=c.changeset_2)
        except (RepositoryError, NodeError):
            log.error(traceback.format_exc())
            raise HTTPFound(location=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
            c.a_rev, c.cs_rev, a_path, diff, st, op = 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.file_diff_data = [(fid, fid, op, a_path, node2.path, diff, st)]

            return render('files/file_diff.html')