コード例 #1
0
ファイル: changeset.py プロジェクト: adamscieszko/rhodecode
    def get_file_history(self, path, limit=None):
        """
        Returns history of file as reversed list of ``Changeset`` objects for
        which file at given ``path`` has been modified.

        TODO: This function now uses os underlying 'git' and 'grep' commands
        which is generally not good. Should be replaced with algorithm
        iterating commits.
        """
        self._get_filectx(path)
        cs_id = safe_str(self.id)
        f_path = safe_str(path)

        if limit:
            cmd = 'log -n %s --pretty="format: %%H" -s -p %s -- "%s"' % (
                      safe_int(limit, 0), cs_id, f_path
                   )

        else:
            cmd = 'log --pretty="format: %%H" -s -p %s -- "%s"' % (
                      cs_id, f_path
                   )
        so, se = self.repository.run_git_command(cmd)
        ids = re.findall(r'[0-9a-fA-F]{40}', so)
        return [self.repository.get_changeset(id) for id in ids]
コード例 #2
0
    def _get_id_for_path(self, path):
        path = safe_str(path)
        # FIXME: Please, spare a couple of minutes and make those codes cleaner;
        if not path in self._paths:
            path = path.strip('/')
            # set root tree
            tree = self.repository._repo[self._tree_id]
            if path == '':
                self._paths[''] = tree.id
                return tree.id
            splitted = path.split('/')
            dirs, name = splitted[:-1], splitted[-1]
            curdir = ''

            # initially extract things from root dir
            for item, stat, id in tree.iteritems():
                if curdir:
                    name = '/'.join((curdir, item))
                else:
                    name = item
                self._paths[name] = id
                self._stat_modes[name] = stat

            for dir in dirs:
                if curdir:
                    curdir = '/'.join((curdir, dir))
                else:
                    curdir = dir
                dir_id = None
                for item, stat, id in tree.iteritems():
                    if dir == item:
                        dir_id = id
                if dir_id:
                    # Update tree
                    tree = self.repository._repo[dir_id]
                    if not isinstance(tree, objects.Tree):
                        raise ChangesetError('%s is not a directory' % curdir)
                else:
                    raise ChangesetError('%s have not been found' % curdir)

                # cache all items from the given traversed tree
                for item, stat, id in tree.iteritems():
                    if curdir:
                        name = '/'.join((curdir, item))
                    else:
                        name = item
                    self._paths[name] = id
                    self._stat_modes[name] = stat
            if not path in self._paths:
                raise NodeDoesNotExistError(
                    "There is no file nor directory "
                    "at the given path '%s' at revision %s" %
                    (path, safe_str(self.short_id)))
        return self._paths[path]
コード例 #3
0
ファイル: changeset.py プロジェクト: adamscieszko/rhodecode
    def _get_id_for_path(self, path):
        path = safe_str(path)
        # FIXME: Please, spare a couple of minutes and make those codes cleaner;
        if not path in self._paths:
            path = path.strip('/')
            # set root tree
            tree = self.repository._repo[self._tree_id]
            if path == '':
                self._paths[''] = tree.id
                return tree.id
            splitted = path.split('/')
            dirs, name = splitted[:-1], splitted[-1]
            curdir = ''

            # initially extract things from root dir
            for item, stat, id in tree.iteritems():
                if curdir:
                    name = '/'.join((curdir, item))
                else:
                    name = item
                self._paths[name] = id
                self._stat_modes[name] = stat

            for dir in dirs:
                if curdir:
                    curdir = '/'.join((curdir, dir))
                else:
                    curdir = dir
                dir_id = None
                for item, stat, id in tree.iteritems():
                    if dir == item:
                        dir_id = id
                if dir_id:
                    # Update tree
                    tree = self.repository._repo[dir_id]
                    if not isinstance(tree, objects.Tree):
                        raise ChangesetError('%s is not a directory' % curdir)
                else:
                    raise ChangesetError('%s have not been found' % curdir)

                # cache all items from the given traversed tree
                for item, stat, id in tree.iteritems():
                    if curdir:
                        name = '/'.join((curdir, item))
                    else:
                        name = item
                    self._paths[name] = id
                    self._stat_modes[name] = stat
            if not path in self._paths:
                raise NodeDoesNotExistError("There is no file nor directory "
                    "at the given path '%s' at revision %s"
                    % (path, safe_str(self.short_id)))
        return self._paths[path]
