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 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." % (string.join(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)) except IOError, e: entry.errors.append("rcsparse error: %s" % e) except RuntimeError, e: entry.errors.append("rcsparse error: %s" % e) except rcsparse.RCSStopParser: pass
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)) if self.guesser: entry.log = self.guesser.utf8(entry.log) except IOError, e: entry.errors.append("rcsparse error: %s" % e) except RuntimeError, e: entry.errors.append("rcsparse error: %s" % e) except rcsparse.RCSStopParser: pass
def parse_cvs_file(self, rcs_pathname, opt_rev = None, opt_m_timestamp = None): # Args in: opt_rev - requested revision # opt_m - time since modified # Args out: revision_map # timestamp # revision_deltatext # CheckHidden(rcs_pathname) try: rcsfile = open(rcs_pathname, 'rb') except: raise RuntimeError, ('error: %s appeared to be under CVS control, ' + 'but the RCS file is inaccessible.') % rcs_pathname rcsparse.parse(rcsfile, self) rcsfile.close() if opt_rev in [None, '', 'HEAD']: # Explicitly specified topmost revision in tree revision = self.head_revision else: # Symbolic tag or specific revision number specified. revision = self.map_tag_to_revision(opt_rev) if revision == '': raise RuntimeError, 'error: -r: No such revision: ' + opt_rev # The primordial revision is not always 1.1! Go find it. primordial = revision while self.prev_revision.get(primordial): primordial = self.prev_revision[primordial] # Don't display file at all, if -m option is specified and no # changes have been made in the specified file. if opt_m_timestamp and self.timestamp[revision] < opt_m_timestamp: return '' # Figure out how many lines were in the primordial, i.e. version 1.1, # check-in by moving backward in time from the head revision to the # first revision. line_count = 0 if self.revision_deltatext.get(self.head_revision): tmp_array = self.deltatext_split(self.head_revision) line_count = len(tmp_array) skip = 0 rev = self.prev_revision.get(self.head_revision) while rev: diffs = self.deltatext_split(rev) for command in diffs: dmatch = self.d_command.match(command) amatch = self.a_command.match(command) if skip > 0: # Skip insertion lines from a prior "a" command skip = skip - 1 elif dmatch: # "d" - Delete command start_line = int(dmatch.group(1)) count = int(dmatch.group(2)) line_count = line_count - count elif amatch: # "a" - Add command start_line = int(amatch.group(1)) count = int(amatch.group(2)) skip = count line_count = line_count + count else: raise RuntimeError, 'error: illegal RCS file' rev = self.prev_revision.get(rev) # Now, play the delta edit commands *backwards* from the primordial # revision forward, but rather than applying the deltas to the text of # each revision, apply the changes to an array of revision numbers. # This creates a "revision map" -- an array where each element # represents a line of text in the given revision but contains only # the revision number in which the line was introduced rather than # the line text itself. # # Note: These are backward deltas for revisions on the trunk and # forward deltas for branch revisions. # Create initial revision map for primordial version. self.revision_map = [primordial] * line_count ancestors = [revision, ] + self.ancestor_revisions(revision) ancestors = ancestors[:-1] # Remove "1.1" last_revision = primordial ancestors.reverse() for revision in ancestors: is_trunk_revision = self.trunk_rev.match(revision) is not None if is_trunk_revision: diffs = self.deltatext_split(last_revision) # Revisions on the trunk specify deltas that transform a # revision into an earlier revision, so invert the translation # of the 'diff' commands. for command in diffs: if skip > 0: skip = skip - 1 else: dmatch = self.d_command.match(command) amatch = self.a_command.match(command) if dmatch: start_line = int(dmatch.group(1)) count = int(dmatch.group(2)) temp = [] while count > 0: temp.append(revision) count = count - 1 self.revision_map = (self.revision_map[:start_line - 1] + temp + self.revision_map[start_line - 1:]) elif amatch: start_line = int(amatch.group(1)) count = int(amatch.group(2)) del self.revision_map[start_line:start_line + count] skip = count else: raise RuntimeError, 'Error parsing diff commands' else: # Revisions on a branch are arranged backwards from those on # the trunk. They specify deltas that transform a revision # into a later revision. adjust = 0 diffs = self.deltatext_split(revision) for command in diffs: if skip > 0: skip = skip - 1 else: dmatch = self.d_command.match(command) amatch = self.a_command.match(command) if dmatch: start_line = int(dmatch.group(1)) count = int(dmatch.group(2)) adj_begin = start_line + adjust - 1 adj_end = start_line + adjust - 1 + count del self.revision_map[adj_begin:adj_end] adjust = adjust - count elif amatch: start_line = int(amatch.group(1)) count = int(amatch.group(2)) skip = count temp = [] while count > 0: temp.append(revision) count = count - 1 self.revision_map = (self.revision_map[:start_line + adjust] + temp + self.revision_map[start_line + adjust:]) adjust = adjust + skip else: raise RuntimeError, 'Error parsing diff commands' last_revision = revision return revision
def parse_cvs_file(self, rcs_pathname, opt_rev = None, opt_m_timestamp = None): # Args in: opt_rev - requested revision # opt_m - time since modified # Args out: revision_map # timestamp # revision_deltatext # CheckHidden(rcs_pathname) try: rcsfile = open(rcs_pathname, 'rb') except: raise RuntimeError, ('error: %s appeared to be under CVS control, ' + 'but the RCS file is inaccessible.') % rcs_pathname rcsparse.parse(rcsfile, self) rcsfile.close() if opt_rev in [None, '', 'HEAD']: # Explicitly specified topmost revision in tree revision = self.head_revision else: # Symbolic tag or specific revision number specified. revision = self.map_tag_to_revision(opt_rev) if revision == '': raise RuntimeError, 'error: -r: No such revision: ' + opt_rev # The primordial revision is not always 1.1! Go find it. primordial = revision while self.prev_revision.get(primordial): primordial = self.prev_revision[primordial] # Don't display file at all, if -m option is specified and no # changes have been made in the specified file. if opt_m_timestamp and self.timestamp[revision] < opt_m_timestamp: return '' # Figure out how many lines were in the primordial, i.e. version 1.1, # check-in by moving backward in time from the head revision to the # first revision. line_count = 0 if self.revision_deltatext.get(self.head_revision): tmp_array = self.deltatext_split(self.head_revision) line_count = len(tmp_array) skip = 0 rev = self.prev_revision.get(self.head_revision) while rev: diffs = self.deltatext_split(rev) for command in diffs: dmatch = self.d_command.match(command) amatch = self.a_command.match(command) if skip > 0: # Skip insertion lines from a prior "a" command skip = skip - 1 elif dmatch: # "d" - Delete command start_line = string.atoi(dmatch.group(1)) count = string.atoi(dmatch.group(2)) line_count = line_count - count elif amatch: # "a" - Add command start_line = string.atoi(amatch.group(1)) count = string.atoi(amatch.group(2)) skip = count line_count = line_count + count else: raise RuntimeError, 'error: illegal RCS file' rev = self.prev_revision.get(rev) # Now, play the delta edit commands *backwards* from the primordial # revision forward, but rather than applying the deltas to the text of # each revision, apply the changes to an array of revision numbers. # This creates a "revision map" -- an array where each element # represents a line of text in the given revision but contains only # the revision number in which the line was introduced rather than # the line text itself. # # Note: These are backward deltas for revisions on the trunk and # forward deltas for branch revisions. # Create initial revision map for primordial version. self.revision_map = [primordial] * line_count ancestors = [revision, ] + self.ancestor_revisions(revision) ancestors = ancestors[:-1] # Remove "1.1" last_revision = primordial ancestors.reverse() for revision in ancestors: is_trunk_revision = self.trunk_rev.match(revision) is not None if is_trunk_revision: diffs = self.deltatext_split(last_revision) # Revisions on the trunk specify deltas that transform a # revision into an earlier revision, so invert the translation # of the 'diff' commands. for command in diffs: if skip > 0: skip = skip - 1 else: dmatch = self.d_command.match(command) amatch = self.a_command.match(command) if dmatch: start_line = string.atoi(dmatch.group(1)) count = string.atoi(dmatch.group(2)) temp = [] while count > 0: temp.append(revision) count = count - 1 self.revision_map = (self.revision_map[:start_line - 1] + temp + self.revision_map[start_line - 1:]) elif amatch: start_line = string.atoi(amatch.group(1)) count = string.atoi(amatch.group(2)) del self.revision_map[start_line:start_line + count] skip = count else: raise RuntimeError, 'Error parsing diff commands' else: # Revisions on a branch are arranged backwards from those on # the trunk. They specify deltas that transform a revision # into a later revision. adjust = 0 diffs = self.deltatext_split(revision) for command in diffs: if skip > 0: skip = skip - 1 else: dmatch = self.d_command.match(command) amatch = self.a_command.match(command) if dmatch: start_line = string.atoi(dmatch.group(1)) count = string.atoi(dmatch.group(2)) adj_begin = start_line + adjust - 1 adj_end = start_line + adjust - 1 + count del self.revision_map[adj_begin:adj_end] adjust = adjust - count elif amatch: start_line = string.atoi(amatch.group(1)) count = string.atoi(amatch.group(2)) skip = count temp = [] while count > 0: temp.append(revision) count = count - 1 self.revision_map = (self.revision_map[:start_line + adjust] + temp + self.revision_map[start_line + adjust:]) adjust = adjust + skip else: raise RuntimeError, 'Error parsing diff commands' last_revision = revision return revision