A working version of vclib.ccvs. Need a lot of debugging. Writing a driver

(called bincvs) for vclib that uses rlog and co binary rcs commands.
Added a few tests scripts.


git-svn-id: http://viewvc.tigris.org/svn/viewvc/trunk@500 8cb11bc2-c004-0410-86c3-e597b4017df7
remotes/tags/1.0.0-rc1
lbruand 2002-04-13 09:43:22 +00:00
parent 6777801f27
commit 25b5885a6b
6 changed files with 864 additions and 26 deletions

View File

@ -23,7 +23,9 @@ such as CVS.
# ======================================================================
#
# TODO: Add a last modified property
#
class Repository:
"""
Abstract class representing a repository.
@ -91,10 +93,10 @@ class Repository:
gets the properties.
"""
def _getvf_co(self, target, path):
def _getvf_cofile(self, target, path):
"""
should return a file object representing the checked out revision.
Notice that _getvf_co can also adds the properties in <target> the
Notice that _getvf_co can also add the properties in <target> the
way _getvf_properties does.
Developers: method to be overloaded.
@ -118,9 +120,8 @@ class Versfile:
if not isinstance(repository,Repository):
raise TypeError(repository)
self.repository=repository
# path is assumed to be of correct type.
# TODO: do the actual checking.
self.path=path
if tree!=None:
self.tree=tree
@ -166,7 +167,7 @@ class Revision:
raise AttributeError()
def checkout(self):
return self.versfile._getvf_co(self)
return self.versfile._getvf_cofile(self)
# Here are the shortcuts methods.
def getprevious(self):

View File

@ -0,0 +1,467 @@
# -*-python-*-
#
#
# Copyright (C) 1999-2002 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewCVS
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
#
# Contact information:
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
# gstein@lyra.org, http://viewcvs.sourceforge.net/
#
# -----------------------------------------------------------------------
"""Version Control lib driver for locally accessible cvs-repositories.
"""
# ======================================================================
from vclib import Repository, Versfile, Revision
import os
import os.path
import string
import re
import exceptions
import popen
class LogHeader:
"Hold state from the header portion of an 'rlog' output."
def __init__(self, filename, head=None, branch=None, taginfo=None):
self.filename = filename
self.head = head
self.branch = branch
self.taginfo = taginfo
class LogEntry:
"Hold state for each revision entry in an 'rlog' output."
def __init__(self, rev, date, author, state, changed, log):
self.rev = rev
self.date = date
self.author = author
self.state = state
self.changed = changed
self.log = log
def parse_log_header(fp):
"""Parse and RCS/CVS log header.
fp is a file (pipe) opened for reading the log information.
On entry, fp should point to the start of a log entry.
On exit, fp will have consumed the separator line between the header and
the first revision log.
If there is no revision information (e.g. the "-h" switch was passed to
rlog), then fp will consumed the file separator line on exit.
"""
filename = head = branch = None
taginfo = { } # tag name => revision
parsing_tags = 0
eof = None
while 1:
line = fp.readline()
if not line:
# the true end-of-file
eof = _EOF_LOG
break
if parsing_tags:
if line[0] == '\t':
[ tag, rev ] = map(string.strip, string.split(line, ':'))
taginfo[tag] = rev
else:
# oops. this line isn't tag info. stop parsing tags.
parsing_tags = 0
if not parsing_tags:
if line[:9] == 'RCS file:':
# remove the trailing ,v
filename = line[10:-3]
elif line[:5] == 'head:':
head = line[6:-1]
elif line[:7] == 'branch:':
branch = line[8:-1]
elif line[:14] == 'symbolic names':
# start parsing the tag information
parsing_tags = 1
elif line == ENTRY_END_MARKER:
# end of the headers
break
elif line == LOG_END_MARKER:
# end of this file's log information
eof = _EOF_FILE
break
elif line[:6] == 'rlog: ':
# rlog: filename/goes/here,v: error message
idx = string.find(line, ':', 6)
if idx != -1:
if line[idx:idx+32] == ': warning: Unknown phrases like ':
# don't worry about this warning. it can happen with some RCS
# files that have unknown fields in them (e.g. "permissions 644;"
continue
# looks like a filename
filename = line[6:idx]
if filename[-2:] == ',v':
filename = filename[:-2]
return LogHeader(filename), _EOF_ERROR
# dunno what this is
return LogHeader(filename, head, branch, taginfo), eof
_re_log_info = re.compile(r'^date:\s+([^;]+);'
r'\s+author:\s+([^;]+);'
r'\s+state:\s+([^;]+);'
r'(\s+lines:\s+([0-9\s+-]+))?\n$')
### _re_rev should be updated to extract the "locked" flag
_re_rev = re.compile(r'^revision\s+([0-9.]+).*')
def parse_log_entry(fp):
"""Parse a single log entry.
On entry, fp should point to the first line of the entry (the "revision"
line).
On exit, fp will have consumed the log separator line (dashes) or the
end-of-file marker (equals).
Returns: revision, date (time_t secs), author, state, lines changed,
the log text, and eof flag (see _EOF_*)
"""
rev = None
line = fp.readline()
if not line:
return None, _EOF_LOG
if line[:8] == 'revision':
match = _re_rev.match(line)
if not match:
return None, _EOF_LOG
rev = match.group(1)
line = fp.readline()
if not line:
return None, _EOF_LOG
match = _re_log_info.match(line)
eof = None
log = ''
while 1:
line = fp.readline()
if not line:
# true end-of-file
eof = _EOF_LOG
break
if line[:9] == 'branches:':
continue
if line == ENTRY_END_MARKER:
break
if line == LOG_END_MARKER:
# end of this file's log information
eof = _EOF_FILE
break
log = log + line
if not rev or not match:
# there was a parsing error
return None, eof
# parse out a time tuple for the local time
tm = compat.cvs_strptime(match.group(1))
try:
date = int(time.mktime(tm)) - time.timezone
except OverflowError:
# it is possible that CVS recorded an "illegal" time, such as those
# which occur during a Daylight Savings Time switchover (there is a
# gap in the time continuum). Let's advance one hour and try again.
# While the time isn't necessarily "correct", recall that the gap means
# that times *should* be an hour forward. This is certainly close enough
# for our needs.
#
# Note: a true overflow will simply raise an error again, which we won't
# try to catch a second time.
tm = tm[:3] + (tm[3] + 1,) + tm[4:]
date = int(time.mktime(tm)) - time.timezone
return LogEntry(rev, date,
# author, state, lines changed
match.group(2), match.group(3), match.group(5),
log), eof
def skip_file(fp):
"Skip the rest of a file's log information."
while 1:
line = fp.readline()
if not line:
break
if line == LOG_END_MARKER:
break
def process_rlog_output(rlog, full_name, view_tag, fileinfo, alltags):
"Fill in fileinfo and alltags with info from the rlog output."
# consume each file found in the resulting log
while 1:
revwanted = None
branch = None
branchpoint = None
header, eof = parse_log_header(rlog)
filename = header.filename
head = header.head
branch = header.branch
symrev = header.taginfo
# the rlog output is done
if eof == _EOF_LOG:
break
if filename:
# convert from absolute to relative
if filename[:len(full_name)] == full_name:
filename = filename[len(full_name)+1:]
# for a subdir (not Attic files!), use the subdir for a key
idx = string.find(filename, '/')
if idx != -1 and filename[:6] != 'Attic/':
info_key = filename[:idx]
else:
info_key = filename
# an error was found regarding this file
if eof == _EOF_ERROR:
fileinfo[info_key] = _FILE_HAD_ERROR
continue
# if we hit the end of the log information (already!), then there is
# nothing we can do with this file
if eof:
continue
if not filename or not head:
# parsing error. skip the rest of this file.
skip_file(rlog)
continue
if not branch:
idx = string.rfind(head, '.')
branch = head[:idx]
idx = string.rfind(branch, '.')
if idx == -1:
branch = '0.' + branch
else:
branch = branch[:idx] + '.0' + branch[idx:]
symrev['MAIN'] = symrev['HEAD'] = branch
if symrev.has_key(view_tag):
revwanted = symrev[view_tag]
if revwanted[:2] == '0.': ### possible?
branch = revwanted[2:]
else:
idx = string.find(revwanted, '.0.')
if idx == -1:
branch = revwanted
else:
branch = revwanted[:idx] + revwanted[idx+2:]
if revwanted != branch:
revwanted = None
idx = string.rfind(branch, '.')
if idx == -1:
branchpoint = ''
else:
branchpoint = branch[:idx]
elif view_tag:
# the tag wasn't found, so skip this file
skip_file(rlog)
continue
# we don't care about the values -- just the keys. this the fastest
# way to merge the set of keys
alltags.update(symrev)
# read all of the log entries until we find the revision we want
while 1:
# fetch one of the log entries
entry, eof = parse_log_entry(rlog)
if not entry:
# parsing error
if not eof:
skip_file(rlog)
break
rev = entry.rev
idx = string.rfind(rev, '.')
revbranch = rev[:idx]
if not view_tag or (not revwanted and branch == revbranch):
revwanted = rev
if rev == revwanted or rev == branchpoint:
fileinfo[info_key] = (rev, entry.date, entry.log, entry.author,
filename, entry.state)
if rev == revwanted:
# done with this file now
if not eof:
skip_file(rlog)
break
# if we hit the true EOF, or just this file's end-of-info, then we are
# done collecting log entries.
if eof:
break
def get_logs(full_name, files, view_tag):
if len(files) == 0:
return { }, { }
fileinfo = { }
alltags = { # all the tags seen in the files of this dir
'MAIN' : '1',
'HEAD' : '1',
}
chunk_size = 100
while files:
chunk = files[:chunk_size]
del files[:chunk_size]
# prepend the full pathname for each file
for i in range(len(chunk)):
chunk[i] = full_name + '/' + chunk[i]
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
chunk = ('-r',) + tuple(chunk)
rlog = popen.popen(os.path.normpath(os.path.join(cfg.general.rcs_path,'rlog')), chunk, 'r')
process_rlog_output(rlog, full_name, view_tag, fileinfo, alltags)
### it would be nice to verify that we got SOMETHING from rlog about
### each file. if we didn't, then it could be that the chunk is still
### too large, so we want to cut the chunk_size in half and try again.
###
### BUT: if we didn't get feedback for some *other* reason, then halving
### the chunk size could merely send us into a needless retry loop.
###
### more work for later...
status = rlog.close()
if status:
raise 'error during rlog: '+hex(status)
return fileinfo, alltags
class BinCVSRepository:
def __init__(self,name,basepath):
self.name=name
self.basepath=basepath
if self.basepath[-1:]!=os.sep:
self.basepath=self.basepath+os.sep
def _getpath(self,pathname):
return self.basepath+string.join(pathname,os.sep)
def _getrcsname(self,filename):
if filename[-2:]==',v':
return filename
else:
return filename+',v'
def getfile(self,pathname):
if os.path.isfile(self._getrcsname(self._getpath(pathname))):
return Versfile(self,self._getrcsname(self._getpath(pathname)) )
raise exceptions.IOError("File not found %s in repository %s"% (self._getpath(pathname),self.name) )
def getsubdirs(self,path):
h=os.listdir(self._getpath(path))
g=[]
for i in h:
if os.path.isdir(self._getpath(path+[i])):
g.append(i)
return g
def getfiles(self,path):
h=os.listdir(self._getpath(path))
g={}
for i in h:
ci=self._getrcsname(self._getpath(path+[i]))
if os.path.isfile(ci):
g[i]=Versfile(self,ci)
return g
# Private methods ( accessed by Versfile and Revision )
def _getvf_info(self,target, path):
"""
This method will had to <target> (expect to be an instance of Versfile)
a certain number of attributes:
head (string)
age (int timestamp)
author (string)
log
branch
... ( there can be other stuff here)
Developers: method to be overloaded.
"""
if not os.path.isfile(path):
raise "Unknown file: %s " % path
def _getvf_tree(self,versfile):
"""
should return a dictionary of Revisions
Developers: method to be overloaded.
"""
def _getvf_properties(self,target,path,revisionnumber):
"""
Add/update into target's attributes (expected to be an instance of
Revision) a certain number of attributes:
rev
date
author
state
log
previous revision number
branches ( a list of revision numbers )
changes ( string of the form: e.g. "+1 -0 lines" )
tags
... ( there can be other stuff here)
Developers: in the cvs implementation, the method will never be called.
There is no point in developping this method as _getvf_tree already
gets the properties.
"""
def _getvf_cofile(self, target, path):
"""
should return a file object representing the checked out revision.
Notice that _getvf_co can also add the properties in <target> the
way _getvf_properties does.
Developers: method to be overloaded.
"""
fp = popen.popen('co',
('-p'+rev, os.path.join(repo,file) ), 'r')
fp.readline()
fp.readline()
return fp

190
lib/vclib/bincvs/popen.py Normal file
View File

@ -0,0 +1,190 @@
#
# Copyright (C) 2000-2001 The ViewCVS Group. All Rights Reserved.
#
# By using this file, you agree to the terms and conditions set forth in
# the LICENSE.html file which can be found at the top level of the ViewCVS
# distribution or at http://viewcvs.sourceforge.net/license-1.html.
#
# Contact information:
# Greg Stein, PO Box 760, Palo Alto, CA, 94302
# gstein@lyra.org, http://viewcvs.sourceforge.net/
#
# -----------------------------------------------------------------------
#
# popen.py: a replacement for os.popen()
#
# This implementation of popen() provides a cmd + args calling sequence,
# rather than a system() type of convention. The shell facilities are not
# available, but that implies we can avoid worrying about shell hacks in
# the arguments.
#
# -----------------------------------------------------------------------
#
import os
import sys
def popen(cmd, args, mode, capture_err=1):
# flush the stdio buffers since we are about to change the FD under them
sys.stdout.flush()
sys.stderr.flush()
r, w = os.pipe()
pid = os.fork()
if pid:
# in the parent
# close the descriptor that we don't need and return the other one.
if mode == 'r':
os.close(w)
return _pipe(os.fdopen(r, 'r'), pid)
os.close(r)
return _pipe(os.fdopen(w, 'w'), pid)
# in the child
# we'll need /dev/null for the discarded I/O
null = os.open('/dev/null', os.O_RDWR)
if mode == 'r':
# hook stdout/stderr to the "write" channel
os.dup2(w, 1)
# "close" stdin; the child shouldn't use it
### this isn't quite right... we may want the child to read from stdin
os.dup2(null, 0)
# what to do with errors?
if capture_err:
os.dup2(w, 2)
else:
os.dup2(null, 2)
else:
# hook stdin to the "read" channel
os.dup2(r, 0)
# "close" stdout/stderr; the child shouldn't use them
### this isn't quite right... we may want the child to write to these
os.dup2(null, 1)
os.dup2(null, 2)
# don't need these FDs any more
os.close(null)
os.close(r)
os.close(w)
# the stdin/stdout/stderr are all set up. exec the target
try:
os.execvp(cmd, (cmd,) + tuple(args))
except:
# aid debugging, if the os.execvp above fails for some reason:
import string
print "<h2>exec failed:</h2><pre>", cmd, string.join(args), "</pre>"
raise
# crap. shouldn't be here.
sys.exit(127)
def pipe_cmds(cmds):
# flush the stdio buffers since we are about to change the FD under them
sys.stdout.flush()
sys.stderr.flush()
prev_r, parent_w = os.pipe()
null = os.open('/dev/null', os.O_RDWR)
for cmd in cmds[:-1]:
r, w = os.pipe()
pid = os.fork()
if not pid:
# in the child
# hook up stdin to the "read" channel
os.dup2(prev_r, 0)
# hook up stdout to the output channel
os.dup2(w, 1)
# toss errors
os.dup2(null, 2)
# close these extra descriptors
os.close(prev_r)
os.close(parent_w)
os.close(null)
os.close(r)
os.close(w)
# time to run the command
try:
os.execvp(cmd[0], cmd)
except:
pass
sys.exit(127)
# in the parent
# we don't need these any more
os.close(prev_r)
os.close(w)
# the read channel of this pipe will feed into to the next command
prev_r = r
# no longer needed
os.close(null)
# done with most of the commands. set up the last command to write to stdout
pid = os.fork()
if not pid:
# in the child (the last command)
# hook up stdin to the "read" channel
os.dup2(prev_r, 0)
# close these extra descriptors
os.close(prev_r)
os.close(parent_w)
# run the last command
try:
os.execvp(cmds[-1][0], cmds[-1])
except:
pass
sys.exit(127)
# not needed any more
os.close(prev_r)
# write into the first pipe, wait on the final process
return _pipe(os.fdopen(parent_w, 'w'), pid)
class _pipe:
"Wrapper for a file which can wait() on a child process at close time."
def __init__(self, file, child_pid):
self.file = file
self.child_pid = child_pid
def eof(self):
pid, status = os.waitpid(self.child_pid, os.WNOHANG)
if pid:
self.file.close()
self.file = None
return status
return None
def close(self):
if self.file:
self.file.close()
self.file = None
return os.waitpid(self.child_pid, 0)[1]
return None
def __getattr__(self, name):
return getattr(self.file, name)
def __del__(self):
if self.file:
self.close()

View File

@ -19,6 +19,7 @@
"""
This is a Version Control library driver for locally accessible cvs-repositories.
"""
from vclib import Repository, Versfile, Revision
import os
import os.path
@ -26,7 +27,7 @@ import string
import re
import exceptions
import rcsparse
import cStringIO
class InfoSink(rcsparse.Sink):
def __init__(self,target):
@ -63,6 +64,7 @@ class TreeSink(rcsparse.Sink):
def __init__(self,target):
self.target=target
self.tree={}
self.tags={}
def set_head_revision(self, revision):
self.target.__dict__["head"]=revision
@ -74,8 +76,12 @@ class TreeSink(rcsparse.Sink):
if self.tree.has_key(revision):
self.tree[revision].__dict__["tags"].append(name)
else:
self.tree[revision]=Revision(self.target,revision)
self.tree[revision].__dict__["tags"]=[name]
if revision in self.tags:
self.tags[revision].append(name)
else:
self.tags[revision]=[name]
# self.tree[revision]=Revision(self.target,revision)
# self.tree[revision].__dict__["tags"]=[name]
def define_revision(self, revision, timestamp, author, state,
branches, next):
@ -92,6 +98,11 @@ class TreeSink(rcsparse.Sink):
rev.__dict__["branches"]=branches
rev.__dict__["state"]=state
rev.__dict__["previous"]=next
if revision in self.tags:
rev.__dict__["tags"]=self.tags[revision]
del self.tags[revision]
else:
rev.__dict__["tags"]=[]
def set_revision_info(self, revision, log, text):
if self.tree.has_key(revision):
@ -121,8 +132,66 @@ class TreeSink(rcsparse.Sink):
idx = idx +count
else:
raise "error while parsing deltatext: %s" % command
#rev.getprevious().__dict__["changes"]= "+%d -%d lines" %(deled,added)
rev.__dict__["changes"]= (deled,added)
def gobranch(self,rev):
rev.__dict__["changes"]="+%d -%d lines" % (rev.__dict__["changes"][1],rev.__dict__["changes"][0])
if rev.__dict__["previous"]!=None:
self.gobranch(self.tree[rev.__dict__["previous"]])
for x in rev.__dict__["branches"]:
self.gobranch(self.tree[x])
def gotree(self,rev):
if rev.__dict__["previous"]!=None:
rev.__dict__["changes"]="+%d -%d lines" % self.tree[rev.__dict__["previous"]].__dict__["changes"]
self.gotree(self.tree[rev.__dict__["previous"]])
else:
rev.__dict__["changes"]=""
for x in rev.__dict__["branches"]:
self.gobranch(self.tree[x])
def parse_completed(self):
self.gotree(self.tree[self.target.__dict__["head"]])
class StreamText:
d_command = re.compile('^d(\d+)\\s(\\d+)')
a_command = re.compile('^a(\d+)\\s(\\d+)')
def __init__(self,text,head):
self.next_revision(head)
self.text=string.split(text,"\n")
def command(self,cmd):
adjust = 0
add_lines_remaining = 0
diffs = string.split(cmd,"\n")
if diffs[-1]=="":
del diffs[-1]
if len(diffs)==0:
return
if diffs[0]=="":
del diffs[0]
for command in diffs:
if add_lines_remaining > 0:
# Insertion lines from a prior "a" command
self.text.insert(start_line + adjust, command)
add_lines_remaining = add_lines_remaining - 1
adjust = adjust + 1
continue
dmatch = self.d_command.match(command)
amatch = self.a_command.match(command)
if dmatch:
# "d" - Delete command
start_line = string.atoi(dmatch.group(1))
count = string.atoi(dmatch.group(2))
begin = start_line + adjust - 1
del self.text[begin:begin + count]
adjust = adjust - count
elif amatch:
# "a" - Add command
start_line = string.atoi(amatch.group(1))
count = string.atoi(amatch.group(2))
add_lines_remaining = count
else:
raise RuntimeError, 'Error parsing diff commands'
def next_revision(self,revision):
#print "Revision: %s"% revision
pass
class COSink(rcsparse.Sink):
def __init__(self,target):
@ -130,8 +199,10 @@ class COSink(rcsparse.Sink):
def set_head_revision(self, revision):
self.head=revision
self.position=0
self.path=[revision]
self.pathover=0
self.buffer={}
self.sstext=None
def define_revision(self, revision, timestamp, author, state,
branches, next):
@ -141,11 +212,67 @@ class COSink(rcsparse.Sink):
self.target.__dict__["branches"]=branches
self.target.__dict__["state"]=state
self.target.__dict__["previous"]=next
#self.path.append
if self.path[-1]==self.target.rev:
return
if revision==self.path[-1]:
if self.target.rev[:len(revision)]==revision:
# Branch ?
for x in branches:
if self.target.rev[:len(revision)+3]==x[:len(revision)+3]:
self.path.append(x)
if x in self.buffer:
i=self.buffer[x]
del self.buffer[x]
self.define_revision(x,0,"","",i[0],i[1])
return
else:
print revision
print branches
raise " %s revision doesn't exist " % self.target.rev
else:
# no => next
self.path.append(next)
if self.buffer.has_key(next):
self.path.append(next)
if x in self.buffer:
i=self.buffer[next]
del self.buffer[next]
self.define_revision(next,0,"","",i[0],i[1])
else:
self.buffer[revision]=(branches,next)
def set_revision_info(self, revision, log, text):
pass
def tree_completed(self):
if self.path[-1]!=self.target.rev:
raise "Error Incomplete path"
#print self.path
self.buffer={}
def set_revision_info(self, revision, log, text):
if revision==self.target.rev:
self.target.__dict__["log"]=log
if revision in self.path:
if self.path[self.position]==revision:
if revision==self.head:
self.sstext=StreamText(text,self.head)
else:
self.sstext.next_revision(revision)
self.sstext.command(text)
while self.position+1<len(self.path):
self.position = self.position+1
x= self.path[self.position]
if x not in self.buffer:
break
self.sstext.next_revision(x)
self.sstext.command(self.buffer[x])
del self.buffer[x]
else:
self.buffer[revision]=text
def parse_completed(self):
if self.buffer!={}:
raise "Error buffer not emptied"
class CVSRepository(Repository):
def __init__(self,name,basepath):
self.name=name
@ -166,6 +293,7 @@ class CVSRepository(Repository):
if os.path.isfile(self._getrcsname(self._getpath(pathname))):
return Versfile(self,self._getrcsname(self._getpath(pathname)) )
raise exceptions.IOError("File not found %s in repository %s"% (self._getpath(pathname),self.name) )
def getsubdirs(self,path):
h=os.listdir(self._getpath(path))
g=[]
@ -196,18 +324,12 @@ class CVSRepository(Repository):
if not os.path.isfile(versfile.path):
raise "Unknown file: %s " % versfile.path
sink=TreeSink(versfile)
try:
rcsparse.Parser().parse(open(versfile.path),sink)
except RCSStopParser:
pass
rcsparse.Parser().parse(open(versfile.path),sink)
return sink.tree
def _getvf_co(self, target, path):
def _getvf_cofile(self, target, path):
if not os.path.isfile(path):
raise "Unknown file: %s " % versfile.path
raise "Unknown file: %s " % path
sink=COSink(target)
try:
rcsparse.Parser().parse(open(versfile.path),sink)
except RCSStopParser:
pass
return sink.tree
rcsparse.Parser().parse(open(path),sink)
return cStringIO.StringIO(string.join(sink.sstext.text,"\n"))

53
tests/vclib/co.py Executable file
View File

@ -0,0 +1,53 @@
#!/usr/local/bin/python
import sys, os.path
sys.path.append( os.path.normpath(os.path.join(sys.path[0],"..","..","lib")) )
import vclib.ccvs
import popen
def usage():
print """
co simulation using vclib!!!
python co.py <Path to repository> <(relative) Path to file> <revision>
"""
sys.exit()
def convertpath(s):
a=(s,'')
res=[]
while (a[0]!=''):
a=os.path.split(a[0])
res= [a[1]]+res
return res
def compareco(repo,file,rev):
a=vclib.ccvs.CVSRepository("lucas",repo)
f=a.getfile(convertpath(file)) # example: ["kdelibs","po","Attic","nl.po"]
r=f.tree[rev]
fp1 = r.checkout()
fp2 = popen.popen('co',
('-p'+rev, os.path.join(repo,file) ), 'r')
l1 = fp1.readlines()
l2 = fp2.readlines()
ok=1
for i in range(0,len(l1)-1):
if l1[i] != l2[i+2]:
print " Difference in line %d"% i
print " line from CCVS %s" % l1[i]
print " line from RCS %s" % l2[i+2]
ok=0
return ok
if len(sys.argv)==4:
compareco(sys.argv[1],sys.argv[2],sys.argv[3])
elif len(sys.argv)==3:
a=vclib.ccvs.CVSRepository("lucas",sys.argv[1])
f=a.getfile(convertpath(sys.argv[2])) # example: ["kdelibs","po","Attic","nl.po"]
for rev in f.tree.keys():
print ("revision: %s" % rev),
if compareco(sys.argv[1],sys.argv[2],rev):
print "ok"
else:
print "fail"
else:
usage()

5
tests/vclib/rlog.py Normal file
View File

@ -0,0 +1,5 @@
#!/usr/local/bin/python
import sys, os.path
sys.path.append( os.path.normpath(os.path.join(sys.path[0],"..","..","lib")) )
import vclib.ccvs
import popen