コード例 #4
0
ファイル: compare.py プロジェクト: adamscieszko/rhodecode
    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 rev: revision to fetch
        :param repo: repo instance
        """

        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))
コード例 #5
0
ファイル: changeset.py プロジェクト: adamscieszko/rhodecode
 def get_file_mode(self, path):
     """
     Returns stat mode of the file at the given ``path``.
     """
     # ensure path is traversed
     path = safe_str(path)
     self._get_id_for_path(path)
     return self._stat_modes[path]
コード例 #6
0
 def get_file_mode(self, path):
     """
     Returns stat mode of the file at the given ``path``.
     """
     # ensure path is traversed
     path = safe_str(path)
     self._get_id_for_path(path)
     return self._stat_modes[path]
コード例 #7
0
    def _fix_path(self, path):
        """
        Paths are stored without trailing slash so we need to get rid off it if
        needed. Also mercurial keeps filenodes as str so we need to decode
        from unicode to str
        """
        if path.endswith('/'):
            path = path.rstrip('/')

        return safe_str(path)
コード例 #8
0
ファイル: nodes.py プロジェクト: adamscieszko/rhodecode
 def __init__(self, path, kind):
     if path.startswith('/'):
         raise NodeError("Cannot initialize Node objects with slash at "
                         "the beginning as only relative paths are supported")
     self.path = safe_str(path.rstrip('/'))  # we store paths as str
     if path == '' and kind != NodeKind.DIR:
         raise NodeError("Only DirNode and its subclasses may be "
                         "initialized with empty path")
     self.kind = kind
     #self.dirs, self.files = [], []
     if self.is_root() and not self.is_dir():
         raise NodeError("Root node cannot be FILE kind")
コード例 #9
0
 def __init__(self, path, kind):
     if path.startswith('/'):
         raise NodeError(
             "Cannot initialize Node objects with slash at "
             "the beginning as only relative paths are supported")
     self.path = safe_str(path.rstrip('/'))  # we store paths as str
     if path == '' and kind != NodeKind.DIR:
         raise NodeError("Only DirNode and its subclasses may be "
                         "initialized with empty path")
     self.kind = kind
     #self.dirs, self.files = [], []
     if self.is_root() and not self.is_dir():
         raise NodeError("Root node cannot be FILE kind")
コード例 #10
0
    def get_file_history(self, path, limit=None):
        """
        Returns history of file as reversed list of ``Changeset`` objects for
        which file at given ``path`` has been modified.

        TODO: This function now uses os underlying 'git' and 'grep' commands
        which is generally not good. Should be replaced with algorithm
        iterating commits.
        """
        self._get_filectx(path)
        cs_id = safe_str(self.id)
        f_path = safe_str(path)

        if limit:
            cmd = 'log -n %s --pretty="format: %%H" -s -p %s -- "%s"' % (
                safe_int(limit, 0), cs_id, f_path)

        else:
            cmd = 'log --pretty="format: %%H" -s -p %s -- "%s"' % (cs_id,
                                                                   f_path)
        so, se = self.repository.run_git_command(cmd)
        ids = re.findall(r'[0-9a-fA-F]{40}', so)
        return [self.repository.get_changeset(id) for id in ids]
コード例 #11
0
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 rev: revision to fetch
        :param repo: repo instance
        """

        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()
