Beispiel #1
0
  def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
    """see vclib.Repository.rawdiff docstring

    Option values recognized by this implementation:

      ignore_keyword_subst - boolean, ignore keyword substitution
    """
    if self.itemtype(path_parts1, rev1) != vclib.FILE:  # does auth-check
      raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts1)))
    if self.itemtype(path_parts2, rev2) != vclib.FILE:  # does auth-check
      raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts2)))

    args = vclib._diff_args(type, options)
    if options.get('ignore_keyword_subst', 0):
      args.append('-kk')

    rcsfile = self.rcsfile(path_parts1, 1)
    if path_parts1 != path_parts2:
      raise NotImplementedError("cannot diff across paths in cvs")
    args.extend(['-r' + rev1, '-r' + rev2, rcsfile])
    
    fp = self.rcs_popen('rcsdiff', args, True)

    # Eat up the non-GNU-diff-y headers.
    while 1:
      line = fp.readline()
      if not line or line[0:5] == 'diff ':
        break
    return fp
Beispiel #2
0
    def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
        if self.itemtype(path_parts1, rev1) != vclib.FILE:  # does auth-check
            raise vclib.Error("Path '%s' is not a file." %
                              (string.join(path_parts1, "/")))
        if self.itemtype(path_parts2, rev2) != vclib.FILE:  # does auth-check
            raise vclib.Error("Path '%s' is not a file." %
                              (string.join(path_parts2, "/")))

        temp1 = tempfile.mktemp()
        open(temp1,
             'wb').write(self.openfile(path_parts1, rev1, {})[0].getvalue())
        temp2 = tempfile.mktemp()
        open(temp2,
             'wb').write(self.openfile(path_parts2, rev2, {})[0].getvalue())

        r1 = self.itemlog(path_parts1, rev1, vclib.SORTBY_DEFAULT, 0, 0,
                          {})[-1]
        r2 = self.itemlog(path_parts2, rev2, vclib.SORTBY_DEFAULT, 0, 0,
                          {})[-1]

        info1 = (self.rcsfile(path_parts1, root=1, v=0), r1.date, r1.string)
        info2 = (self.rcsfile(path_parts2, root=1, v=0), r2.date, r2.string)

        diff_args = vclib._diff_args(type, options)

        return vclib._diff_fp(temp1, temp2, info1, info2, self.utilities.diff
                              or 'diff', diff_args)
Beispiel #3
0
    def openfile(self, path_parts, rev):
        if not rev or rev == 'HEAD' or rev == 'MAIN':
            rev_flag = '-p'
        else:
            rev_flag = '-p' + rev
        full_name = self.rcsfile(path_parts, root=1, v=0)

        used_rlog = 0
        tip_rev = None  # used only if we have to fallback to using rlog

        fp = self.rcs_popen('co', (rev_flag, full_name), 'rb')
        try:
            filename, revision = _parse_co_header(fp)
        except COMissingRevision:
            # We got a "revision X.Y.Z absent" error from co.  This could be
            # because we were asked to find a tip of a branch, which co
            # doesn't seem to handle.  So we do rlog-gy stuff to figure out
            # which revision the tip of the branch currently maps to.
            ### TODO: Only do this when 'rev' is a branch symbol name?
            if not used_rlog:
                tip_rev = self._get_tip_revision(full_name + ',v', rev)
                used_rlog = 1
            if not tip_rev:
                raise vclib.Error("Unable to find valid revision")
            fp = self.rcs_popen('co', ('-p' + tip_rev.string, full_name), 'rb')
            filename, revision = _parse_co_header(fp)

        if filename is None:
            # CVSNT's co exits without any output if a dead revision is requested.
            # Bug at http://www.cvsnt.org/cgi-bin/bugzilla/show_bug.cgi?id=190
            # As a workaround, we invoke rlog to find the first non-dead revision
            # that precedes it and check out that revision instead.  Of course,
            # if we've already invoked rlog above, we just reuse its output.
            if not used_rlog:
                tip_rev = self._get_tip_revision(full_name + ',v', rev)
                used_rlog = 1
            if not (tip_rev and tip_rev.undead):
                raise vclib.Error(
                    'Could not find non-dead revision preceding "%s"' % rev)
            fp = self.rcs_popen('co',
                                ('-p' + tip_rev.undead.string, full_name),
                                'rb')
            filename, revision = _parse_co_header(fp)

        if filename is None:
            raise vclib.Error('Missing output from co (filename = "%s")' %
                              full_name)

        if not _paths_eq(filename, full_name):
            raise vclib.Error(
                'The filename from co ("%s") did not match (expected "%s")' %
                (filename, full_name))

        return fp, revision
