예제 #1
0
    def _get_revision(self, revision):
        """
        For git backend we always return integer here. This way we ensure
        that changset's revision attribute would become integer.
        """
        pattern = re.compile(r'^[[0-9a-fA-F]{12}|[0-9a-fA-F]{40}]$')
        is_bstr = lambda o: isinstance(o, (str, unicode))
        is_null = lambda o: len(o) == revision.count('0')

        if len(self.revisions) == 0:
            raise EmptyRepositoryError("There are no changesets yet")

        if revision in (None, '', 'tip', 'HEAD', 'head', -1):
            revision = self.revisions[-1]

        if ((is_bstr(revision) and revision.isdigit() and len(revision) < 12)
                or isinstance(revision, int) or is_null(revision)):
            try:
                revision = self.revisions[int(revision)]
            except:
                raise ChangesetDoesNotExistError("Revision %r does not exist "
                                                 "for this repository %s" %
                                                 (revision, self))

        elif is_bstr(revision):
            if not pattern.match(revision) or revision not in self.revisions:
                raise ChangesetDoesNotExistError("Revision %r does not exist "
                                                 "for this repository %s" %
                                                 (revision, self))

        # Ensure we return full id
        if not pattern.match(str(revision)):
            raise ChangesetDoesNotExistError(
                "Given revision %r not recognized" % revision)
        return revision
예제 #2
0
    def change(self, *filenodes):
        """
        Marks given ``FileNode`` objects to be *changed* in next commit.

        :raises ``EmptyRepositoryError``: if there are no changesets yet
        :raises ``NodeAlreadyExistsError``: if node with same path is already
          marked to be *changed*
        :raises ``NodeAlreadyRemovedError``: if node with same path is already
          marked to be *removed*
        :raises ``NodeDoesNotExistError``: if node doesn't exist in latest
          changeset
        :raises ``NodeNotChangedError``: if node hasn't really be changed
        """
        for node in filenodes:
            if node.path in (n.path for n in self.removed):
                raise NodeAlreadyRemovedError("Node at %s is already marked "
                                              "as removed" % node.path)
        try:
            self.repository.get_changeset()
        except EmptyRepositoryError:
            raise EmptyRepositoryError("Nothing to change - try to *add* new "
                                       "nodes rather than changing them")
        for node in filenodes:
            if node.path in (n.path for n in self.changed):
                raise NodeAlreadyChangedError("Node at '%s' is already "
                                              "marked as changed" % node.path)
            self.changed.append(node)
예제 #3
0
        def _get_readme_from_cache(key):
            readme_data = None
            readme_file = None
            log.debug('Looking for README file')
            try:
                # get's the landing revision! or tip if fails
                cs = db_repo.get_landing_changeset()
                if isinstance(cs, EmptyChangeset):
                    raise EmptyRepositoryError()
                renderer = MarkupRenderer()
                for f in README_FILES:
                    try:
                        readme = cs.get_node(f)
                        if not isinstance(readme, FileNode):
                            continue
                        readme_file = f
                        log.debug('Found README file `%s` rendering...' %
                                  readme_file)
                        readme_data = renderer.render(readme.content, f)
                        break
                    except NodeDoesNotExistError:
                        continue
            except ChangesetError:
                log.error(traceback.format_exc())
                pass
            except EmptyRepositoryError:
                pass
            except Exception:
                log.error(traceback.format_exc())

            return readme_data, readme_file
예제 #4
0
        def _generate_readme(cache_key):
            readme_data = None
            readme_file = None
            try:
                # gets the landing revision or tip if fails
                commit = db_repo.get_landing_commit()
                if isinstance(commit, EmptyCommit):
                    raise EmptyRepositoryError()
                renderer = MarkupRenderer()
                for f in README_FILES:
                    try:
                        node = commit.get_node(f)
                    except NodeDoesNotExistError:
                        continue

                    if not node.is_file():
                        continue

                    readme_file = f
                    log.debug('Found README file `%s` rendering...',
                              readme_file)
                    readme_data = renderer.render(node.content, filename=f)
                    break
            except CommitError:
                log.exception("Problem getting commit")
                pass
            except EmptyRepositoryError:
                pass
            except Exception:
                log.exception("General failure")

            return readme_data, readme_file
