# standard modules import os import os.path import difflib # custom modules from reviewpages.lib import defs from reviewpages.lib.logger import Logger class VCSItemStatus(set): """ Specifies a custom status of a changed item. For Subversion it could be one of the "added", "conflicted" etc. It is a set of such statuses """ good = frozenset([ "added", "deleted", "modified", "properties modified" ]) old = frozenset([ "deleted", "modified", "properties modified" ]) new = frozenset([ "added", "modified", "properties modified" ]) def is_interesting(self): return self <= type(self).good def has_new(self): return self <= type(self).new def has_old(self): return self <= type(self).old def __str__(self): return ", ".join([ x for x in self ]) def cleanup(self): return self & type(self).good class VCSItemProperties(dict): def get_diff(self, target): if target != None: sk = frozenset(self.keys()) tk = frozenset(target.keys()) #diff m = [ x for x in sk & tk if self[x] != target[x]] return (sk - tk, tk - sk, m) else: return (None, None, None) def __str__(self): return "\n".join([x+": "+self[x] for x in self]) class VCSDiff(Logger): """ Knows about a changed item: old, new versions and status Creates patches and diff files """ id = 1 def __init__(self, item1, item2, status): Logger.__init__(self) self.old = item1 self.new = item2 self.status = status self.id = VCSDiff.id VCSDiff.id += 1 pathname = self.new.pathname if self.new else self.old.pathname self.pathname = pathname self.path, self.name = os.path.split(self.pathname) self.log.debug("New diff instance: "+self.pathname) self.directory = self.old.directory if self.old else self.new.directory self.new_file = None self.old_file = None # Pages are relative to index.html self.udiff_page = None self.sdiffs_page = None self.sdifff_page = None self.elevator = None self.short_path = None self.comment = [] self.excluded = False self.has_diff = True self.log.debug("New diff %i, status: %s" % (self.id, status)) def strip_base(self, base): if self.path.startswith(base): self.short_path = self.path[len(base):].lstrip("\\/") else: self.short_path = self.path.lstrip("\\/") self.log.debug("Short path for '%s': %s" % (self.name, self.short_path)) self.elevator = "../" * (1+(self.short_path.replace('\\','/')).count('/')) self.new_file = os.path.join( defs.target_dir, defs.new_dir, self.short_path, self.name) self.old_file = os.path.join( defs.target_dir, defs.old_dir, self.short_path, self.name) # Pages are relative to index.html self.udiff_page = os.path.join(defs.udiff_dir, self.short_path, self.name+".html") self.sdiffs_page = os.path.join(defs.sdiffs_dir, self.short_path, self.name+".html") self.sdifff_page = os.path.join(defs.sdifff_dir, self.short_path, self.name+".html") def copy_files(self): """ Copy originals to the 'new' and 'old' directories Create diffs Create patch (?) """ self.log.debug("Copying '"+self.short_path+"'") # copy new: if self.status.has_new() and not self.new.directory: self.new.copy(os.path.join(defs.target_dir, defs.new_dir, self.short_path)) # copy old if self.status.has_old() and not self.old.directory: self.old.copy(os.path.join(defs.target_dir, defs.old_dir, self.short_path)) class VCSItem(Logger): def __init__(self, system, path, revision=None): Logger.__init__(self) self.id = 0 self.system = system self.pathname = path # os.path.normpath(path) removes //, which is really bad self.path, self.name = os.path.split(self.pathname) self.directory = os.path.isdir(path) self.outdated = False self.binary = False self.directory = False self.url = path self.revision = revision if revision else system.revision self.author = system.author if system.revision else '' self.properties = None self.author = system.author self.log.debug("new item: "+str(self)) def get_properties(self): pass def copy(self, dst): """ Copy the item of specific revision to the dst directory """ pass def __str__(self): return "%s at rev. %s by %s, %s (%s)" % \ (self.pathname, self.revision, self.author, "binary" if self.binary else "dir" if self.directory else "text", type(self.system).name) class VCSystem(Logger): """ """ name = "" aliases = [] systems = {} def __init__(self, username, pwd, root, rev): Logger.__init__(self) self.username = username self.pwd = pwd self.revision = rev self.root = root #os.path.abspath(root) self.status = [] self.author = None #self.patch = os.path.join(defs.diff_dir, self.name+".patch") def get_item(self, path): return VCSItem(self, path) def get_name(self): return type(self).name def get_diff(self, target): """ target - another vcs. returns VCSDiff array """ return self.status def get_all(self): """ Returns the list of all files in the root with theirs dates of modifications. Will be used to be able to compare different sources. """ return [] def make_patch(self, pfilename): if not self.status: return pfile = open(pfilename, "a") for diff in self.status: if diff.excluded: continue if "properties modified" in diff.status: pfile.write("\nProperty changes on: %s\n%s\n" % (diff.pathname, "___________________________________________________________________")) if diff.old and diff.new: d, a, m = diff.old.properties.get_diff(diff.new.properties) elif diff.old: d = diff.old.properties a, m = {}, {} else: a = diff.new.properties d, m = {}, {} for prop in d: pfile.write("Deleted: %s\n - %s\n" % (prop, diff.old.properties[prop])) for prop in a: pfile.write("Added: %s\n + %s\n" % (prop, diff.new.properties[prop])) for prop in m: pfile.write("Modified: %s\n - %s\n + %s\n" % (prop, diff.old.properties[prop], diff.new.properties[prop])) else: if any([ x and (x.binary or x.directory) for x in [diff.old, diff.new] ]): continue oldLines = newLines = [] rev1 = rev2 = None pathname1 = pathname2 = diff.pathname if diff.status.has_old() and diff.old: oldfilename = os.path.join(defs.target_dir, defs.old_dir, diff.short_path, diff.old.name+'.txt') oldLines = file(oldfilename).readlines() rev1 = diff.old.revision pathname1 = diff.old.pathname if diff.status.has_new() and diff.new: newfilename = os.path.join(defs.target_dir, defs.new_dir, diff.short_path, diff.new.name+'.txt') newLines = file(newfilename).readlines() rev2 = diff.new.revision pathname2 = diff.new.pathname pfile.write("Index: %s\n%s\n" % (diff.pathname, "===================================================================")) pfile.writelines( difflib.unified_diff(oldLines, newLines, pathname1, pathname2, "(revision %s)" % rev1, "(revision %s)" % rev2 \ if rev2 else "(working copy)", defs.common_lines)) pfile.close() def __str__(self): out = [] if self.username: out.append(self.username) if self.pwd: out.append(':'+self.pwd) if out: out.append('@') if self.root: if self.root.startswith(defs.base_path): out.append(os.path.normpath(self.root.replace(defs.base_path, '').lstrip('\\/'))) else: out.append(self.root) if self.revision: out.append('[%s]' % self.revision) out.append(' (%s)' % type(self).name) return ''.join(out) class VCSDetector(Logger): system_class = VCSystem def __init__(self, priority): Logger.__init__(self) VCSystem.systems[self] = priority def check_path(self, root): """ Check if the filename is the controlled resource """ return False def get_instance(self, username, pwd, root, rev): return type(self).system_class(username, pwd, root, rev) VCSDetector(1000)