Beispiel #4
0
  def annotate(self, path_parts, rev=None, include_text=False):
    if self.itemtype(path_parts, rev) != vclib.FILE:  # does auth-check
      raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts)))

    from vclib.ccvs import blame
    source = blame.BlameSource(self.rcsfile(path_parts, 1), rev, include_text)
    return source, source.revision
Beispiel #5
0
    def set_revision_info(self, revision, log, text):
        # check revs.has_key(revision)
        rev = self.revs[revision]
        rev.log = log

        changed = None
        added = 0
        deled = 0
        if self.head != revision:
            changed = 1
            lines = text.split('\n')
            idx = 0
            while idx < len(lines):
                command = lines[idx]
                dmatch = self.d_command.match(command)
                idx = idx + 1
                if dmatch:
                    deled = deled + int(dmatch.group(2))
                else:
                    amatch = self.a_command.match(command)
                    if amatch:
                        count = int(amatch.group(2))
                        added = added + count
                        idx = idx + count
                    elif command:
                        raise vclib.Error("error while parsing deltatext: %s" %
                                          command)

        if len(rev.number) == 2:
            rev.next_changed = changed and "+%i -%i" % (deled, added)
        else:
            rev.changed = changed and "+%i -%i" % (added, deled)
Beispiel #6
0
 def __getitem__(self, idx):
     if idx == self.idx:
         return self.last
     if idx != self.idx + 1:
         raise BlameSequencingError()
     line = self.fp.readline()
     if not line:
         raise IndexError("No more annotations")
     m = _re_blameinfo.match(line[:17])
     if not m:
         raise vclib.Error("Could not parse blame output at line %i\n%s" %
                           (idx + 1, line))
     rev, author = m.groups()
     text = line[18:]
     rev = int(rev)
     prev_rev = None
     if rev > self.first_rev:
         prev_rev = rev - 1
     item = _item(text=text,
                  line_number=idx + 1,
                  rev=rev,
                  prev_rev=prev_rev,
                  author=author,
                  date=None)
     self.last = item
     self.idx = idx
     return item
Beispiel #7
0
    def annotate(self, path_parts, rev):
        path = self._getpath(path_parts)
        if self.itemtype(path_parts, rev) != vclib.FILE:  # does auth-check
            raise vclib.Error("Path '%s' is not a file." % path)
        rev = self._getrev(rev)
        url = self._geturl(path)

        blame_data = []

        def _blame_cb(line_no,
                      revision,
                      author,
                      date,
                      line,
                      pool,
                      blame_data=blame_data):
            prev_rev = None
            if revision > 1:
                prev_rev = revision - 1
            blame_data.append(
                vclib.Annotation(line, line_no + 1, revision, prev_rev, author,
                                 None))

        client.svn_client_blame(url, _rev2optrev(1), _rev2optrev(rev),
                                _blame_cb, self.ctx)

        return blame_data, rev
Beispiel #8
0
  def dirlogs(self, path_parts, rev, entries, options):
    """see vclib.Repository.dirlogs docstring

    rev can be a tag name or None. if set only information from revisions
    matching the tag will be retrieved

    Option values recognized by this implementation:

      cvs_subdirs
        boolean. true to fetch logs of the most recently modified file in each
        subdirectory

    Option values returned by this implementation:

      cvs_tags, cvs_branches
        lists of tag and branch names encountered in the directory
    """
    if self.itemtype(path_parts, rev) != vclib.DIR:  # does auth-check
      raise vclib.Error("Path '%s' is not a directory."
                        % (_path_join(path_parts)))

    subdirs = options.get('cvs_subdirs', 0)
    entries_to_fetch = []
    for entry in entries:
      if vclib.check_path_access(self, path_parts + [entry.name], None, rev):
        entries_to_fetch.append(entry)
    alltags = _get_logs(self, path_parts, entries_to_fetch, rev, subdirs)
    branches = options['cvs_branches'] = []
    tags = options['cvs_tags'] = []
    for name, rev in alltags.items():
      if Tag(None, rev).is_branch:
        branches.append(name)
      else:
        tags.append(name)
