home *** CD-ROM | disk | FTP | other *** search
/ H4CK3R 14 / hacker14.iso / programacao / pythonwin / python.exe / LOGMERGE.PY < prev    next >
Encoding:
Python Source  |  2003-04-20  |  5.5 KB  |  181 lines

  1. #! /usr/bin/env python
  2.  
  3. """Consolidate a bunch of CVS or RCS logs read from stdin.
  4.  
  5. Input should be the output of a CVS or RCS logging command, e.g.
  6.  
  7.     cvs log -rrelease14:
  8.  
  9. which dumps all log messages from release1.4 upwards (assuming that
  10. release 1.4 was tagged with tag 'release14').  Note the trailing
  11. colon!
  12.  
  13. This collects all the revision records and outputs them sorted by date
  14. rather than by file, collapsing duplicate revision record, i.e.,
  15. records with the same message for different files.
  16.  
  17. The -t option causes it to truncate (discard) the last revision log
  18. entry; this is useful when using something like the above cvs log
  19. command, which shows the revisions including the given tag, while you
  20. probably want everything *since* that tag.
  21.  
  22. The -r option reverses the output (oldest first; the default is oldest
  23. last).
  24.  
  25. The -b tag option restricts the output to *only* checkin messages
  26. belonging to the given branch tag.  The form -b HEAD restricts the
  27. output to checkin messages belonging to the CVS head (trunk).  (It
  28. produces some output if tag is a non-branch tag, but this output is
  29. not very useful.)
  30.  
  31. -h prints this message and exits.
  32.  
  33. XXX This code was created by reverse engineering CVS 1.9 and RCS 5.7
  34. from their output.
  35. """
  36.  
  37. import os, sys, getopt, re
  38.  
  39. sep1 = '='*77 + '\n'                    # file separator
  40. sep2 = '-'*28 + '\n'                    # revision separator
  41.  
  42. def main():
  43.     """Main program"""
  44.     truncate_last = 0
  45.     reverse = 0
  46.     branch = None
  47.     opts, args = getopt.getopt(sys.argv[1:], "trb:h")
  48.     for o, a in opts:
  49.         if o == '-t':
  50.             truncate_last = 1
  51.         elif o == '-r':
  52.             reverse = 1
  53.         elif o == '-b':
  54.             branch = a
  55.         elif o == '-h':
  56.             print __doc__
  57.             sys.exit(0)
  58.     database = []
  59.     while 1:
  60.         chunk = read_chunk(sys.stdin)
  61.         if not chunk:
  62.             break
  63.         records = digest_chunk(chunk, branch)
  64.         if truncate_last:
  65.             del records[-1]
  66.         database[len(database):] = records
  67.     database.sort()
  68.     if not reverse:
  69.         database.reverse()
  70.     format_output(database)
  71.  
  72. def read_chunk(fp):
  73.     """Read a chunk -- data for one file, ending with sep1.
  74.  
  75.     Split the chunk in parts separated by sep2.
  76.  
  77.     """
  78.     chunk = []
  79.     lines = []
  80.     while 1:
  81.         line = fp.readline()
  82.         if not line:
  83.             break
  84.         if line == sep1:
  85.             if lines:
  86.                 chunk.append(lines)
  87.             break
  88.         if line == sep2:
  89.             if lines:
  90.                 chunk.append(lines)
  91.                 lines = []
  92.         else:
  93.             lines.append(line)
  94.     return chunk
  95.  
  96. def digest_chunk(chunk, branch=None):
  97.     """Digest a chunk -- extract working file name and revisions"""
  98.     lines = chunk[0]
  99.     key = 'Working file:'
  100.     keylen = len(key)
  101.     for line in lines:
  102.         if line[:keylen] == key:
  103.             working_file = line[keylen:].strip()
  104.             break
  105.     else:
  106.         working_file = None
  107.     if branch is None:
  108.         pass
  109.     elif branch == "HEAD":
  110.         branch = re.compile(r"^\d+\.\d+$")
  111.     else:
  112.         revisions = {}
  113.         key = 'symbolic names:\n'
  114.         found = 0
  115.         for line in lines:
  116.             if line == key:
  117.                 found = 1
  118.             elif found:
  119.                 if line[0] in '\t ':
  120.                     tag, rev = line.split()
  121.                     if tag[-1] == ':':
  122.                         tag = tag[:-1]
  123.                     revisions[tag] = rev
  124.                 else:
  125.                     found = 0
  126.         rev = revisions.get(branch)
  127.         branch = re.compile(r"^<>$") # <> to force a mismatch by default
  128.         if rev:
  129.             if rev.find('.0.') >= 0:
  130.                 rev = rev.replace('.0.', '.')
  131.                 branch = re.compile(r"^" + re.escape(rev) + r"\.\d+$")
  132.     records = []
  133.     for lines in chunk[1:]:
  134.         revline = lines[0]
  135.         dateline = lines[1]
  136.         text = lines[2:]
  137.         words = dateline.split()
  138.         author = None
  139.         if len(words) >= 3 and words[0] == 'date:':
  140.             dateword = words[1]
  141.             timeword = words[2]
  142.             if timeword[-1:] == ';':
  143.                 timeword = timeword[:-1]
  144.             date = dateword + ' ' + timeword
  145.             if len(words) >= 5 and words[3] == 'author:':
  146.                 author = words[4]
  147.                 if author[-1:] == ';':
  148.                     author = author[:-1]
  149.         else:
  150.             date = None
  151.             text.insert(0, revline)
  152.         words = revline.split()
  153.         if len(words) >= 2 and words[0] == 'revision':
  154.             rev = words[1]
  155.         else:
  156.             # No 'revision' line -- weird...
  157.             rev = None
  158.             text.insert(0, revline)
  159.         if branch:
  160.             if rev is None or not branch.match(rev):
  161.                 continue
  162.         records.append((date, working_file, rev, author, text))
  163.     return records
  164.  
  165. def format_output(database):
  166.     prevtext = None
  167.     prev = []
  168.     database.append((None, None, None, None, None)) # Sentinel
  169.     for (date, working_file, rev, author, text) in database:
  170.         if text != prevtext:
  171.             if prev:
  172.                 print sep2,
  173.                 for (p_date, p_working_file, p_rev, p_author) in prev:
  174.                     print p_date, p_author, p_working_file, p_rev
  175.                 sys.stdout.writelines(prevtext)
  176.             prev = []
  177.         prev.append((date, working_file, rev, author))
  178.         prevtext = text
  179.  
  180. main()
  181.