Initial revision
This commit is contained in:
parent
107c747009
commit
5f07b84796
|
@ -0,0 +1,285 @@
|
|||
#! /usr/local/bin/python
|
||||
|
||||
"""RCS Proxy.
|
||||
|
||||
Provide a simplified interface on RCS files, locally or remotely.
|
||||
The functionality is geared towards implementing some sort of
|
||||
remote CVS like utility. It is modeled after the similar module
|
||||
FSProxy.
|
||||
|
||||
The module defines three classes:
|
||||
|
||||
RCSProxyLocal -- used for local access
|
||||
RCSProxyServer -- used on the server side of remote access
|
||||
RCSProxyClient -- used on the client side of remote access
|
||||
|
||||
The remote classes are instantiated with an IP address and an optional
|
||||
verbosity flag.
|
||||
"""
|
||||
|
||||
import server
|
||||
import client
|
||||
import md5
|
||||
import os
|
||||
import fnmatch
|
||||
import string
|
||||
import tempfile
|
||||
|
||||
|
||||
okchars = string.letters + string.digits + '-_=+.'
|
||||
|
||||
|
||||
class RCSProxyLocal:
|
||||
|
||||
def __init__(self):
|
||||
self._dirstack = []
|
||||
|
||||
def _close(self):
|
||||
while self._dirstack:
|
||||
self.back()
|
||||
|
||||
def pwd(self):
|
||||
return os.getcwd()
|
||||
|
||||
def cd(self, name):
|
||||
save = os.getcwd()
|
||||
os.chdir(name)
|
||||
self._dirstack.append(save)
|
||||
|
||||
def back(self):
|
||||
if not self._dirstack:
|
||||
raise os.error, "empty directory stack"
|
||||
dir = self._dirstack[-1]
|
||||
os.chdir(dir)
|
||||
del self._dirstack[-1]
|
||||
|
||||
def _filter(self, files, pat = None):
|
||||
if pat:
|
||||
def keep(name, pat = pat):
|
||||
return fnmatch.fnmatch(name, pat)
|
||||
files = filter(keep, files)
|
||||
files.sort()
|
||||
return files
|
||||
|
||||
def isfile(self, name):
|
||||
namev = name + ',v'
|
||||
return os.path.isfile(namev) or \
|
||||
os.path.isfile(os.path.join('RCS', namev))
|
||||
|
||||
def _unmangle(self, name):
|
||||
if type(name) == type(''):
|
||||
rev = ''
|
||||
else:
|
||||
name, rev = name
|
||||
return name, rev
|
||||
|
||||
def checkfile(self, name):
|
||||
name, rev = self._unmangle(name)
|
||||
if not self.isfile(name):
|
||||
raise os.error, 'not an rcs file %s' % `name`
|
||||
for c in rev:
|
||||
if c not in okchars:
|
||||
raise ValueError, "bad char in rev"
|
||||
return name, rev
|
||||
|
||||
def listfiles(self, pat = None):
|
||||
def isrcs(name): return name[-2:] == ',v'
|
||||
def striprcs(name): return name[:-2]
|
||||
files = os.listdir(os.curdir)
|
||||
files = filter(isrcs, files)
|
||||
if os.path.isdir('RCS'):
|
||||
files2 = os.listdir('RCS')
|
||||
files2 = filter(isrcs, files2)
|
||||
files = files + files2
|
||||
files = map(striprcs, files)
|
||||
return self._filter(files, pat)
|
||||
|
||||
def listsubdirs(self, pat = None):
|
||||
files = os.listdir(os.curdir)
|
||||
files = filter(os.path.isdir, files)
|
||||
return self._filter(files, pat)
|
||||
|
||||
def isdir(self, name):
|
||||
return os.path.isdir(name)
|
||||
|
||||
def _open(self, name, cmd = 'co -p'):
|
||||
name, rev = self.checkfile(name)
|
||||
namev = name + ',v'
|
||||
if rev:
|
||||
cmd = cmd + ' -r' + rev
|
||||
return os.popen('%s %s' % (cmd, `namev`))
|
||||
|
||||
def _closepipe(self, f):
|
||||
sts = f.close()
|
||||
if sts:
|
||||
raise IOError, "Exit status %d" % sts
|
||||
|
||||
def _remove(self, fn):
|
||||
try:
|
||||
os.unlink(fn)
|
||||
except os.error:
|
||||
pass
|
||||
|
||||
def sum(self, name):
|
||||
f = self._open(name)
|
||||
BUFFERSIZE = 1024*8
|
||||
sum = md5.new()
|
||||
while 1:
|
||||
buffer = f.read(BUFFERSIZE)
|
||||
if not buffer:
|
||||
break
|
||||
sum.update(buffer)
|
||||
self._closepipe(f)
|
||||
return sum.digest()
|
||||
|
||||
def _list(self, function, list):
|
||||
if list is None:
|
||||
list = self.listfiles()
|
||||
res = []
|
||||
for name in list:
|
||||
try:
|
||||
res.append((name, function(name)))
|
||||
except (os.error, IOError):
|
||||
res.append((name, None))
|
||||
return res
|
||||
|
||||
def sumlist(self, list = None):
|
||||
return self.list(self.sum, list)
|
||||
|
||||
def _dict(self, function, list):
|
||||
if list is None:
|
||||
list = self.listfiles()
|
||||
dict = {}
|
||||
for name in list:
|
||||
try:
|
||||
dict[name] = function(name)
|
||||
except (os.error, IOError):
|
||||
pass
|
||||
return dict
|
||||
|
||||
def sumdict(self, list = None):
|
||||
return self.dict(self.sum, list)
|
||||
|
||||
def get(self, name):
|
||||
f = self._open(name)
|
||||
data = f.read()
|
||||
self._closepipe(f)
|
||||
return data
|
||||
|
||||
def info(self, name):
|
||||
f = self._open(name, 'rlog -h')
|
||||
dict = {}
|
||||
while 1:
|
||||
line = f.readline()
|
||||
if not line: break
|
||||
if line[0] == '\t':
|
||||
continue # XXX lock details, later
|
||||
i = string.find(line, ':')
|
||||
if i > 0:
|
||||
key, value = line[:i], string.strip(line[i+1:])
|
||||
dict[key] = value
|
||||
self._closepipe(f)
|
||||
return dict
|
||||
|
||||
def head(self, name):
|
||||
dict = self.info(name)
|
||||
return dict['head']
|
||||
|
||||
def log(self, name, flags = ''):
|
||||
f = self._open(name, 'rlog %s 2>&1' % flags)
|
||||
log = f.read()
|
||||
self._closepipe(f)
|
||||
return log
|
||||
|
||||
def put(self, fullname, data, message = ""):
|
||||
if message and message[-1] != '\n':
|
||||
message = message + '\n'
|
||||
name, rev = self._unmangle(fullname)
|
||||
new = not self.isfile(name)
|
||||
if new:
|
||||
for c in name:
|
||||
if c not in okchars:
|
||||
raise ValueError, "bad char in name"
|
||||
else:
|
||||
self._remove(name)
|
||||
f = open(name, 'w')
|
||||
f.write(data)
|
||||
f.close()
|
||||
tf = tempfile.mktemp()
|
||||
try:
|
||||
if not new:
|
||||
cmd = "rcs -l%s %s >>%s 2>&1" % (rev, name, tf)
|
||||
sts = os.system(cmd)
|
||||
if sts:
|
||||
raise IOError, "rcs -l exit status %d" % sts
|
||||
cmd = "ci -r%s %s >>%s 2>&1" % (rev, name, tf)
|
||||
p = os.popen(cmd, 'w')
|
||||
p.write(message)
|
||||
sts = p.close()
|
||||
if sts:
|
||||
raise IOError, "ci exit status %d" % sts
|
||||
messages = open(tf).read()
|
||||
return messages or None
|
||||
finally:
|
||||
self._remove(tf)
|
||||
|
||||
def mkdir(self, name):
|
||||
os.mkdir(name, 0777)
|
||||
|
||||
def rmdir(self, name):
|
||||
os.rmdir(name)
|
||||
|
||||
|
||||
class RCSProxyServer(RCSProxyLocal, server.Server):
|
||||
|
||||
def __init__(self, address, verbose = server.VERBOSE):
|
||||
RCSProxyLocal.__init__(self)
|
||||
server.Server.__init__(self, address, verbose)
|
||||
|
||||
def _close(self):
|
||||
server.Server._close(self)
|
||||
RCSProxyLocal._close(self)
|
||||
|
||||
def _serve(self):
|
||||
server.Server._serve(self)
|
||||
# Retreat into start directory
|
||||
while self._dirstack: self.back()
|
||||
|
||||
|
||||
class RCSProxyClient(client.Client):
|
||||
|
||||
def __init__(self, address, verbose = client.VERBOSE):
|
||||
client.Client.__init__(self, address, verbose)
|
||||
|
||||
|
||||
def test_server():
|
||||
import string
|
||||
import sys
|
||||
if sys.argv[1:]:
|
||||
port = string.atoi(sys.argv[1])
|
||||
else:
|
||||
port = 4127
|
||||
proxy = RCSProxyServer(('', port))
|
||||
proxy._serverloop()
|
||||
|
||||
|
||||
def test():
|
||||
import sys
|
||||
if not sys.argv[1:] or sys.argv[1] and sys.argv[1][0] in '0123456789':
|
||||
test_server()
|
||||
sys.exit(0)
|
||||
proxy = RCSProxyLocal()
|
||||
what = sys.argv[1]
|
||||
if hasattr(proxy, what):
|
||||
attr = getattr(proxy, what)
|
||||
if callable(attr):
|
||||
print apply(attr, tuple(sys.argv[2:]))
|
||||
else:
|
||||
print `attr`
|
||||
else:
|
||||
print "%s: no such attribute" % what
|
||||
sys.exit(2)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
test()
|
|
@ -0,0 +1,2 @@
|
|||
This directory contains various modules and classes that support
|
||||
remote file system operations
|
|
@ -0,0 +1,186 @@
|
|||
"""Utilities to read and write CVS admin files (esp. CVS/Entries)"""
|
||||
|
||||
import string
|
||||
import os
|
||||
import time
|
||||
|
||||
|
||||
class Entry:
|
||||
|
||||
"""Class representing one (parsed) line from CVS/Entries"""
|
||||
|
||||
def __init__(self, line):
|
||||
words = string.splitfields(line, '/')
|
||||
self.file = words[1]
|
||||
self.rev = words[2]
|
||||
dates = words[3] # ctime, mtime
|
||||
if dates[:7] == 'Initial':
|
||||
self.ctime = None
|
||||
self.mtime = None
|
||||
self.new = 1
|
||||
else:
|
||||
self.ctime = unctime(dates[:24])
|
||||
self.mtime = unctime(dates[25:])
|
||||
self.new = 0
|
||||
self.extra = words[4]
|
||||
self.sum = None
|
||||
|
||||
def unparse(self):
|
||||
if self.new:
|
||||
dates = "Initial %s" % self.file
|
||||
else:
|
||||
dates = gmctime(self.ctime) + ' ' + gmctime(self.mtime)
|
||||
return "/%s/%s/%s/%s/\n" % (
|
||||
self.file,
|
||||
self.rev,
|
||||
dates,
|
||||
self.extra)
|
||||
|
||||
def setsum(self, sum):
|
||||
self.sum = sum
|
||||
|
||||
def getsum(self):
|
||||
return self.sum
|
||||
|
||||
def sethexsum(self, hexsum):
|
||||
self.setsum(unhexify(hexsum))
|
||||
|
||||
def gethexsum(self):
|
||||
if self.sum:
|
||||
return hexify(self.sum)
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class CVS:
|
||||
|
||||
"""Class representing the contents of CVS/Entries (and CVS/Sums)"""
|
||||
|
||||
def __init__(self):
|
||||
self.readentries()
|
||||
|
||||
def readentries(self):
|
||||
self.entries = {}
|
||||
f = self.cvsopen("Entries")
|
||||
while 1:
|
||||
line = f.readline()
|
||||
if not line: break
|
||||
e = Entry(line)
|
||||
self.entries[e.file] = e
|
||||
f.close()
|
||||
|
||||
def readsums(self):
|
||||
try:
|
||||
f = self.cvsopen("Sums")
|
||||
except IOError:
|
||||
return
|
||||
while 1:
|
||||
line = f.readline()
|
||||
if not line: break
|
||||
words = string.split(line)
|
||||
[file, rev, hexsum] = words
|
||||
e = self.entries[file]
|
||||
if e.rev == rev:
|
||||
e.sethexsum(hexsum)
|
||||
f.close()
|
||||
|
||||
def writeentries(self):
|
||||
f = self.cvsopen("Entries", 'w')
|
||||
for file in self.keys():
|
||||
f.write(self.entries[file].unparse())
|
||||
f.close()
|
||||
|
||||
def writesums(self):
|
||||
if self.cvsexists("Sums"):
|
||||
f = self.cvsopen("Sums", 'w')
|
||||
else:
|
||||
f = None
|
||||
for file in self.keys():
|
||||
e = self.entries[file]
|
||||
hexsum = e.gethexsum()
|
||||
if hexsum:
|
||||
if not f:
|
||||
f = self.cvsopen("Sums", 'w')
|
||||
f.write("%s %s %s\n" % (file, e.rev, hexsum))
|
||||
if f:
|
||||
f.close()
|
||||
|
||||
def keys(self):
|
||||
keys = self.entries.keys()
|
||||
keys.sort()
|
||||
return keys
|
||||
|
||||
def cvsexists(self, file):
|
||||
file = os.path.join("CVS", file)
|
||||
return os.path.exists(file)
|
||||
|
||||
def cvsopen(self, file, mode = 'r'):
|
||||
file = os.path.join("CVS", file)
|
||||
if 'r' not in mode:
|
||||
self.backup(file)
|
||||
return open(file, mode)
|
||||
|
||||
def backup(self, file):
|
||||
if os.path.isfile(file):
|
||||
bfile = file + '~'
|
||||
os.rename(file, bfile)
|
||||
|
||||
|
||||
hexify_format = '%02x' * 16
|
||||
def hexify(sum):
|
||||
"Return a hex representation of a 16-byte string (e.g. an MD5 digest)"
|
||||
return hexify_format % tuple(map(ord, sum))
|
||||
|
||||
def unhexify(hexsum):
|
||||
"Return the original from a hexified string"
|
||||
sum = ''
|
||||
for i in range(0, len(hexsum), 2):
|
||||
sum = sum + chr(string.atoi(hexsum[i:i+2], 16))
|
||||
return sum
|
||||
|
||||
|
||||
unctime_monthmap = {}
|
||||
def unctime(date):
|
||||
if not unctime_monthmap:
|
||||
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
||||
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
|
||||
i = 0
|
||||
for m in months:
|
||||
i = i+1
|
||||
unctime_monthmap[m] = i
|
||||
words = string.split(date) # Day Mon DD HH:MM:SS YEAR
|
||||
year = string.atoi(words[4])
|
||||
month = unctime_monthmap[words[1]]
|
||||
day = string.atoi(words[2])
|
||||
[hh, mm, ss] = map(string.atoi, string.splitfields(words[3], ':'))
|
||||
ss = ss - time.timezone
|
||||
return time.mktime((year, month, day, hh, mm, ss, 0, 0, 0))
|
||||
|
||||
def gmctime(t):
|
||||
return time.asctime(time.gmtime(t))
|
||||
|
||||
def test_unctime():
|
||||
now = int(time.time())
|
||||
t = time.gmtime(now)
|
||||
at = time.asctime(t)
|
||||
print 'GMT', now, at
|
||||
print 'timezone', time.timezone
|
||||
print 'local', time.ctime(now)
|
||||
u = unctime(at)
|
||||
print 'unctime()', u
|
||||
gu = time.gmtime(u)
|
||||
print '->', gu
|
||||
print time.asctime(gu)
|
||||
|
||||
def test():
|
||||
x = CVS()
|
||||
keys = x.entries.keys()
|
||||
keys.sort()
|
||||
for file in keys:
|
||||
e = x.entries[file]
|
||||
print file, e.rev, gmctime(e.ctime), gmctime(e.mtime), e.extra,
|
||||
print e.gethexsum()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
test()
|
|
@ -0,0 +1,192 @@
|
|||
from cvslib import CVS, Entry
|
||||
import RCSProxy
|
||||
import client
|
||||
import md5
|
||||
import os
|
||||
import string
|
||||
import sys
|
||||
import time
|
||||
import fnmatch
|
||||
|
||||
|
||||
ignored_patterns = ['*.pyc', '.*', '*~', '@*']
|
||||
def ignored(file):
|
||||
if os.path.isdir(file): return 1
|
||||
for pat in ignored_patterns:
|
||||
if fnmatch.fnmatch(file, pat): return 1
|
||||
return 0
|
||||
|
||||
|
||||
class PCVS(CVS):
|
||||
|
||||
def __init__(self, proxy):
|
||||
CVS.__init__(self)
|
||||
self.proxy = proxy
|
||||
self.readsums()
|
||||
self.calcsums()
|
||||
|
||||
def calcsums(self):
|
||||
for file in self.keys():
|
||||
e = self.entries[file]
|
||||
if not e.new and e.sum is None:
|
||||
sum = self.proxy.sum((file, e.rev))
|
||||
e.setsum(sum)
|
||||
|
||||
def fullcheck(self):
|
||||
ok = 1
|
||||
for file in self.keys():
|
||||
e = self.entries[file]
|
||||
if e.new:
|
||||
if self.proxy.isfile(file):
|
||||
print "%s: created by someone else!"
|
||||
ok = 0
|
||||
continue
|
||||
rrev = self.proxy.head(file)
|
||||
if rrev != e.rev:
|
||||
print "%s: out of date (%s vs. %s)" % \
|
||||
(file, e.rev, rrev)
|
||||
ok = 0
|
||||
return ok
|
||||
|
||||
def update(self):
|
||||
for file in self.keys():
|
||||
e = self.entries[file]
|
||||
if e.new:
|
||||
print 'A', file
|
||||
continue
|
||||
rrev = self.proxy.head(file)
|
||||
lsum = sumfile(file)
|
||||
if rrev == e.rev:
|
||||
if lsum == e.sum:
|
||||
print '=', file
|
||||
else:
|
||||
print 'M', file
|
||||
continue
|
||||
if e.sum != lsum:
|
||||
print "%s: conflict -- not updated" % file
|
||||
continue
|
||||
print "%s: getting ..." % file
|
||||
data = self.proxy.get(file)
|
||||
f = open(file, 'w')
|
||||
f.write(data)
|
||||
f.close()
|
||||
nsum = md5.new(data).digest()
|
||||
e.setsum(nsum)
|
||||
e.rev = rrev
|
||||
print 'U', file
|
||||
self.writeentries()
|
||||
self.writesums()
|
||||
|
||||
def commit(self):
|
||||
if not self.fullcheck():
|
||||
print "correct above errors first"
|
||||
return
|
||||
needed = []
|
||||
for file in self.keys():
|
||||
e = self.entries[file]
|
||||
if e.new:
|
||||
needed.append(file)
|
||||
continue
|
||||
lsum = sumfile(file)
|
||||
if lsum != e.sum:
|
||||
needed.append(file)
|
||||
continue
|
||||
if not needed:
|
||||
print "no changes need committing"
|
||||
return
|
||||
message = raw_input("One-liner: ")
|
||||
for file in needed:
|
||||
print "%s: putting ..." % file
|
||||
e = self.entries[file]
|
||||
data = open(file).read()
|
||||
self.proxy.put(file, data, message)
|
||||
e.rev = self.proxy.head(file)
|
||||
e.setsum(self.proxy.sum(file))
|
||||
# XXX get it?
|
||||
mtime, ctime = os.stat(file)[-2:]
|
||||
e.mtime = mtime
|
||||
e.ctime = ctime
|
||||
self.writeentries()
|
||||
self.writesums()
|
||||
|
||||
def report(self):
|
||||
keys = self.keys()
|
||||
files = os.listdir(os.curdir)
|
||||
allfiles = files
|
||||
for file in keys:
|
||||
if file not in allfiles:
|
||||
allfiles.append(file)
|
||||
allfiles.sort()
|
||||
for file in allfiles:
|
||||
if file not in keys:
|
||||
if not ignored(file):
|
||||
print '?', file
|
||||
continue
|
||||
if file not in files:
|
||||
print file, ': lost'
|
||||
continue
|
||||
e = self.entries[file]
|
||||
if not os.path.exists(file):
|
||||
print "%s: lost" % file
|
||||
continue
|
||||
if e.new:
|
||||
print 'A', file
|
||||
continue
|
||||
lsum = sumfile(file)
|
||||
rrev = self.proxy.head(file)
|
||||
if rrev == e.rev:
|
||||
if lsum == e.sum:
|
||||
print '=', file
|
||||
else:
|
||||
print 'M', file
|
||||
else:
|
||||
if lsum == e.sum:
|
||||
print 'U', file
|
||||
else:
|
||||
print 'C', file
|
||||
|
||||
def add(self, file):
|
||||
if self.entries.has_key(file):
|
||||
print "%s: already known"
|
||||
else:
|
||||
self.entries[file] = Entry('/%s/0/Initial %s//\n' %
|
||||
(file, file))
|
||||
|
||||
|
||||
def sumfile(file):
|
||||
return md5.new(open(file).read()).digest()
|
||||
|
||||
|
||||
def test():
|
||||
proxy = RCSProxy.RCSProxyClient(('voorn.cwi.nl', 4127))
|
||||
proxy.cd('/ufs/guido/voorn/python-RCS/Demo/pdist')
|
||||
x = PCVS(proxy)
|
||||
args = sys.argv[1:]
|
||||
if args:
|
||||
cmd = args[0]
|
||||
files = args[1:]
|
||||
if cmd == 'add':
|
||||
if not files:
|
||||
print "add needs at least one file argument"
|
||||
else:
|
||||
for file in files:
|
||||
x.add(file)
|
||||
x.writeentries()
|
||||
elif cmd in ('update', 'up'):
|
||||
if files:
|
||||
print "updates wants no file arguments"
|
||||
else:
|
||||
x.update()
|
||||
elif cmd in ('commit', 'com'):
|
||||
if files:
|
||||
print "commit wants no file arguments"
|
||||
else:
|
||||
x.commit()
|
||||
else:
|
||||
print "Unknown command", cmd
|
||||
else:
|
||||
x.report()
|
||||
if sys.argv[1:]: x.writesums()
|
||||
|
||||
if __name__ == "__main__":
|
||||
test()
|
|
@ -0,0 +1,174 @@
|
|||
#! /usr/local/bin/python
|
||||
|
||||
import sys
|
||||
import os
|
||||
import getopt
|
||||
import string
|
||||
import md5
|
||||
import tempfile
|
||||
|
||||
def main():
|
||||
sys.stdout = sys.stderr
|
||||
try:
|
||||
opts, rest = getopt.getopt(sys.argv[1:], 'h:p:qv')
|
||||
if not rest:
|
||||
raise getopt.error, "missing command"
|
||||
cmd, rest = rest[0], rest[1:]
|
||||
if not commands.has_key(cmd):
|
||||
raise getopt.error, "unknown command"
|
||||
coptset, func = commands[cmd]
|
||||
copts, files = getopt.getopt(rest, coptset)
|
||||
except getopt.error, msg:
|
||||
print msg
|
||||
print "usage: rrcs [options] command [options] [file] ..."
|
||||
print "where command can be:"
|
||||
print " ci|put # checkin the given files"
|
||||
print " co|get # checkout"
|
||||
print " info # print header info"
|
||||
print " head # print revision of head branch"
|
||||
print " list # list filename if valid"
|
||||
print " log # print full log"
|
||||
print " diff # diff rcs file and work file"
|
||||
print "if no files are given, all remote rcs files are assumed"
|
||||
sys.exit(2)
|
||||
x = openclient(opts)
|
||||
if not files:
|
||||
files = x.listfiles()
|
||||
for fn in files:
|
||||
try:
|
||||
func(x, copts, fn)
|
||||
except (IOError, os.error), msg:
|
||||
print "%s: %s" % (fn, msg)
|
||||
|
||||
def openclient(opts):
|
||||
import client
|
||||
import RCSProxy
|
||||
host = 'spam'
|
||||
port = 4127
|
||||
verbose = client.VERBOSE
|
||||
for o, a in opts:
|
||||
if o == '-h':
|
||||
host = a
|
||||
if ':' in host:
|
||||
i = string.find(host, ':')
|
||||
host, p = host[:i], host[i+1:]
|
||||
if p:
|
||||
port = string.atoi(p)
|
||||
if o == '-p':
|
||||
port = string.atoi(a)
|
||||
if o == '-v':
|
||||
verbose = verbose + 1
|
||||
if o == '-q':
|
||||
verbose = 0
|
||||
address = (host, port)
|
||||
x = RCSProxy.RCSProxyClient(address, verbose)
|
||||
return x
|
||||
|
||||
def checkin(x, copts, fn):
|
||||
f = open(fn)
|
||||
data = f.read()
|
||||
f.close()
|
||||
new = not x.isfile(fn)
|
||||
if not new and same(x, copts, fn, data):
|
||||
print "%s: unchanged since last checkin" % fn
|
||||
return
|
||||
message = asklogmessage(new)
|
||||
messages = x.put(fn, data, message)
|
||||
if messages:
|
||||
print messages
|
||||
|
||||
def checkout(x, copts, fn):
|
||||
data = x.get(fn)
|
||||
f = open(fn, 'w')
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
def info(x, copts, fn):
|
||||
dict = x.info(fn)
|
||||
keys = dict.keys()
|
||||
keys.sort()
|
||||
for key in keys:
|
||||
print key + ':', dict[key]
|
||||
print '='*70
|
||||
|
||||
def head(x, copts, fn):
|
||||
head = x.head(fn)
|
||||
print fn, head
|
||||
|
||||
def list(x, copts, fn):
|
||||
if x.isfile(fn):
|
||||
print fn
|
||||
|
||||
def log(x, copts, fn):
|
||||
flags = ''
|
||||
for o, a in copts:
|
||||
flags = flags + ' ' + o + a
|
||||
flags = flags[1:]
|
||||
messages = x.log(fn, flags)
|
||||
print messages
|
||||
|
||||
def diff(x, copts, fn):
|
||||
if same(x, copts, fn):
|
||||
return
|
||||
flags = ''
|
||||
for o, a in copts:
|
||||
flags = flags + ' ' + o + a
|
||||
flags = flags[1:]
|
||||
data = x.get(fn)
|
||||
tfn = tempfile.mktemp()
|
||||
try:
|
||||
tf = open(tfn, 'w')
|
||||
tf.write(data)
|
||||
tf.close()
|
||||
print 'diff %s -r%s %s' % (flags, x.head(fn), fn)
|
||||
sts = os.system('diff %s %s %s' % (flags, tfn, fn))
|
||||
if sts:
|
||||
print '='*70
|
||||
finally:
|
||||
remove(tfn)
|
||||
|
||||
def same(x, copts, fn, data = None):
|
||||
if data is None:
|
||||
f = open(fn)
|
||||
data = f.read()
|
||||
f.close()
|
||||
lsum = md5.new(data).digest()
|
||||
rsum = x.sum(fn)
|
||||
return lsum == rsum
|
||||
|
||||
def asklogmessage(new):
|
||||
if new:
|
||||
print "enter description,",
|
||||
else:
|
||||
print "enter log message,",
|
||||
print "terminate with single '.' or end of file:"
|
||||
if new:
|
||||
print "NOTE: This is NOT the log message!"
|
||||
message = ""
|
||||
while 1:
|
||||
sys.stderr.write(">> ")
|
||||
sys.stderr.flush()
|
||||
line = sys.stdin.readline()
|
||||
if not line or line == '.\n': break
|
||||
message = message + line
|
||||
return message
|
||||
|
||||
def remove(fn):
|
||||
try:
|
||||
os.unlink(fn)
|
||||
except os.error:
|
||||
pass
|
||||
|
||||
commands = {
|
||||
'ci': ('', checkin),
|
||||
'put': ('', checkin),
|
||||
'co': ('', checkout),
|
||||
'get': ('', checkout),
|
||||
'info': ('', info),
|
||||
'head': ('', head),
|
||||
'list': ('', list),
|
||||
'log': ('bhLRtd:l:r:s:w:V:', log),
|
||||
'diff': ('c', diff),
|
||||
}
|
||||
|
||||
main()
|
Loading…
Reference in New Issue