Beispiel #9
0
  def itemlog(self, path_parts, rev, sortby, first, limit, options):
    """see vclib.Repository.itemlog docstring

    rev parameter can be a revision number, a branch number, a tag name,
    or None. If None, will return information about all revisions, otherwise,
    will only return information about the specified revision or branch.

    Option values returned by this implementation:

      cvs_tags
        dictionary of Tag objects for all tags encountered
    """
    if self.itemtype(path_parts, rev) != vclib.FILE:  # does auth-check
      raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts)))

    path = self.rcsfile(path_parts, 1)
    sink = TreeSink()
    rcsparse.parse(open(path, 'rb'), sink)
    filtered_revs = _file_log(sink.revs.values(), sink.tags, sink.lockinfo,
                              sink.default_branch, rev)
    for rev in filtered_revs:
      if rev.prev and len(rev.number) == 2:
        rev.changed = rev.prev.next_changed
    options['cvs_tags'] = sink.tags

    if sortby == vclib.SORTBY_DATE:
      filtered_revs.sort(_logsort_date_cmp)
    elif sortby == vclib.SORTBY_REV:
      filtered_revs.sort(_logsort_rev_cmp)

    if len(filtered_revs) < first:
      return []
    if limit:
      return filtered_revs[first:first+limit]
    return filtered_revs
Beispiel #10
0
 def openfile(self, path_parts, rev, options):
   if self.itemtype(path_parts, rev) != vclib.FILE:  # does auth-check
     raise vclib.Error("Path '%s' is not a file." % (_path_join(path_parts)))
   path = self.rcsfile(path_parts, 1)
   sink = COSink(rev)
   rcsparse.parse(open(path, 'rb'), sink)
   revision = sink.last and sink.last.string
   return cStringIO.StringIO('\n'.join(sink.sstext.text)), revision
Beispiel #11
0
 def filesize(self, path_parts, rev):
     path = self._getpath(path_parts)
     if self.itemtype(path_parts, rev) != vclib.FILE:  # does auth-check
         raise vclib.Error("Path '%s' is not a file." % path)
     rev = self._getrev(rev)
     dirents, locks = self._get_dirents(self._getpath(path_parts[:-1]), rev)
     dirent = dirents.get(path_parts[-1], None)
     return dirent.size
Beispiel #12
0
    def dirlogs(self, path_parts, rev, entries, options):
        """see vclib.Repository.dirlogs docstring

    rev can be a tag name or None. if set only information from revisions
    matching the tag will be retrieved

    Option values recognized by this implementation:

      cvs_subdirs
        boolean. true to fetch logs of the most recently modified file in each
        subdirectory

    Option values returned by this implementation:

      cvs_tags, cvs_branches
        lists of tag and branch names encountered in the directory
    """
        if self.itemtype(path_parts, rev) != vclib.DIR:  # does auth-check
            raise vclib.Error("Path '%s' is not a directory." %
                              (part2path(path_parts)))
        entries_to_fetch = []
        for entry in entries:
            if vclib.check_path_access(self, path_parts + [entry.name], None,
                                       rev):
                entries_to_fetch.append(entry)

        subdirs = options.get('cvs_subdirs', 0)

        dirpath = self._getpath(path_parts)
        alltags = {  # all the tags seen in the files of this dir
            'MAIN': '',
            'HEAD': '1.1'
        }

        for entry in entries_to_fetch:
            entry.rev = entry.date = entry.author = None
            entry.dead = entry.absent = entry.log = entry.lockinfo = None
            path = _log_path(entry, dirpath, subdirs)
            if path:
                entry.path = path
                try:
                    rcsparse.parse(
                        open(path, 'rb'),
                        InfoSink(entry, rev, alltags, self.encoding))
                except IOError as e:
                    entry.errors.append("rcsparse error: %s" % e)
                except RuntimeError as e:
                    entry.errors.append("rcsparse error: %s" % e)
                except rcsparse.RCSStopParser:
                    pass

        branches = options['cvs_branches'] = []
        tags = options['cvs_tags'] = []
        for name, rev in alltags.items():
            if Tag(None, rev).is_branch:
                branches.append(name)
            else:
                tags.append(name)