コード例 #12
0
ファイル: inmemory.py プロジェクト: yujiro/rhodecode
    def commit(self, message, author, parents=None, branch=None, date=None,
               **kwargs):
        """
        Performs in-memory commit (doesn't check workdir in any way) and
        returns newly created ``Changeset``. Updates repository's
        ``revisions``.

        :param message: message of the commit
        :param author: full username, i.e. "Joe Doe <*****@*****.**>"
        :param parents: single parent or sequence of parents from which commit
          would be derieved
        :param date: ``datetime.datetime`` instance. Defaults to
          ``datetime.datetime.now()``.
        :param branch: branch name, as string. If none given, default backend's
          branch would be used.

        :raises ``CommitError``: if any error occurs while committing
        """
        self.check_integrity(parents)

        from .repository import GitRepository
        if branch is None:
            branch = GitRepository.DEFAULT_BRANCH_NAME

        repo = self.repository._repo
        object_store = repo.object_store

        ENCODING = "UTF-8"
        DIRMOD = 040000

        # Create tree and populates it with blobs
        commit_tree = self.parents[0] and repo[self.parents[0]._commit.tree] or\
            objects.Tree()
        for node in self.added + self.changed:
            # Compute subdirs if needed
            dirpath, nodename = posixpath.split(node.path)
            dirnames = dirpath and dirpath.split('/') or []
            parent = commit_tree
            ancestors = [('', parent)]

            # Tries to dig for the deepest existing tree
            while dirnames:
                curdir = dirnames.pop(0)
                try:
                    dir_id = parent[curdir][1]
                except KeyError:
                    # put curdir back into dirnames and stops
                    dirnames.insert(0, curdir)
                    break
                else:
                    # If found, updates parent
                    parent = self.repository._repo[dir_id]
                    ancestors.append((curdir, parent))
            # Now parent is deepest existing tree and we need to create subtrees
            # for dirnames (in reverse order) [this only applies for nodes from added]
            new_trees = []

            if not node.is_binary:
                content = node.content.encode(ENCODING)
            else:
                content = node.content
            blob = objects.Blob.from_string(content)

            node_path = node.name.encode(ENCODING)
            if dirnames:
                # If there are trees which should be created we need to build
                # them now (in reverse order)
                reversed_dirnames = list(reversed(dirnames))
                curtree = objects.Tree()
                curtree[node_path] = node.mode, blob.id
                new_trees.append(curtree)
                for dirname in reversed_dirnames[:-1]:
                    newtree = objects.Tree()
                    #newtree.add(DIRMOD, dirname, curtree.id)
                    newtree[dirname] = DIRMOD, curtree.id
                    new_trees.append(newtree)
                    curtree = newtree
                parent[reversed_dirnames[-1]] = DIRMOD, curtree.id
            else:
                parent.add(name=node_path, mode=node.mode, hexsha=blob.id)

            new_trees.append(parent)
            # Update ancestors
            for parent, tree, path in reversed([(a[1], b[1], b[0]) for a, b in
                zip(ancestors, ancestors[1:])]):
                parent[path] = DIRMOD, tree.id
                object_store.add_object(tree)

            object_store.add_object(blob)
            for tree in new_trees:
                object_store.add_object(tree)
        for node in self.removed:
            paths = node.path.split('/')
            tree = commit_tree
            trees = [tree]
            # Traverse deep into the forest...
            for path in paths:
                try:
                    obj = self.repository._repo[tree[path][1]]
                    if isinstance(obj, objects.Tree):
                        trees.append(obj)
                        tree = obj
                except KeyError:
                    break
            # Cut down the blob and all rotten trees on the way back...
            for path, tree in reversed(zip(paths, trees)):
                del tree[path]
                if tree:
                    # This tree still has elements - don't remove it or any
                    # of it's parents
                    break

        object_store.add_object(commit_tree)

        # Create commit
        commit = objects.Commit()
        commit.tree = commit_tree.id
        commit.parents = [p._commit.id for p in self.parents if p]
        commit.author = commit.committer = safe_str(author)
        commit.encoding = ENCODING
        commit.message = safe_str(message)

        # Compute date
        if date is None:
            date = time.time()
        elif isinstance(date, datetime.datetime):
            date = time.mktime(date.timetuple())

        author_time = kwargs.pop('author_time', date)
        commit.commit_time = int(date)
        commit.author_time = int(author_time)
        tz = time.timezone
        author_tz = kwargs.pop('author_timezone', tz)
        commit.commit_timezone = tz
        commit.author_timezone = author_tz

        object_store.add_object(commit)

        ref = 'refs/heads/%s' % branch
        repo.refs[ref] = commit.id
        repo.refs.set_symbolic_ref('HEAD', ref)

        # Update vcs repository object & recreate dulwich repo
        self.repository.revisions.append(commit.id)
        self.repository._repo = Repo(self.repository.path)
        # invalidate parsed refs after commit
        self.repository._parsed_refs = self.repository._get_parsed_refs()
        tip = self.repository.get_changeset()
        self.reset()
        return tip
