From: Fred L. D. <fd...@us...> - 2003-07-09 04:55:11
|
Update of /cvsroot/cvs-syncmail/syncmail In directory sc8-pr-cvs1:/tmp/cvs-serv26960 Modified Files: syncmail Log Message: Massive changes to the way we determine what diffs to generate: - read the CVS/Entries and CVS/Entries.Log files from the temporary workspace on the server; these provide all the information we need to determine what the filenames are and which revisions should be used to generate diffs - as a side effect, we can deal with any character in a filename except '/' and '\n'; closes SF issue #608641 - add a helper function that can determine the branch name (not yet used to any effect) Index: syncmail =================================================================== RCS file: /cvsroot/cvs-syncmail/syncmail/syncmail,v retrieving revision 1.30 retrieving revision 1.31 diff -u -d -r1.30 -r1.31 --- syncmail 8 Jul 2003 17:57:26 -0000 1.30 +++ syncmail 9 Jul 2003 04:54:35 -0000 1.31 @@ -129,9 +129,6 @@ DIFF_TAIL_LINES = 20 DIFF_TRUNCATE_IF_LARGER = 1000 -EMPTYSTRING = '' -SPACE = ' ' -DOT = '.' COMMASPACE = ', ' PROGRAM = sys.argv[0] @@ -140,8 +137,7 @@ "(This appears to be a binary file; contents omitted.)\n" ] -REVCRE = re.compile("^(NONE|[0-9.]+)$") -NOVERSION = "Couldn't generate diff; no version number found in filespec: %s" +NOVERSION = "Couldn't generate diff; no version number found for file: %s" BACKSLASH = "Couldn't generate diff: backslash in filespec's filename: %s" @@ -154,35 +150,20 @@ -def calculate_diff(filespec, contextlines): - spec = string.split(filespec, ',') - if len(spec) < 3: - # Too few parts; command line probable used a replacement - # other than "%{sVv}"; don't fail, but don't produce a diff - # since we can't be sure what diff to generate. - return '' - # This allows filenames that contain commas: - file = string.join(spec[:-2], ",") - oldrev = spec[-2] - newrev = spec[-1] +def calculate_diff(entry, contextlines): + file = entry.name + oldrev = entry.revision + newrev = entry.new_revision # Make sure we can find a CVS version number - if not REVCRE.match(oldrev): - return NOVERSION % filespec - if not REVCRE.match(newrev): - return NOVERSION % filespec - - if string.find(file, '\\') <> -1: - # I'm sorry, a file name that contains a backslash is just too much. - # XXX if someone wants to figure out how to escape the backslashes in - # a safe way to allow filenames containing backslashes, this is the - # place to do it. --Zooko 2002-03-17 - return BACKSLASH % filespec + if oldrev is None and newrev is None: + return NOVERSION % file if string.find(file, "'") <> -1: # Those crazy users put single-quotes in their file names! Now we # have to escape everything that is meaningful inside double-quotes. - filestr = string.replace(file, '`', '\`') + filestr = string.replace(file, '\\', '\\\\') + filestr = string.replace(filestr, '`', '\`') filestr = string.replace(filestr, '"', '\"') filestr = string.replace(filestr, '$', '\$') # and quote it with double-quotes. @@ -190,7 +171,7 @@ else: # quote it with single-quotes. filestr = "'" + file + "'" - if oldrev == 'NONE': + if oldrev is None: # File is being added. try: if os.path.exists(file): @@ -213,7 +194,7 @@ except IOError, e: lines = ['***** Error reading new file: ', str(e), '\n***** file: ', file, ' cwd: ', os.getcwd()] - elif newrev == 'NONE': + elif newrev is None: lines = ['--- %s DELETED ---\n' % file] else: # File has been changed. @@ -227,10 +208,8 @@ % (difftype, oldrev, newrev, filestr) fp = os.popen(diffcmd) lines = fp.readlines() - sts = fp.close() # ignore the error code, it always seems to be 1 :( -## if sts: -## return 'Error code %d occurred during diff\n' % (sts >> 8) + fp.close() if len(lines) > DIFF_TRUNCATE_IF_LARGER: removedlines = len(lines) - DIFF_HEAD_LINES - DIFF_TAIL_LINES del lines[DIFF_HEAD_LINES:-DIFF_TAIL_LINES] @@ -250,7 +229,7 @@ -def blast_mail(subject, people, filestodiff, contextlines, fromhost, replyto): +def blast_mail(subject, people, entries, contextlines, fromhost, replyto): # cannot wait for child process or that will cause parent to retain cvs # lock for too long. Urg! if not os.fork(): @@ -289,8 +268,8 @@ s.write(sys.stdin.read()) # append the diffs if available print - for file in filestodiff: - print calculate_diff(file, contextlines) + for entry in entries: + print calculate_diff(entry, contextlines) finally: sys.stdout = sys.__stdout__ resp = conn.sendmail(address, people, s.getvalue()) @@ -299,6 +278,83 @@ +class CVSEntry: + def __init__(self, name, revision, timestamp, conflict, options, tagdate): + self.name = name + self.revision = revision + self.timestamp = timestamp + self.conflict = conflict + self.options = options + self.tagdate = tagdate + +class CVSDirectory(CVSEntry): + def __init__(self, name): + CVSEntry.__init__(self, name, None, None, None, None, None) + +def load_change_info(): + entries_fn = os.path.join("CVS", "Entries") + entries_log_fn = entries_fn + ".Log" + d = {} + f = open(entries_fn) + while 1: + line = f.readline() + if not line: + break + if string.strip(line) == "D": + continue + if line[0] == "D": + name = string.split(line, "/")[1] + d[name] = CVSDirectory(name) + elif line[0] == "/": + # normal file + parts = string.split(line, "/") + _, name, revision, timestamp, options, tagdate = parts + if revision == "0": + revision = None + conflict = None + if string.find(timestamp, "+") != -1: + timestamp, conflict = tuple(string.split(timestamp, "+")) + d[name] = CVSEntry(name, revision, timestamp, conflict, + options, tagdate) + # else: bogus Entries line + f.close() + if os.path.isfile(entries_log_fn): + f = open(entries_log_fn) + while 1: + line = f.readline() + if not line: + break + if line[1:2] != ' ': + # really old version of CVS + break + parts = string.split(line, "/")[1:] + name = parts[0] + if line[0] == "A": + # adding a file + try: + entry = d[name] + except KeyError: + entry = CVSEntry(name, None, parts[2], parts[3], parts[4]) + d[name] = entry + d[name].new_revision = parts[1] + elif line[0] == "R": + # removing a file + d[name].new_revision = None + f.close() + for entry in d.values(): + assert hasattr(entry, "new_revision") + return d + +def load_branch_name(): + tag_fn = os.path.join("CVS", "Tag") + if os.path.isfile(tag_fn): + f = open(tag_fn) + line = string.strip(f.readline()) + f.close() + if line[:1] == "T": + return line[1:] + return None + # scan args for options def main(): # XXX Should really move all the options to an object, just to @@ -348,6 +404,8 @@ # $CVSROOT, followed by the list of files that are changing. if not args: usage(1, 'No CVS module specified') + changes = load_change_info() + branch = load_branch_name() subject = subject_prefix + args[0] specs = string.split(args[0]) del args[0] @@ -359,26 +417,15 @@ # Now do the mail command people = args - if verbose: - print 'Mailing %s...' % string.join(people, COMMASPACE) - if specs[-3:] == ['-', 'Imported', 'sources']: + print 'Not sending email for imported sources.' return - if specs[-3:] == ['-', 'New', 'directory']: - del specs[-3:] - elif len(specs) > 2: - L = specs[:2] - for s in specs[2:]: - prev = L[-1] - if string.count(prev, ',') < 2: - L[-1] = "%s %s" % (prev, s) - else: - L.append(s) - specs = L if verbose: + print 'Mailing %s...' % string.join(people, COMMASPACE) print 'Generating notification message...' - blast_mail(subject, people, specs[1:], contextlines, fromhost, replyto) + blast_mail(subject, people, changes.values(), + contextlines, fromhost, replyto) if verbose: print 'Generating notification message... done.' |