Beispiel #13
0
 def openfile(self, path_parts, rev, options):
   path = self._getpath(path_parts)
   if self.itemtype(path_parts, rev) != vclib.FILE:  # does auth-check
     raise vclib.Error("Path '%s' is not a file." % path)
   rev = self._getrev(rev)
   # rev here should be the last history revision of the URL
   fp = SelfCleanFP(cat_to_tempfile(self, path, rev))
   lh_rev, c_rev = self._get_last_history_rev(path_parts, rev)
   return fp, lh_rev
Beispiel #14
0
 def openfile(self, path_parts, rev, options):
     path = self._getpath(path_parts)
     if self.itemtype(path_parts, rev) != vclib.FILE:  # does auth-check
         raise vclib.Error("Path '%s' is not a file." % path)
     rev = self._getrev(rev)
     fsroot = self._getroot(rev)
     revision = str(_get_last_history_rev(fsroot, path))
     fp = FileContentsPipe(fsroot, path)
     return fp, revision
Beispiel #15
0
    def itemlog(self, path_parts, rev, sortby, first, limit, options):
        """see vclib.Repository.itemlog docstring

    rev parameter can be a revision number, a branch number, a tag name,
    or None. If None, will return information about all revisions, otherwise,
    will only return information about the specified revision or branch.

    Option values recognized by this implementation:

      cvs_pass_rev
        boolean, default false. set to true to pass rev parameter as -r
        argument to rlog, this is more efficient but causes less
        information to be returned

    Option values returned by this implementation:

      cvs_tags
        dictionary of Tag objects for all tags encountered
    """

        if self.itemtype(path_parts, rev) != vclib.FILE:  # does auth-check
            raise vclib.Error("Path '%s' is not a file." %
                              (_path_join(path_parts)))

        # Invoke rlog
        rcsfile = self.rcsfile(path_parts, 1)
        if rev and options.get('cvs_pass_rev', 0):
            args = '-r' + rev, rcsfile
        else:
            args = rcsfile,

        fp = self.rcs_popen('rlog', args, 'rt', 0)
        filename, default_branch, tags, lockinfo, msg, eof = _parse_log_header(
            fp)

        # Retrieve revision objects
        revs = []
        while not eof:
            revision, eof = _parse_log_entry(fp)
            if revision:
                revs.append(revision)

        filtered_revs = _file_log(revs, tags, lockinfo, default_branch, rev)

        options['cvs_tags'] = tags
        if sortby == vclib.SORTBY_DATE:
            filtered_revs.sort(_logsort_date_cmp)
        elif sortby == vclib.SORTBY_REV:
            filtered_revs.sort(_logsort_rev_cmp)

        if len(filtered_revs) < first:
            return []
        if limit:
            return filtered_revs[first:first + limit]
        return filtered_revs
Beispiel #16
0
 def listdir(self, path_parts, rev, options):
     path = self._getpath(path_parts)
     if self.itemtype(path_parts, rev) != vclib.DIR:  # does auth-check
         raise vclib.Error("Path '%s' is not a directory." % path)
     rev = self._getrev(rev)
     entries = []
     dirents, locks = self._get_dirents(path, rev)
     for name, entry in dirents.items():
         pathtype = _kind2type(entry.kind)
         entries.append(vclib.DirEntry(name, pathtype))
     return entries
Beispiel #17
0
    def annotate(self, path_parts, rev, include_text=False):
        path = self._getpath(path_parts)
        if self.itemtype(path_parts, rev) != vclib.FILE:  # does auth-check
            raise vclib.Error("Path '%s' is not a file." % path)
        rev = self._getrev(rev)
        url = self._geturl(path)

        # Examine logs for the file to determine the oldest revision we are
        # permitted to see.
        log_options = {
            'svn_cross_copies': 1,
            'svn_show_all_dir_logs': 1,
        }
        revs = self.itemlog(path_parts, rev, vclib.SORTBY_REV, 0, 0,
                            log_options)
        oldest_rev = revs[-1].number

        # Now calculate the annotation data.  Note that we'll not
        # inherently trust the provided author and date, because authz
        # rules might necessitate that we strip that information out.
        blame_data = []

        def _blame_cb(line_no,
                      revision,
                      author,
                      date,
                      line,
                      pool,
                      blame_data=blame_data):
            prev_rev = None
            if revision > 1:
                prev_rev = revision - 1

            # If we have an invalid revision, clear the date and author
            # values.  Otherwise, if we have authz filtering to do, use the
            # revinfo cache to do so.
            if revision < 0:
                date = author = None
            elif self.auth:
                date, author, msg, revprops, changes = self._revinfo(revision)
            else:
                author = _normalize_property_value(author, self.encoding)

            # Strip text if the caller doesn't want it.
            if not include_text:
                line = None
            blame_data.append(
                vclib.Annotation(line, line_no + 1, revision, prev_rev, author,
                                 date))

        client.blame2(url, _rev2optrev(rev), _rev2optrev(oldest_rev),
                      _rev2optrev(rev), _blame_cb, self.ctx)
        return blame_data, rev