コード例 #13
0
    def commit(self,
               message,
               author,
               parents=None,
               branch=None,
               date=None,
               **kwargs):
        """
        Performs in-memory commit (doesn't check workdir in any way) and
        returns newly created ``Changeset``. Updates repository's
        ``revisions``.

        :param message: message of the commit
        :param author: full username, i.e. "Joe Doe <*****@*****.**>"
        :param parents: single parent or sequence of parents from which commit
          would be derieved
        :param date: ``datetime.datetime`` instance. Defaults to
          ``datetime.datetime.now()``.
        :param branch: branch name, as string. If none given, default backend's
          branch would be used.

        :raises ``CommitError``: if any error occurs while committing
        """
        self.check_integrity(parents)

        from .repository import GitRepository
        if branch is None:
            branch = GitRepository.DEFAULT_BRANCH_NAME

        repo = self.repository._repo
        object_store = repo.object_store

        ENCODING = "UTF-8"
        DIRMOD = 040000

        # Create tree and populates it with blobs
        commit_tree = self.parents[0] and repo[self.parents[0]._commit.tree] or\
            objects.Tree()
        for node in self.added + self.changed:
            # Compute subdirs if needed
            dirpath, nodename = posixpath.split(node.path)
            dirnames = dirpath and dirpath.split('/') or []
            parent = commit_tree
            ancestors = [('', parent)]

            # Tries to dig for the deepest existing tree
            while dirnames:
                curdir = dirnames.pop(0)
                try:
                    dir_id = parent[curdir][1]
                except KeyError:
                    # put curdir back into dirnames and stops
                    dirnames.insert(0, curdir)
                    break
                else:
                    # If found, updates parent
                    parent = self.repository._repo[dir_id]
                    ancestors.append((curdir, parent))
            # Now parent is deepest existing tree and we need to create subtrees
            # for dirnames (in reverse order) [this only applies for nodes from added]
            new_trees = []

            if not node.is_binary:
                content = node.content.encode(ENCODING)
            else:
                content = node.content
            blob = objects.Blob.from_string(content)

            node_path = node.name.encode(ENCODING)
            if dirnames:
                # If there are trees which should be created we need to build
                # them now (in reverse order)
                reversed_dirnames = list(reversed(dirnames))
                curtree = objects.Tree()
                curtree[node_path] = node.mode, blob.id
                new_trees.append(curtree)
                for dirname in reversed_dirnames[:-1]:
                    newtree = objects.Tree()
                    #newtree.add(DIRMOD, dirname, curtree.id)
                    newtree[dirname] = DIRMOD, curtree.id
                    new_trees.append(newtree)
                    curtree = newtree
                parent[reversed_dirnames[-1]] = DIRMOD, curtree.id
            else:
                parent.add(name=node_path, mode=node.mode, hexsha=blob.id)

            new_trees.append(parent)
            # Update ancestors
            for parent, tree, path in reversed([
                (a[1], b[1], b[0]) for a, b in zip(ancestors, ancestors[1:])
            ]):
                parent[path] = DIRMOD, tree.id
                object_store.add_object(tree)

            object_store.add_object(blob)
            for tree in new_trees:
                object_store.add_object(tree)
        for node in self.removed:
            paths = node.path.split('/')
            tree = commit_tree
            trees = [tree]
            # Traverse deep into the forest...
            for path in paths:
                try:
                    obj = self.repository._repo[tree[path][1]]
                    if isinstance(obj, objects.Tree):
                        trees.append(obj)
                        tree = obj
                except KeyError:
                    break
            # Cut down the blob and all rotten trees on the way back...
            for path, tree in reversed(zip(paths, trees)):
                del tree[path]
                if tree:
                    # This tree still has elements - don't remove it or any
                    # of it's parents
                    break

        object_store.add_object(commit_tree)

        # Create commit
        commit = objects.Commit()
        commit.tree = commit_tree.id
        commit.parents = [p._commit.id for p in self.parents if p]
        commit.author = commit.committer = safe_str(author)
        commit.encoding = ENCODING
        commit.message = safe_str(message)

        # Compute date
        if date is None:
            date = time.time()
        elif isinstance(date, datetime.datetime):
            date = time.mktime(date.timetuple())

        author_time = kwargs.pop('author_time', date)
        commit.commit_time = int(date)
        commit.author_time = int(author_time)
        tz = time.timezone
        author_tz = kwargs.pop('author_timezone', tz)
        commit.commit_timezone = tz
        commit.author_timezone = author_tz

        object_store.add_object(commit)

        ref = 'refs/heads/%s' % branch
        repo.refs[ref] = commit.id
        repo.refs.set_symbolic_ref('HEAD', ref)

        # Update vcs repository object & recreate dulwich repo
        self.repository.revisions.append(commit.id)
        # invalidate parsed refs after commit
        self.repository._parsed_refs = self.repository._get_parsed_refs()
        tip = self.repository.get_changeset()
        self.reset()
        return tip
