Changeset - fa4ef7f0f440
[Not reviewed]
default
0 2 0
Mads Kiilerich - 12 years ago 2013-06-28 11:50:13
madski@unity3d.com
compare: calculate changesets and calculate and show ancestor also for non-merge diffs
2 files changed with 16 insertions and 20 deletions:
0 comments (0 inline, 0 general)
kallithea/controllers/compare.py
Show inline comments
 
@@ -53,141 +53,133 @@ log = logging.getLogger(__name__)
 
class CompareController(BaseRepoController):
 

	
 
    def __before__(self):
 
        super(CompareController, self).__before__()
 

	
 
    def __get_rev_or_redirect(self, ref, repo, redirect_after=True,
 
                              partial=False):
 
        """
 
        Safe way to get changeset if error occur it redirects to changeset with
 
        proper message. If partial is set then don't do redirect raise Exception
 
        instead
 

	
 
        :param ref:
 
        :param repo:
 
        :param redirect_after:
 
        :param partial:
 
        """
 
        rev = ref[1] # default and used for git
 
        if repo.scm_instance.alias == 'hg':
 
            # lookup up the exact node id
 
            _revset_predicates = {
 
                    'branch': 'branch',
 
                    'book': 'bookmark',
 
                    'tag': 'tag',
 
                    'rev': 'id',
 
                }
 
            rev_spec = "max(%s(%%s))" % _revset_predicates[ref[0]]
 
            revs = repo.scm_instance._repo.revs(rev_spec, safe_str(ref[1]))
 
            if revs:
 
                rev = revs[-1]
 
            # else: TODO: just report 'not found'
 

	
 
        try:
 
            return repo.scm_instance.get_changeset(rev).raw_id
 
        except EmptyRepositoryError, e:
 
            if not redirect_after:
 
                return None
 
            h.flash(h.literal(_('There are no changesets yet')),
 
                    category='warning')
 
            redirect(url('summary_home', repo_name=repo.repo_name))
 

	
 
        except RepositoryError, e:
 
            log.error(traceback.format_exc())
 
            h.flash(safe_str(e), category='warning')
 
            if not partial:
 
                redirect(h.url('summary_home', repo_name=repo.repo_name))
 
            raise HTTPBadRequest()
 

	
 
    def _get_changesets(self, alias, org_repo, org_rev, other_repo, other_rev, merge):
 
    def _get_changesets(self, alias, org_repo, org_rev, other_repo, other_rev):
 
        """
 
        Returns a list of changesets that can be merged from org_repo at org_rev
 
        to other_repo at other_rev ... and the ancestor that would be used for merge.
 

	
 
        :param org_repo: repo object, that is most likely the orginal repo we forked from
 
        :param org_rev: the revision we want our compare to be made
 
        :param other_repo: repo object, mostl likely the fork of org_repo. It hass
 
            all changesets that we need to obtain
 
        :param other_rev: revision we want out compare to be made on other_repo
 

	
 
        """
 
        ancestor = None
 
        if org_rev == other_rev:
 
            changesets = []
 
            if merge:
 
                ancestor = org_rev
 
            ancestor = org_rev
 

	
 
        elif alias == 'hg':
 
            #case two independent repos
 
            if org_repo != other_repo:
 
                hgrepo = unionrepo.unionrepository(other_repo.baseui,
 
                                                   other_repo.path,
 
                                                   org_repo.path)
 
                # all the changesets we are looking for will be in other_repo,
 
                # so rev numbers from hgrepo can be used in other_repo
 

	
 
            #no remote compare do it on the same repository
 
            else:
 
                hgrepo = other_repo._repo
 

	
 
            if merge:
 
                revs = hgrepo.revs(
 
                    "ancestors(id(%s)) and not ancestors(id(%s)) and not id(%s)",
 
                    other_rev, org_rev, org_rev)
 
            ancestors = hgrepo.revs("ancestor(id(%s), id(%s))", org_rev, other_rev)
 
            if ancestors:
 
                # pick arbitrary ancestor - but there is usually only one
 
                ancestor = hgrepo[ancestors[0]].hex()
 

	
 
                ancestors = hgrepo.revs("ancestor(id(%s), id(%s))", org_rev,
 
                                        other_rev)
 
                if ancestors:
 
                    # pick arbitrary ancestor - but there is usually only one
 
                    ancestor = hgrepo[ancestors[0]].hex()
 
            else:
 
                # TODO: have both + and - changesets
 
                revs = hgrepo.revs("id(%s) :: id(%s) - id(%s)",
 
                                   org_rev, other_rev, org_rev)
 
            # TODO: have both + and - changesets
 
            revs = hgrepo.revs("ancestors(id(%s)) and not ancestors(id(%s)) and not id(%s)",
 
                               other_rev, org_rev, org_rev)
 

	
 
            changesets = [other_repo.get_changeset(rev) for rev in revs]
 

	
 
        elif alias == 'git':
 
            if org_repo != other_repo:
 
                from dulwich.repo import Repo
 
                from dulwich.client import SubprocessGitClient
 

	
 
                gitrepo = Repo(org_repo.path)
 
                SubprocessGitClient(thin_packs=False).fetch(other_repo.path, gitrepo)
 

	
 
                gitrepo_remote = Repo(other_repo.path)
 
                SubprocessGitClient(thin_packs=False).fetch(org_repo.path, gitrepo_remote)
 

	
 
                revs = []
 
                for x in gitrepo_remote.get_walker(include=[other_rev],
 
                                                   exclude=[org_rev]):
 
                    revs.append(x.commit.id)
 

	
 
                changesets = [other_repo.get_changeset(rev) for rev in reversed(revs)]
 
                if changesets:
 
                    ancestor = changesets[0].parents[0].raw_id
 
                else:
 
                    # no changesets from other repo, ancestor is the other_rev
 
                    ancestor = other_rev
 

	
 
            else:
 
                so, se = org_repo.run_git_command(
 
                    'log --reverse --pretty="format: %%H" -s %s..%s'
 
                        % (org_rev, other_rev)
 
                )
 
                changesets = [org_repo.get_changeset(cs)
 
                              for cs in re.findall(r'[0-9a-fA-F]{40}', so)]
 

	
 
        else:
 
            raise Exception('Bad alias only git and hg is allowed')
 

	
 
        return changesets, ancestor
 

	
 
    @LoginRequired()
 
    @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
 
                                   'repository.admin')
 
    def index(self, repo_name):
 
        c.compare_home = True
 
        org_repo = c.db_repo.repo_name
 
        other_repo = request.GET.get('other_repo', org_repo)
 
        c.org_repo = Repository.get_by_repo_name(org_repo)
 
        c.other_repo = Repository.get_by_repo_name(other_repo)
 