Beispiel #18
0
 def annotate(self, path_parts, rev, include_text=False):
     path = self._getpath(path_parts)
     path_type = self.itemtype(path_parts, rev)  # does auth-check
     if path_type != vclib.FILE:
         raise vclib.Error("Path '%s' is not a file." % path)
     rev = self._getrev(rev)
     fsroot = self._getroot(rev)
     history = self._get_history(path, rev, path_type, 0,
                                 {'svn_cross_copies': 1})
     youngest_rev, youngest_path = history[0]
     oldest_rev, oldest_path = history[-1]
     source = BlameSource(_rootpath2url(self.rootpath, path), youngest_rev,
                          oldest_rev, include_text, self.config_dir)
     return source, youngest_rev
Beispiel #19
0
 def openfile(self, path_parts, rev, options):
     path = self._getpath(path_parts)
     if self.itemtype(path_parts, rev) != vclib.FILE:  # does auth-check
         raise vclib.Error("Path '%s' is not a file." % path)
     rev = self._getrev(rev)
     url = self._geturl(path)
     tmp_file = tempfile.mktemp()
     stream = core.svn_stream_from_aprfile(tmp_file)
     ### rev here should be the last history revision of the URL
     client.svn_client_cat(core.Stream(stream), url, _rev2optrev(rev),
                           self.ctx)
     core.svn_stream_close(stream)
     return SelfCleanFP(tmp_file), self._get_last_history_rev(
         path_parts, rev)
Beispiel #20
0
 def listdir(self, path_parts, rev, options):
     path = self._getpath(path_parts)
     if self.itemtype(path_parts, rev) != vclib.DIR:  # does auth-check
         raise vclib.Error("Path '%s' is not a directory." % path)
     rev = self._getrev(rev)
     fsroot = self._getroot(rev)
     dirents = fs.dir_entries(fsroot, path)
     entries = []
     for entry in dirents.values():
         kind = _kind2type(entry.kind)
         ent_path = _to_str(entry.name)
         if vclib.check_path_access(self, path_parts + [ent_path], kind,
                                    rev):
             entries.append(vclib.DirEntry(ent_path, kind))
     return entries
Beispiel #21
0
 def listdir(self, path_parts, rev, options):
   path = self._getpath(path_parts)
   if self.itemtype(path_parts, rev) != vclib.DIR:  # does auth-check
     raise vclib.Error("Path '%s' is not a directory." % path)
   rev = self._getrev(rev)
   fsroot = self._getroot(rev)
   dirents = fs.dir_entries(fsroot, path)
   entries = [ ]
   for entry in dirents.values():
     if entry.kind == core.svn_node_dir:
       kind = vclib.DIR
     elif entry.kind == core.svn_node_file:
       kind = vclib.FILE
     if vclib.check_path_access(self, path_parts + [entry.name], kind, rev):
       entries.append(vclib.DirEntry(entry.name, kind))
   return entries
Beispiel #22
0
 def listdir(self, path_parts, rev, options):
     path = self._getpath(path_parts)
     if self.itemtype(path_parts, rev) != vclib.DIR:  # does auth-check
         raise vclib.Error("Path '%s' is not a directory." % path)
     rev = self._getrev(rev)
     entries = []
     dirents, locks = self._get_dirents(path, rev)
     for name in dirents.keys():
         entry = dirents[name]
         if entry.kind == core.svn_node_dir:
             kind = vclib.DIR
         elif entry.kind == core.svn_node_file:
             kind = vclib.FILE
         if vclib.check_path_access(self, path_parts + [name], kind, rev):
             entries.append(vclib.DirEntry(name, kind))
     return entries