コード例 #14
0
    def _get_repo_refs(self, repo, rev=None, branch=None, branch_rev=None):
        """return a structure with repo's interesting changesets, suitable for
        the selectors in pullrequest.html

        rev: a revision that must be in the list somehow and selected by default
        branch: a branch that must be in the list and selected by default - even if closed
        branch_rev: a revision of which peers should be preferred and available."""
        # list named branches that has been merged to this named branch - it should probably merge back
        peers = []

        if rev:
            rev = safe_str(rev)

        if branch:
            branch = safe_str(branch)

        if branch_rev:
            branch_rev = safe_str(branch_rev)
            # not restricting to merge() would also get branch point and be better
            # (especially because it would get the branch point) ... but is currently too expensive
            otherbranches = {}
            for i in repo._repo.revs(
                "sort(parents(branch(id(%s)) and merge()) - branch(id(%s)))",
                branch_rev, branch_rev):
                cs = repo.get_changeset(i)
                otherbranches[cs.branch] = cs.raw_id
            for abranch, node in otherbranches.iteritems():
                selected = 'branch:%s:%s' % (abranch, node)
                peers.append((selected, abranch))

        selected = None

        branches = []
        for abranch, branchrev in repo.branches.iteritems():
            n = 'branch:%s:%s' % (abranch, branchrev)
            branches.append((n, abranch))
            if rev == branchrev:
                selected = n
            if branch == abranch:
                selected = n
                branch = None
        if branch: # branch not in list - it is probably closed
            revs = repo._repo.revs('max(branch(%s))', branch)
            if revs:
                cs = repo.get_changeset(revs[0])
                selected = 'branch:%s:%s' % (branch, cs.raw_id)
                branches.append((selected, branch))

        bookmarks = []
        for bookmark, bookmarkrev in repo.bookmarks.iteritems():
            n = 'book:%s:%s' % (bookmark, bookmarkrev)
            bookmarks.append((n, bookmark))
            if rev == bookmarkrev:
                selected = n

        tags = []
        for tag, tagrev in repo.tags.iteritems():
            n = 'tag:%s:%s' % (tag, tagrev)
            tags.append((n, tag))
            if rev == tagrev and tag != 'tip': # tip is not a real tag - and its branch is better
                selected = n

        # prio 1: rev was selected as existing entry above

        # prio 2: create special entry for rev; rev _must_ be used
        specials = []
        if rev and selected is None:
            selected = 'rev:%s:%s' % (rev, rev)
            specials = [(selected, '%s: %s' % (_("Changeset"), rev[:12]))]

        # prio 3: most recent peer branch
        if peers and not selected:
            selected = peers[0][0][0]

        # prio 4: tip revision
        if not selected:
            selected = 'tag:tip:%s' % repo.tags['tip']

        groups = [(specials, _("Special")),
                  (peers, _("Peer branches")),
                  (bookmarks, _("Bookmarks")),
                  (branches, _("Branches")),
                  (tags, _("Tags")),
                  ]
        return [g for g in groups if g[0]], selected