예제 #5
0
    def get_commits(self,
                    start_id=None,
                    end_id=None,
                    start_date=None,
                    end_date=None,
                    branch_name=None,
                    pre_load=None):
        if self.is_empty():
            raise EmptyRepositoryError("There are no commit_ids yet")
        self._validate_branch_name(branch_name)

        if start_id is not None:
            self._validate_commit_id(start_id)
        if end_id is not None:
            self._validate_commit_id(end_id)

        start_raw_id = self._sanitize_commit_id(start_id)
        start_pos = self.commit_ids.index(start_raw_id) if start_id else None
        end_raw_id = self._sanitize_commit_id(end_id)
        end_pos = max(0, self.commit_ids.index(end_raw_id)) if end_id else None

        if None not in [start_id, end_id] and start_pos > end_pos:
            raise RepositoryError(
                "Start commit '%s' cannot be after end commit '%s'" %
                (start_id, end_id))
        if end_pos is not None:
            end_pos += 1

        # Date based filtering
        if start_date or end_date:
            start_raw_id, end_raw_id = self._remote.lookup_interval(
                date_astimestamp(start_date) if start_date else None,
                date_astimestamp(end_date) if end_date else None)
            start_pos = start_raw_id - 1
            end_pos = end_raw_id

        commit_ids = self.commit_ids

        # TODO: johbo: Reconsider impact of DEFAULT_BRANCH_NAME here
        if branch_name not in [None, self.DEFAULT_BRANCH_NAME]:
            svn_rev = long(self.commit_ids[-1])
            commit_ids = self._remote.node_history(path=branch_name,
                                                   revision=svn_rev,
                                                   limit=None)
            commit_ids = [str(i) for i in reversed(commit_ids)]

        if start_pos or end_pos:
            commit_ids = commit_ids[start_pos:end_pos]
        return base.CollectionGenerator(self, commit_ids, pre_load=pre_load)
예제 #6
0
    def get_commit(self, commit_id=None, commit_idx=None, pre_load=None):
        if self.is_empty():
            raise EmptyRepositoryError("There are no commits yet")
        if commit_id is not None:
            self._validate_commit_id(commit_id)
        elif commit_idx is not None:
            self._validate_commit_idx(commit_idx)
            try:
                commit_id = self.commit_ids[commit_idx]
            except IndexError:
                raise CommitDoesNotExistError

        commit_id = self._sanitize_commit_id(commit_id)
        commit = SubversionCommit(repository=self, commit_id=commit_id)
        return commit
예제 #7
0
    def _get_revision(self, revision):
        """
        For git backend we always return integer here. This way we ensure
        that changset's revision attribute would become integer.
        """
        pattern = re.compile(r'^[[0-9a-fA-F]{12}|[0-9a-fA-F]{40}]$')
        is_bstr = lambda o: isinstance(o, (str, unicode))
        is_null = lambda o: len(o) == revision.count('0')

        if len(self.revisions) == 0:
            raise EmptyRepositoryError("There are no changesets yet")

        if revision in (None, '', 'tip', 'HEAD', 'head', -1):
            revision = self.revisions[-1]

        if ((is_bstr(revision) and revision.isdigit() and len(revision) < 12)
            or isinstance(revision, int) or is_null(revision)):
            try:
                revision = self.revisions[int(revision)]
            except Exception:
                raise ChangesetDoesNotExistError("Revision %s does not exist "
                    "for this repository" % (revision))

        elif is_bstr(revision):
            # get by branch/tag name
            _ref_revision = self._parsed_refs.get(revision)
            _tags_shas = self.tags.values()
            if _ref_revision:  # and _ref_revision[1] in ['H', 'RH', 'T']:
                return _ref_revision[0]

            # maybe it's a tag ? we don't have them in self.revisions
            elif revision in _tags_shas:
                return _tags_shas[_tags_shas.index(revision)]

            elif not pattern.match(revision) or revision not in self.revisions:
                raise ChangesetDoesNotExistError("Revision %s does not exist "
                    "for this repository" % (revision))

        # Ensure we return full id
        if not pattern.match(str(revision)):
            raise ChangesetDoesNotExistError("Given revision %s not recognized"
                % revision)
        return revision