Beispiel #23
0
    def listdir(self, path_parts, rev, options):
        basepath = self._getpath(path_parts)
        if self.itemtype(path_parts, rev) != vclib.DIR:
            raise vclib.Error("Path '%s' is not a directory." % basepath)

        rev = self._getrev(rev)
        fsroot = self._getroot(rev)
        dirents = fs.dir_entries(fsroot, basepath, self.scratch_pool)
        entries = []
        for entry in dirents.values():
            if entry.kind == core.svn_node_dir:
                kind = vclib.DIR
            elif entry.kind == core.svn_node_file:
                kind = vclib.FILE
            entries.append(vclib.DirEntry(entry.name, kind))
        self._scratch_clear()
        return entries
Beispiel #24
0
 def dirlogs(self, path_parts, rev, entries, options):
     path = self._getpath(path_parts)
     if self.itemtype(path_parts, rev) != vclib.DIR:  # does auth-check
         raise vclib.Error("Path '%s' is not a directory." % path)
     rev = self._getrev(rev)
     dirents, locks = self._get_dirents(path, rev)
     for entry in entries:
         entry_path_parts = path_parts + [entry.name]
         if not vclib.check_path_access(self, entry_path_parts, entry.kind,
                                        rev):
             continue
         dirent = dirents[entry.name]
         entry.date, entry.author, entry.log, revprops, changes = \
                     self.revinfo(dirent.created_rev)
         entry.rev = str(dirent.created_rev)
         entry.size = dirent.size
         entry.lockinfo = None
         if locks.has_key(entry.name):
             entry.lockinfo = locks[entry.name].owner
Beispiel #25
0
 def dirlogs(self, path_parts, rev, entries, options):
   path = self._getpath(path_parts)
   if self.itemtype(path_parts, rev) != vclib.DIR:  # does auth-check
     raise vclib.Error("Path '%s' is not a directory." % path)
   rev = self._getrev(rev)
   dirents, locks = self._get_dirents(path, rev)
   for entry in entries:
     dirent = dirents.get(entry.name, None)
     # dirents is authz-sanitized, so ensure the entry is found therein.
     if dirent is None:
       continue
     # Get authz-sanitized revision metadata.
     entry.date, entry.author, entry.log, revprops, changes = (
       self._revinfo(dirent.created_rev))
     entry.rev = str(dirent.created_rev)
     entry.size = dirent.size
     entry.lockinfo = None
     if entry.name in locks:
       entry.lockinfo = locks[entry.name].owner
Beispiel #26
0
    def listdir(self, path_parts, rev, options):
        if self.itemtype(path_parts, rev) != vclib.DIR:  # does auth-check
            raise vclib.Error("Path '%s' is not a directory." %
                              (_path_join(path_parts)))

        # Only RCS files (*,v) and subdirs are returned.
        data = []
        full_name = self._getpath(path_parts)
        for file in os.listdir(full_name):
            name = None
            kind, errors = _check_path(os.path.join(full_name, file))
            if kind == vclib.FILE:
                if file[-2:] == ',v':
                    name = file[:-2]
            elif kind == vclib.DIR:
                if file != 'Attic' and file != 'CVS':  # CVS directory is for fileattr
                    name = file
            else:
                name = file
            if not name:
                continue
            if vclib.check_path_access(self, path_parts + [name], kind, rev):
                data.append(CVSDirEntry(name, kind, errors, 0))

        full_name = os.path.join(full_name, 'Attic')
        if os.path.isdir(full_name):
            for file in os.listdir(full_name):
                name = None
                kind, errors = _check_path(os.path.join(full_name, file))
                if kind == vclib.FILE:
                    if file[-2:] == ',v':
                        name = file[:-2]
                elif kind != vclib.DIR:
                    name = file
                if not name:
                    continue
                if vclib.check_path_access(self, path_parts + [name], kind,
                                           rev):
                    data.append(CVSDirEntry(name, kind, errors, 1))

        return data
Beispiel #27
0
 def dirlogs(self, path_parts, rev, entries, options):
   path = self._getpath(path_parts)
   if self.itemtype(path_parts, rev) != vclib.DIR:  # does auth-check
     raise vclib.Error("Path '%s' is not a directory." % path)
   fsroot = self._getroot(self._getrev(rev))
   rev = self._getrev(rev)
   for entry in entries:
     entry_path_parts = path_parts + [entry.name]
     if not vclib.check_path_access(self, entry_path_parts, entry.kind, rev):
       continue
     path = self._getpath(entry_path_parts)
     entry_rev = _get_last_history_rev(fsroot, path)
     date, author, msg, revprops, changes = self._revinfo(entry_rev)
     entry.rev = str(entry_rev)
     entry.date = date
     entry.author = author
     entry.log = msg
     if entry.kind == vclib.FILE:
       entry.size = fs.file_length(fsroot, path)
     lock = fs.get_lock(self.fs_ptr, path)
     entry.lockinfo = lock and lock.owner or None