コード例 #15
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

        self.__get_cs_or_redirect(rev=org_ref, repo=org_repo, partial=partial)
        self.__get_cs_or_redirect(rev=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_ref,
                                                       other_repo.scm_instance, other_ref,
                                                       merge)

        c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
                                                   c.cs_ranges])
        if not c.ancestor:
            log.warning('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_ref = ('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_ref, other_ref, org_repo.scm_instance.path))
        txtdiff = org_repo.scm_instance.get_diff(rev1=safe_str(org_ref[1]), rev2=safe_str(other_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']
            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')
コード例 #16
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]
コード例 #17
0
    def _get_changesets(self, alias, org_repo, org_ref, other_repo, other_ref, merge):
        """
        Returns a list of changesets that can be merged from org_repo@org_ref
        to other_repo@other_ref ... and the ancestor that would be used for merge

        :param org_repo:
        :param org_ref:
        :param other_repo:
        :param other_ref:
        :param tmp:
        """

        ancestor = None

        if alias == 'hg':
            # lookup up the exact node id
            _revset_predicates = {
                    'branch': 'branch',
                    'book': 'bookmark',
                    'tag': 'tag',
                    'rev': 'id',
                }

            org_rev_spec = "max(%s(%%s))" % _revset_predicates[org_ref[0]]
            org_revs = org_repo._repo.revs(org_rev_spec, safe_str(org_ref[1]))
            org_rev = org_repo._repo[org_revs[-1] if org_revs else -1].hex()

            other_revs_spec = "max(%s(%%s))" % _revset_predicates[other_ref[0]]
            other_revs = other_repo._repo.revs(other_revs_spec, safe_str(other_ref[1]))
            other_rev = other_repo._repo[other_revs[-1] if other_revs else -1].hex()

            #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()
            else:
                # TODO: have both + and - changesets
                revs = hgrepo.revs("id(%s) :: id(%s) - id(%s)",
                                   org_rev, other_rev, org_rev)

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

        elif alias == 'git':
            if org_repo != other_repo:
                raise Exception('Comparing of different GIT repositories is not'
                                'allowed. Got %s != %s' % (org_repo, other_repo))

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

        return changesets, ancestor
コード例 #18
0
    def _get_changesets(self, alias, org_repo, org_ref, other_repo, other_ref, merge):
        """
        Returns a list of changesets that can be merged from org_repo@org_ref
        to other_repo@other_ref ... and the ancestor that would be used for merge

        :param org_repo:
        :param org_ref:
        :param other_repo:
        :param other_ref:
        :param tmp:
        """

        ancestor = None

        if alias == 'hg':
            # lookup up the exact node id
            _revset_predicates = {
                    'branch': 'branch',
                    'book': 'bookmark',
                    'tag': 'tag',
                    'rev': 'id',
                }

            org_rev_spec = "%s('%s')" % (_revset_predicates[org_ref[0]],
                                         safe_str(org_ref[1]))
            if org_ref[1] == EmptyChangeset().raw_id:
                org_rev = org_ref[1]
            else:
                org_rev = org_repo._repo[scmutil.revrange(org_repo._repo,
                                                          [org_rev_spec])[-1]]
            other_rev_spec = "%s('%s')" % (_revset_predicates[other_ref[0]],
                                           safe_str(other_ref[1]))
            if other_ref[1] == EmptyChangeset().raw_id:
                other_rev = other_ref[1]
            else:
                other_rev = other_repo._repo[scmutil.revrange(other_repo._repo,
                                                        [other_rev_spec])[-1]]

            #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 = ["ancestors(id('%s')) and not ancestors(id('%s')) and not id('%s')" %
                        (other_rev, org_rev, org_rev)]

                ancestors = scmutil.revrange(hgrepo,
                     ["ancestor(id('%s'), id('%s'))" % (org_rev, other_rev)])
                if len(ancestors) == 1:
                    ancestor = hgrepo[ancestors[0]].hex()
            else:
                # TODO: have both + and - changesets
                revs = ["id('%s') :: id('%s') - id('%s')" %
                        (org_rev, other_rev, org_rev)]

            changesets = [other_repo.get_changeset(cs)
                          for cs in scmutil.revrange(hgrepo, revs)]

        elif alias == 'git':
            assert org_repo == other_repo, (org_repo, other_repo) # no git support for different repos
            so, se = org_repo.run_git_command(
                'log --reverse --pretty="format: %%H" -s -p %s..%s' % (org_ref[1],
                                                                       other_ref[1])
            )
            changesets = [org_repo.get_changeset(cs)
                          for cs in re.findall(r'[0-9a-fA-F]{40}', so)]

        return changesets, ancestor