예제 #8
0
    def _get_commit_id(self, commit_id_or_idx):
        def is_null(value):
            return len(value) == commit_id_or_idx.count('0')

        if self.is_empty():
            raise EmptyRepositoryError("There are no commits yet")

        if commit_id_or_idx in (None, '', 'tip', 'HEAD', 'head', -1):
            return self.commit_ids[-1]

        is_bstr = isinstance(commit_id_or_idx, (str, unicode))
        if ((is_bstr and commit_id_or_idx.isdigit() and len(commit_id_or_idx) < 12)
            or isinstance(commit_id_or_idx, int) or is_null(commit_id_or_idx)):
            try:
                commit_id_or_idx = self.commit_ids[int(commit_id_or_idx)]
            except Exception:
                msg = "Commit %s does not exist for %s" % (
                    commit_id_or_idx, self)
                raise CommitDoesNotExistError(msg)

        elif is_bstr:
            # get by branch/tag name
            ref_id = self._parsed_refs.get(commit_id_or_idx)
            if ref_id:  # and ref_id[1] in ['H', 'RH', 'T']:
                return ref_id[0]

            tag_ids = self.tags.values()
            # maybe it's a tag ? we don't have them in self.commit_ids
            if commit_id_or_idx in tag_ids:
                return commit_id_or_idx

            elif (not SHA_PATTERN.match(commit_id_or_idx) or
                    commit_id_or_idx not in self.commit_ids):
                msg = "Commit %s does not exist for %s" % (
                    commit_id_or_idx, self)
                raise CommitDoesNotExistError(msg)

        # Ensure we return full id
        if not SHA_PATTERN.match(str(commit_id_or_idx)):
            raise CommitDoesNotExistError(
                "Given commit id %s not recognized" % commit_id_or_idx)
        return commit_id_or_idx
예제 #9
0
    def get_commit(self, commit_id=None, commit_idx=None, pre_load=None):
        """
        Returns ``MercurialCommit`` object representing repository's
        commit at the given `commit_id` or `commit_idx`.
        """
        if self.is_empty():
            raise EmptyRepositoryError("There are no commits yet")

        if commit_id is not None:
            self._validate_commit_id(commit_id)
            try:
                idx = self._commit_ids[commit_id]
                return MercurialCommit(self, commit_id, idx, pre_load=pre_load)
            except KeyError:
                pass
        elif commit_idx is not None:
            self._validate_commit_idx(commit_idx)
            commit_idx = self._sanitize_commit_idx(commit_idx)
            try:
                id_ = self.commit_ids[commit_idx]
                if commit_idx < 0:
                    commit_idx += len(self.commit_ids)
                return MercurialCommit(self,
                                       id_,
                                       commit_idx,
                                       pre_load=pre_load)
            except IndexError:
                commit_id = commit_idx
        else:
            commit_id = "tip"

        # TODO Paris: Ugly hack to "serialize" long for msgpack
        if isinstance(commit_id, long):
            commit_id = float(commit_id)

        if isinstance(commit_id, unicode):
            commit_id = safe_str(commit_id)

        raw_id, idx = self._remote.lookup(commit_id, both=True)

        return MercurialCommit(self, raw_id, idx, pre_load=pre_load)
예제 #10
0
    def _get_revision(self, revision):
        """
        Get's an ID revision given as str. This will always return a fill
        40 char revision number

        :param revision: str or int or None
        """

        if self._empty:
            raise EmptyRepositoryError("There are no changesets yet")

        if revision in [-1, 'tip', None]:
            revision = 'tip'

        try:
            revision = hex(self._repo.lookup(revision))
        except (IndexError, ValueError, RepoLookupError, TypeError):
            raise ChangesetDoesNotExistError("Revision %s does not "
                                             "exist for this repository" %
                                             (revision))
        return revision
예제 #11
0
    def get_commits(
            self, start_id=None, end_id=None, start_date=None, end_date=None,
            branch_name=None, pre_load=None):
        """
        Returns generator of `GitCommit` objects from start to end (both
        are inclusive), in ascending date order.

        :param start_id: None, str(commit_id)
        :param end_id: None, str(commit_id)
        :param start_date: if specified, commits with commit date less than
          ``start_date`` would be filtered out from returned set
        :param end_date: if specified, commits with commit date greater than
          ``end_date`` would be filtered out from returned set
        :param branch_name: if specified, commits not reachable from given
          branch would be filtered out from returned set

        :raise BranchDoesNotExistError: If given `branch_name` does not
            exist.
        :raise CommitDoesNotExistError: If commits for given `start` or
          `end` could not be found.

        """
        if self.is_empty():
            raise EmptyRepositoryError("There are no commits yet")
        self._validate_branch_name(branch_name)

        if start_id is not None:
            self._validate_commit_id(start_id)
        if end_id is not None:
            self._validate_commit_id(end_id)

        start_raw_id = self._get_commit_id(start_id)
        start_pos = self._commit_ids[start_raw_id] if start_id else None
        end_raw_id = self._get_commit_id(end_id)
        end_pos = max(0, self._commit_ids[end_raw_id]) if end_id else None

        if None not in [start_id, end_id] and start_pos > end_pos:
            raise RepositoryError(
                "Start commit '%s' cannot be after end commit '%s'" %
                (start_id, end_id))

        if end_pos is not None:
            end_pos += 1

        filter_ = []
        if branch_name:
            filter_.append({'branch_name': branch_name})
        if start_date and not end_date:
            filter_.append({'since': start_date})
        if end_date and not start_date:
            filter_.append({'until': end_date})
        if start_date and end_date:
            filter_.append({'since': start_date})
            filter_.append({'until': end_date})

        # if start_pos or end_pos:
        #     filter_.append({'start': start_pos})
        #     filter_.append({'end': end_pos})

        if filter_:
            revfilters = {
                'branch_name': branch_name,
                'since': start_date.strftime('%m/%d/%y %H:%M:%S') if start_date else None,
                'until': end_date.strftime('%m/%d/%y %H:%M:%S') if end_date else None,
                'start': start_pos,
                'end': end_pos,
            }
            commit_ids = self._get_all_commit_ids(filters=revfilters)

            # pure python stuff, it's slow due to walker walking whole repo
            # def get_revs(walker):
            #     for walker_entry in walker:
            #         yield walker_entry.commit.id
            # revfilters = {}
            # commit_ids = list(reversed(list(get_revs(self._repo.get_walker(**revfilters)))))
        else:
            commit_ids = self.commit_ids

        if start_pos or end_pos:
            commit_ids = commit_ids[start_pos: end_pos]

        return CollectionGenerator(self, commit_ids, pre_load=pre_load)