Beispiel #28
0
 def annotate(self, path_parts, rev=None):
     if self.itemtype(path_parts, rev) != vclib.FILE:  # does auth-check
         raise vclib.Error("Path '%s' is not a file." %
                           (string.join(path_parts, "/")))
     source = blame.BlameSource(self.rcsfile(path_parts, 1), rev)
     return source, source.revision
Beispiel #29
0
def _get_logs(repos, dir_path_parts, entries, view_tag, get_dirs):
  alltags = {           # all the tags seen in the files of this dir
    'MAIN' : '',
    'HEAD' : '1.1'
    }

  entries_idx = 0
  entries_len = len(entries)
  max_args = 100

  while 1:
    chunk = []

    while len(chunk) < max_args and entries_idx < entries_len:
      entry = entries[entries_idx]
      path = _log_path(entry, repos._getpath(dir_path_parts), get_dirs)
      if path:
        entry.path = path
        entry.idx = entries_idx
        chunk.append(entry)

      # set properties even if we don't retrieve logs
      entry.rev = entry.date = entry.author = None
      entry.dead = entry.log = entry.lockinfo = None

      entries_idx = entries_idx + 1

    if not chunk:
      return alltags

    args = []
    if not view_tag:
      # NOTE: can't pass tag on command line since a tag may contain "-"
      #       we'll search the output for the appropriate revision
      # fetch the latest revision on the default branch
      args.append('-r')
    args.extend([x.path for x in chunk])
    rlog = repos.rcs_popen('rlog', args, True)

    # consume each file found in the resulting log
    chunk_idx = 0
    while chunk_idx < len(chunk):
      file = chunk[chunk_idx]
      filename, default_branch, taginfo, lockinfo, msg, eof \
        = _parse_log_header(rlog)

      if eof == _EOF_LOG:
        # the rlog output ended early. this can happen on errors that rlog
        # thinks are so serious that it stops parsing the current file and
        # refuses to parse any of the files that come after it. one of the
        # errors that triggers this obnoxious behavior looks like:
        #
        # rlog: c:\cvsroot\dir\file,v:8: unknown expand mode u
        # rlog aborted

        # if current file has errors, restart on the next one
        if file.errors:
          chunk_idx = chunk_idx + 1
          if chunk_idx < len(chunk):
            entries_idx = chunk[chunk_idx].idx
          break

        # otherwise just error out
        raise vclib.Error('Rlog output ended early. Expected RCS file "%s"'
                          % file.path)

      # if rlog filename doesn't match current file and we already have an
      # error message about this file, move on to the next file
      while not (file and _paths_eq(file.path, filename)):
        if file and file.errors:
          chunk_idx = chunk_idx + 1
          file = chunk_idx < len(chunk) and chunk[chunk_idx] or None
          continue

        raise vclib.Error('Error parsing rlog output. Expected RCS file %s'
                          ', found %s' % (file and file.path, filename))

      # if we get an rlog error message, restart loop without advancing
      # chunk_idx cause there might be more output about the same file
      if eof == _EOF_ERROR:
        file.errors.append("rlog error: %s" % msg)
        continue

      tag = None
      if view_tag == 'MAIN' or view_tag == 'HEAD':
        tag = Tag(None, default_branch)
      elif view_tag in taginfo:
        tag = Tag(None, taginfo[view_tag])
      elif view_tag and (eof != _EOF_FILE):
        # the tag wasn't found, so skip this file (unless we already
        # know there's nothing left of it to read)
        _skip_file(rlog)
        eof = _EOF_FILE

      # we don't care about the specific values -- just the keys and whether
      # the values point to branches or revisions. this the fastest way to
      # merge the set of keys and keep values that allow us to make the
      # distinction between branch tags and normal tags
      alltags.update(taginfo)

      # read all of the log entries until we find the revision we want
      wanted_entry = None
      while not eof:

        # fetch one of the log entries
        entry, eof = _parse_log_entry(rlog)

        if not entry:
          # parsing error
          break

        # A perfect match is a revision on the branch being viewed or
        # a revision having the tag being viewed or any revision
        # when nothing is being viewed. When there's a perfect match
        # we set the wanted_entry value and break out of the loop.
        # An imperfect match is a revision at the branch point of a
        # branch being viewed. When there's an imperfect match we
        # also set the wanted_entry value but keep looping in case
        # something better comes along.
        perfect = not tag or entry.number == tag.number or       \
                  (len(entry.number) == 2 and not tag.number) or \
                  entry.number[:-1] == tag.number
        if perfect or entry.number == tag.number[:-1]:
          wanted_entry = entry
          if perfect:
            break

      if wanted_entry:
        file.rev = wanted_entry.string
        file.date = wanted_entry.date
        file.author = wanted_entry.author
        file.dead = file.kind == vclib.FILE and wanted_entry.dead
        file.absent = 0
        file.log = wanted_entry.log
        file.lockinfo = lockinfo.get(file.rev)
        # suppress rlog errors if we find a usable revision in the end
        del file.errors[:]
      elif file.kind == vclib.FILE:
        file.dead = 0
        #file.errors.append("No revisions exist on %s" % (view_tag or "MAIN"))
        file.absent = 1

      # done with this file now, skip the rest of this file's revisions
      if not eof:
        _skip_file(rlog)

      # end of while loop, advance index
      chunk_idx = chunk_idx + 1

    rlog.close()