コード例 #19
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]
コード例 #20
0
    def _get_repo_refs(self, repo, rev=None, branch=None, branch_rev=None):
        """return a structure with repo's interesting changesets, suitable for
        the selectors in pullrequest.html

        rev: a revision that must be in the list somehow and selected by default
        branch: a branch that must be in the list and selected by default - even if closed
        branch_rev: a revision of which peers should be preferred and available."""
        # list named branches that has been merged to this named branch - it should probably merge back
        peers = []

        if rev:
            rev = safe_str(rev)

        if branch:
            branch = safe_str(branch)

        if branch_rev:
            branch_rev = safe_str(branch_rev)
            # not restricting to merge() would also get branch point and be better
            # (especially because it would get the branch point) ... but is currently too expensive
            otherbranches = {}
            for i in repo._repo.revs(
                    "sort(parents(branch(id(%s)) and merge()) - branch(id(%s)))",
                    branch_rev, branch_rev):
                cs = repo.get_changeset(i)
                otherbranches[cs.branch] = cs.raw_id
            for abranch, node in otherbranches.iteritems():
                selected = 'branch:%s:%s' % (abranch, node)
                peers.append((selected, abranch))

        selected = None

        branches = []
        for abranch, branchrev in repo.branches.iteritems():
            n = 'branch:%s:%s' % (abranch, branchrev)
            branches.append((n, abranch))
            if rev == branchrev:
                selected = n
            if branch == abranch:
                selected = n
                branch = None
        if branch:  # branch not in list - it is probably closed
            revs = repo._repo.revs('max(branch(%s))', branch)
            if revs:
                cs = repo.get_changeset(revs[0])
                selected = 'branch:%s:%s' % (branch, cs.raw_id)
                branches.append((selected, branch))

        bookmarks = []
        for bookmark, bookmarkrev in repo.bookmarks.iteritems():
            n = 'book:%s:%s' % (bookmark, bookmarkrev)
            bookmarks.append((n, bookmark))
            if rev == bookmarkrev:
                selected = n

        tags = []
        for tag, tagrev in repo.tags.iteritems():
            n = 'tag:%s:%s' % (tag, tagrev)
            tags.append((n, tag))
            if rev == tagrev and tag != 'tip':  # tip is not a real tag - and its branch is better
                selected = n

        # prio 1: rev was selected as existing entry above

        # prio 2: create special entry for rev; rev _must_ be used
        specials = []
        if rev and selected is None:
            selected = 'rev:%s:%s' % (rev, rev)
            specials = [(selected, '%s: %s' % (_("Changeset"), rev[:12]))]

        # prio 3: most recent peer branch
        if peers and not selected:
            selected = peers[0][0][0]

        # prio 4: tip revision
        if not selected:
            selected = 'tag:tip:%s' % repo.tags['tip']

        groups = [
            (specials, _("Special")),
            (peers, _("Peer branches")),
            (bookmarks, _("Bookmarks")),
            (branches, _("Branches")),
            (tags, _("Tags")),
        ]
        return [g for g in groups if g[0]], selected