예제 #12
0
    def get_commits(self,
                    start_id=None,
                    end_id=None,
                    start_date=None,
                    end_date=None,
                    branch_name=None,
                    pre_load=None):
        """
        Returns generator of ``MercurialCommit`` objects from start to end
        (both are inclusive)

        :param start_id: None, str(commit_id)
        :param end_id: None, str(commit_id)
        :param start_date: if specified, commits with commit date less than
          ``start_date`` would be filtered out from returned set
        :param end_date: if specified, commits with commit date greater than
          ``end_date`` would be filtered out from returned set
        :param branch_name: if specified, commits not reachable from given
          branch would be filtered out from returned set

        :raise BranchDoesNotExistError: If given ``branch_name`` does not
            exist.
        :raise CommitDoesNotExistError: If commit for given ``start`` or
          ``end`` could not be found.
        """
        # actually we should check now if it's not an empty repo
        branch_ancestors = False
        if self.is_empty():
            raise EmptyRepositoryError("There are no commits yet")
        self._validate_branch_name(branch_name)

        if start_id is not None:
            self._validate_commit_id(start_id)
            c_start = self.get_commit(commit_id=start_id)
            start_pos = self._commit_ids[c_start.raw_id]
        else:
            start_pos = None

        if end_id is not None:
            self._validate_commit_id(end_id)
            c_end = self.get_commit(commit_id=end_id)
            end_pos = max(0, self._commit_ids[c_end.raw_id])
        else:
            end_pos = None

        if None not in [start_id, end_id] and start_pos > end_pos:
            raise RepositoryError(
                "Start commit '%s' cannot be after end commit '%s'" %
                (start_id, end_id))

        if end_pos is not None:
            end_pos += 1

        commit_filter = []
        if branch_name and not branch_ancestors:
            commit_filter.append('branch("%s")' % branch_name)
        elif branch_name and branch_ancestors:
            commit_filter.append('ancestors(branch("%s"))' % branch_name)
        if start_date and not end_date:
            commit_filter.append('date(">%s")' % start_date)
        if end_date and not start_date:
            commit_filter.append('date("<%s")' % end_date)
        if start_date and end_date:
            commit_filter.append('date(">%s") and date("<%s")' %
                                 (start_date, end_date))

        # TODO: johbo: Figure out a simpler way for this solution
        collection_generator = CollectionGenerator
        if commit_filter:
            commit_filter = map(safe_str, commit_filter)
            revisions = self._remote.rev_range(commit_filter)
            collection_generator = MercurialIndexBasedCollectionGenerator
        else:
            revisions = self.commit_ids

        if start_pos or end_pos:
            revisions = revisions[start_pos:end_pos]

        return collection_generator(self, revisions, pre_load=pre_load)