@@ -216,100 +208,99 @@ class CompareController(BaseRepoControll
 
        # 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 '')
 

	
 
        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.org_rev = self.__get_rev_or_redirect(ref=org_ref, repo=org_repo, partial=partial)
 
        c.other_rev = self.__get_rev_or_redirect(ref=other_ref, repo=other_repo, partial=partial)
 

	
 
        c.compare_home = False
 
        c.org_repo = org_repo
 
        c.other_repo = other_repo
 
        c.org_ref_name = org_ref_name
 
        c.other_ref_name = other_ref_name
 
        c.org_ref_type = org_ref_type
 
        c.other_ref_type = other_ref_type
 

	
 
        c.cs_ranges, c.ancestor = self._get_changesets(
 
            org_repo.scm_instance.alias, org_repo.scm_instance, c.org_rev,
 
            other_repo.scm_instance, c.other_rev, merge)
 
            other_repo.scm_instance, c.other_rev)
 
        c.statuses = c.db_repo.statuses(
 
            [x.raw_id for x in c.cs_ranges])
 

	
 
        if partial:
 
            return render('compare/compare_cs.html')
 
        if c.ancestor:
 
            assert merge
 
        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.org_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:
 
                log.error('cannot compare across repos %s and %s', org_repo, other_repo)
 
                raise HTTPNotFound
 
            rev1 = c.org_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.other_rev, org_repo.scm_instance.path))
 
        txtdiff = org_repo.scm_instance.get_diff(rev1=rev1, rev2=c.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')
kallithea/templates/compare/compare_cs.html
Show inline comments
 
## Changesets table !
 
<div class="container">
 
  %if not c.cs_ranges:
 
    <span class="empty_data">${_('No changesets')}</span>
 
  %else:
 

	
 
    %if c.ancestor:
 
    <div class="ancestor">${_('Ancestor')}:
 
      ${h.link_to(h.short_id(c.ancestor),h.url('changeset_home',repo_name=c.repo_name,revision=c.ancestor))}
 
    </div>
 
    %endif
 

	
 
    <table class="compare_view_commits noborder">
 
    %for cs in reversed(c.cs_ranges):
 
        <tr id="row-${cs.raw_id}">
 
        <td style="width:50px">
 
          %if cs.raw_id in c.statuses:
 
            <div title="${_('Changeset status: %s') % c.statuses[cs.raw_id][1]}" class="changeset-status-ico">
 
                <img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses[cs.raw_id][0])}" />
 
            </div>
 
          %endif
 
        </td>
 
        <td style="width: 140px"><span class="tooltip" title="${h.tooltip(h.age(cs.date))}">${cs.date}</span></td>
 
        <td><div class="gravatar" commit_id="${cs.raw_id}"><img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),14)}"/></div></td>
 
        <td><div class="author">${h.person(cs.author)}</div></td>
 
        <td>${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.other_repo.repo_name,revision=cs.raw_id))}
 
        %if c.as_form:
 
          ${h.hidden('revisions',cs.raw_id)}
 
        %endif
 
        </td>
 
        <td>
 
        %if cs.branch:
 
        <span class="branchtag">${cs.branch}</span>
 
        %endif
 
        </td>
 
        <td class="expand_commit" commit_id="${cs.raw_id}" title="${_('Expand commit message')}">
 
            <i class="icon-resize-vertical" style="color:#DDD"></i>
 
        </td>
 
        <td><div id="C-${cs.raw_id}" class="message" style="white-space:normal; height:1.1em; padding:0">${h.urlify_commit(cs.message, c.repo_name)}</div></td>
 
        </tr>
 
    %endfor
 
    </table>
 
    %if c.ancestor:
 
      <div class="ancestor">${_('Ancestor')}:
 
        ${h.link_to(h.short_id(c.ancestor),h.url('changeset_home',repo_name=c.repo_name,revision=c.ancestor))}
 
      </div>
 
    %endif
 
    %if c.as_form:
 
      ${h.hidden('ancestor_rev',c.ancestor)}
 
      ${h.hidden('merge_rev',c.cs_ranges[-1].raw_id)}
 
    %endif
 
  %endif
 
</div>
 

	
 
<script>
 
$('.expand_commit').on('click',function(e){
 
    $(this).children('i').hide();
 
    var cid = $(this).attr('commit_id');
 
    $('#C-'+cid).css({'height': 'auto','width': 'auto', 'white-space': 'pre'})
 
});
 
$('.gravatar').on('click',function(e){
 
    var cid = $(this).attr('commit_id');
 
    $('#row-'+cid).toggleClass('hl', !$('#row-'+cid).hasClass('hl'));
 
});
 
</script>
0 comments (0 inline, 0 general)