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
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)
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
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
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)
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
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
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)
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
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
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
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)
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
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
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
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
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
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
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)
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
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
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
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
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
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
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
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
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
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()
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