#! /usr/local/bin/python """Remote CVS -- command line interface""" from cvslib import CVS, File import md5 import os import string import sys from cmdfw import CommandFrameWork class MyFile(File): def action(self): """Return a code indicating the update status of this file. The possible return values are: '=' -- everything's fine '0' -- file doesn't exist anywhere '?' -- exists locally only 'A' -- new locally 'R' -- deleted locally 'U' -- changed remotely, no changes locally (includes new remotely or deleted remotely) 'M' -- changed locally, no changes remotely 'C' -- conflict: changed locally as well as remotely (includes cases where the file has been added or removed locally and remotely) 'D' -- deleted remotely 'N' -- new remotely 'r' -- get rid of entry 'c' -- create entry 'u' -- update entry """ if not self.lseen: self.getlocal() if not self.rseen: self.getremote() if not self.eseen: if not self.lseen: if not self.rseen: return '0' # Never heard of else: return 'N' # New remotely else: # self.lseen if not self.rseen: return '?' # Local only # Local and remote, but no entry if self.lsum == self.rsum: return 'c' # Restore entry only else: return 'C' # Real conflict else: # self.eseen if not self.lseen: if self.eremoved: if self.rseen: return 'R' # Removed else: return 'r' # Get rid of entry else: # not self.eremoved if self.rseen: print "warning:", print self.file, print "was lost" return 'U' else: return 'r' # Get rid of entry else: # self.lseen if not self.rseen: if self.enew: return 'A' # New locally else: return 'D' # Deleted remotely else: # self.rseen if self.enew: if self.lsum == self.rsum: return 'u' else: return 'C' if self.lsum == self.esum: if self.esum == self.rsum: return '=' else: return 'U' elif self.esum == self.rsum: return 'M' elif self.lsum == self.rsum: return 'u' else: return 'C' def update(self): code = self.action() if code == '=': return print code, self.file if code in ('U', 'N'): self.get() elif code == 'C': print "%s: conflict resolution not yet implemented" % \ self.file elif code == 'D': remove(self.file) self.eseen = 0 elif code == 'r': self.eseen = 0 elif code in ('c', 'u'): self.eseen = 1 self.erev = self.rrev self.enew = 0 self.edeleted = 0 self.esum = self.rsum self.emtime, self.ectime = os.stat(self.file)[-2:] self.extra = '' def commit(self, message = ""): code = self.action() if code in ('A', 'M'): self.put(message) elif code == 'R': print "%s: committing removes not yet implemented" % \ self.file elif code == 'C': print "%s: conflict resolution not yet implemented" % \ self.file def diff(self, opts = []): self.action() # To update lseen, rseen if self.lsum == self.rsum: return import tempfile flags = '' for o, a in opts: flags = flags + ' ' + o + a flags = flags[1:] fn = self.file data = self.proxy.get(fn) tfn = tempfile.mktemp() try: tf = open(tfn, 'w') tf.write(data) tf.close() print 'diff %s -r%s %s' % (flags, self.rrev, fn) sts = os.system('diff %s %s %s' % (flags, tfn, fn)) if sts: print '='*70 finally: remove(tfn) def commitcheck(self): return self.action() != 'C' def put(self, message = ""): print "Checking in", self.file, "..." data = open(self.file).read() messages = self.proxy.put(self.file, data, message) if messages: print messages self.setentry(self.proxy.head(self.file), self.lsum) def get(self): data = self.proxy.get(self.file) f = open(self.file, 'w') f.write(data) f.close() self.setentry(self.rrev, self.rsum) def setentry(self, erev, esum): self.eseen = 0 # While we're hacking... self.esum = esum self.emtime, self.ectime = os.stat(self.file)[-2:] self.erev = erev self.enew = 0 self.edeleted = 0 self.eseen = 1 # Done class RCVS(CVS): FileClass = MyFile def __init__(self): CVS.__init__(self) def update(self, files): for e in self.whichentries(files, 1): e.update() def commit(self, files, message = ""): list = self.whichentries(files) ok = 1 for e in list: if not e.commitcheck(): ok = 0 if not ok: print "correct above errors first" return if not message: message = raw_input("One-liner: ") for e in list: e.commit(message) def report(self, files): for e in self.whichentries(files): e.report() def diff(self, files, opts): for e in self.whichentries(files): e.diff(opts) def whichentries(self, files, localfilestoo = 0): if files: list = [] for file in files: if self.entries.has_key(file): e = self.entries[file] else: e = self.FileClass(file) self.entries[file] = e list.append(e) else: list = self.entries.values() for file in self.proxy.listfiles(): if self.entries.has_key(file): continue e = self.FileClass(file) self.entries[file] = e list.append(e) if localfilestoo: for file in os.listdir(os.curdir): if not self.entries.has_key(file) \ and not self.ignored(file): e = self.FileClass(file) self.entries[file] = e list.append(e) list.sort() if self.proxy: for e in list: if e.proxy is None: e.proxy = self.proxy return list class rcvs(CommandFrameWork): GlobalFlags = 'd:h:p:qv' UsageMessage = \ "usage: rcvs [-d directory] [-h host] [-p port] [-q] [-v] [subcommand arg ...]" PostUsageMessage = \ "If no subcommand is given, the status of all files is listed" def __init__(self): """Constructor.""" CommandFrameWork.__init__(self) self.proxy = None self.cvs = RCVS() def options(self, opts): self.opts = opts def ready(self): import rcsclient self.proxy = rcsclient.openrcsclient(self.opts) self.cvs.setproxy(self.proxy) self.cvs.getentries() def default(self): self.cvs.report([]) def do_update(self, opts, files): """update [file] ...""" self.cvs.update(files) self.cvs.putentries() do_up = do_update def do_commit(self, opts, files): """commit [-m message] [file] ...""" message = "" for o, a in opts: if o == '-m': message = a self.cvs.commit(files, message) self.cvs.putentries() do_com = do_commit flags_commit = 'm:' def do_diff(self, opts, files): """diff [difflags] [file] ...""" self.cvs.diff(files, opts) do_dif = do_diff flags_diff = 'cbitwcefhnlrsD:S:' def remove(fn): try: os.unlink(fn) except os.error: pass def main(): rcvs().run() if __name__ == "__main__": main()