Beispiel #30
0
def _file_log(revs, taginfo, lockinfo, cur_branch, filter):
  """Augment list of Revisions and a dictionary of Tags"""

  # Add artificial ViewVC tag MAIN. If the file has a default branch, then
  # MAIN acts like a branch tag pointing to that branch. Otherwise MAIN acts
  # like a branch tag that points to the trunk. (Note: A default branch is
  # just a branch number specified in an RCS file that tells CVS and RCS
  # what branch to use for checkout and update operations by default, when
  # there's no revision argument or sticky branch to override it. Default
  # branches get set by "cvs import" to point to newly created vendor
  # branches. Sometimes they are also set manually with "cvs admin -b")
  taginfo['MAIN'] = cur_branch

  # Create tag objects
  for name, num in taginfo.items():
    taginfo[name] = Tag(name, num)
  tags = list(taginfo.values())

  # Set view_tag to a Tag object in order to filter results. We can filter by
  # revision number or branch number
  if filter:
    try:
      view_tag = Tag(None, filter)
    except ValueError:
      view_tag = None
    else:
      tags.append(view_tag)

  # Match up tags and revisions
  _match_revs_tags(revs, tags)

  # Match up lockinfo and revision
  for rev in revs:
    rev.lockinfo = lockinfo.get(rev.string)

  # Add artificial ViewVC tag HEAD, which acts like a non-branch tag pointing
  # at the latest revision on the MAIN branch. The HEAD revision doesn't have
  # anything to do with the "head" revision number specified in the RCS file
  # and in rlog output. HEAD refers to the revision that the CVS and RCS co
  # commands will check out by default, whereas the "head" field just refers
  # to the highest revision on the trunk.
  taginfo['HEAD'] = _add_tag('HEAD', taginfo['MAIN'].co_rev)

  # Determine what revisions to return
  if filter:
    # If view_tag isn't set, it means filter is not a valid revision or
    # branch number. Check taginfo to see if filter is set to a valid tag
    # name. If so, filter by that tag, otherwise raise an error.
    if not view_tag:
      try:
        view_tag = taginfo[filter]
      except KeyError:
        raise vclib.Error('Invalid tag or revision number "%s"' % filter)
    filtered_revs = [ ]

    # only include revisions on the tag branch or it's parent branches
    if view_tag.is_branch:
      branch = view_tag.number
    elif len(view_tag.number) > 2:
      branch = view_tag.number[:-1]
    else:
      branch = ()

    # for a normal tag, include all tag revision and all preceding revisions.
    # for a branch tag, include revisions on branch, branch point revision,
    # and all preceding revisions
    for rev in revs:
      if (rev.number == view_tag.number
          or rev.branch_number == view_tag.number
          or (rev.number < view_tag.number
              and rev.branch_number == branch[:len(rev.branch_number)])):
        filtered_revs.append(rev)

    # get rid of the view_tag if it was only created for filtering
    if view_tag.name is None:
      _remove_tag(view_tag)
  else:
    filtered_revs = revs

  return filtered_revs