예제 #13
0
    def get_changesets(self, start=None, end=None, start_date=None,
           end_date=None, branch_name=None, reverse=False):
        """
        Returns iterator of ``GitChangeset`` objects from start to end (both
        are inclusive), in ascending date order (unless ``reverse`` is set).

        :param start: changeset ID, as str; first returned changeset
        :param end: changeset ID, as str; last returned changeset
        :param start_date: if specified, changesets with commit date less than
          ``start_date`` would be filtered out from returned set
        :param end_date: if specified, changesets with commit date greater than
          ``end_date`` would be filtered out from returned set
        :param branch_name: if specified, changesets not reachable from given
          branch would be filtered out from returned set
        :param reverse: if ``True``, returned generator would be reversed
          (meaning that returned changesets would have descending date order)

        :raise BranchDoesNotExistError: If given ``branch_name`` does not
            exist.
        :raise ChangesetDoesNotExistError: If changeset for given ``start`` or
          ``end`` could not be found.

        """
        if branch_name and branch_name not in self.branches:
            raise BranchDoesNotExistError("Branch '%s' not found" \
                                          % branch_name)
        # actually we should check now if it's not an empty repo to not spaw
        # subprocess commands
        if self._empty:
            raise EmptyRepositoryError("There are no changesets yet")

        # %H at format means (full) commit hash, initial hashes are retrieved
        # in ascending date order
        cmd_template = 'log --date-order --reverse --pretty=format:"%H"'
        cmd_params = {}
        if start_date:
            cmd_template += ' --since "$since"'
            cmd_params['since'] = start_date.strftime('%m/%d/%y %H:%M:%S')
        if end_date:
            cmd_template += ' --until "$until"'
            cmd_params['until'] = end_date.strftime('%m/%d/%y %H:%M:%S')
        if branch_name:
            cmd_template += ' $branch_name'
            cmd_params['branch_name'] = branch_name
        else:
            rev_filter = _git_path = settings.GIT_REV_FILTER
            cmd_template += ' %s' % (rev_filter)

        cmd = string.Template(cmd_template).safe_substitute(**cmd_params)
        revs = self.run_git_command(cmd)[0].splitlines()
        start_pos = 0
        end_pos = len(revs)
        if start:
            _start = self._get_revision(start)
            try:
                start_pos = revs.index(_start)
            except ValueError:
                pass

        if end is not None:
            _end = self._get_revision(end)
            try:
                end_pos = revs.index(_end)
            except ValueError:
                pass

        if None not in [start, end] and start_pos > end_pos:
            raise RepositoryError('start cannot be after end')

        if end_pos is not None:
            end_pos += 1

        revs = revs[start_pos:end_pos]
        if reverse:
            revs = reversed(revs)
        return CollectionGenerator(self, revs)
예제 #14
0
    def _get_repo_pullrequest_sources(self,
                                      repo,
                                      commit_id=None,
                                      branch=None,
                                      bookmark=None):
        """
        Return a structure with repo's interesting commits, suitable for
        the selectors in pullrequest controller

        :param commit_id: a commit that must be in the list somehow
            and selected by default
        :param branch: a branch that must be in the list and selected
            by default - even if closed
        :param bookmark: a bookmark that must be in the list and selected
        """

        commit_id = safe_str(commit_id) if commit_id else None
        branch = safe_str(branch) if branch else None
        bookmark = safe_str(bookmark) if bookmark else None

        selected = None

        # order matters: first source that has commit_id in it will be selected
        sources = []
        sources.append(
            ('book', repo.bookmarks.items(), _('Bookmarks'), bookmark))
        sources.append(
            ('branch', repo.branches.items(), _('Branches'), branch))

        if commit_id:
            ref_commit = (h.short_id(commit_id), commit_id)
            sources.append(('rev', [ref_commit], _('Commit IDs'), commit_id))

        sources.append(('branch', repo.branches_closed.items(),
                        _('Closed Branches'), branch), )

        groups = []
        for group_key, ref_list, group_name, match in sources:
            group_refs = []
            for ref_name, ref_id in ref_list:
                ref_key = '%s:%s:%s' % (group_key, ref_name, ref_id)
                group_refs.append((ref_key, ref_name))

                if not selected:
                    if set([commit_id, match]) & set([ref_id, ref_name]):
                        selected = ref_key

            if group_refs:
                groups.append((group_refs, group_name))

        if not selected:
            ref = commit_id or branch or bookmark
            if ref:
                raise CommitDoesNotExistError(
                    'No commit refs could be found matching: %s' % ref)
            elif repo.DEFAULT_BRANCH_NAME in repo.branches:
                selected = 'branch:%s:%s' % (
                    repo.DEFAULT_BRANCH_NAME,
                    repo.branches[repo.DEFAULT_BRANCH_NAME])
            elif repo.commit_ids:
                rev = repo.commit_ids[0]
                selected = 'rev:%s:%s' % (rev, rev)
            else:
                raise EmptyRepositoryError()
        return groups, selected