blob: c26df30690cb631b44453356f297276090a4ca07 [file] [log] [blame]
Guido van Rossum6f0cf7e1997-08-14 22:04:00 +00001#! /usr/bin/env python
2
3"""Consolidate a bunch of CVS or RCS logs read from stdin.
4
5Input should be the output of a CVS or RCS logging command, e.g.
6
Guido van Rossum9971f681997-10-06 21:09:32 +00007 cvs log -rrelease14:
Guido van Rossum6f0cf7e1997-08-14 22:04:00 +00008
9which dumps all log messages from release1.4 upwards (assuming that
Guido van Rossum9971f681997-10-06 21:09:32 +000010release 1.4 was tagged with tag 'release14'). Note the trailing
11colon!
Guido van Rossum6f0cf7e1997-08-14 22:04:00 +000012
13This collects all the revision records and outputs them sorted by date
14rather than by file, collapsing duplicate revision record, i.e.,
15records with the same message for different files.
16
17The -t option causes it to truncate (discard) the last revision log
18entry; this is useful when using something like the above cvs log
19command, which shows the revisions including the given tag, while you
20probably want everything *since* that tag.
21
Guido van Rossum9971f681997-10-06 21:09:32 +000022XXX This code was created by reverse engineering CVS 1.9 and RCS 5.7
23from their output.
Guido van Rossum6f0cf7e1997-08-14 22:04:00 +000024
25"""
26
27import os, sys, getopt, string, re
28
Guido van Rossumed5b3d81998-03-24 05:30:29 +000029sep1 = '='*77 + '\n' # file separator
30sep2 = '-'*28 + '\n' # revision separator
Guido van Rossum6f0cf7e1997-08-14 22:04:00 +000031
32def main():
33 """Main program"""
34 truncate_last = 0
35 opts, args = getopt.getopt(sys.argv[1:], "-t")
36 for o, a in opts:
Guido van Rossumed5b3d81998-03-24 05:30:29 +000037 if o == '-t':
38 truncate_last = 1
Guido van Rossum6f0cf7e1997-08-14 22:04:00 +000039 database = []
40 while 1:
Guido van Rossumed5b3d81998-03-24 05:30:29 +000041 chunk = read_chunk(sys.stdin)
42 if not chunk:
43 break
44 records = digest_chunk(chunk)
45 if truncate_last:
46 del records[-1]
47 database[len(database):] = records
Guido van Rossum6f0cf7e1997-08-14 22:04:00 +000048 database.sort()
49 database.reverse()
50 format_output(database)
51
52def read_chunk(fp):
53 """Read a chunk -- data for one file, ending with sep1.
54
55 Split the chunk in parts separated by sep2.
56
57 """
58 chunk = []
59 lines = []
60 while 1:
Guido van Rossumed5b3d81998-03-24 05:30:29 +000061 line = fp.readline()
62 if not line:
63 break
64 if line == sep1:
65 if lines:
66 chunk.append(lines)
67 break
68 if line == sep2:
69 if lines:
70 chunk.append(lines)
71 lines = []
72 else:
73 lines.append(line)
Guido van Rossum6f0cf7e1997-08-14 22:04:00 +000074 return chunk
75
76def digest_chunk(chunk):
77 """Digest a chunk -- extrach working file name and revisions"""
78 lines = chunk[0]
79 key = 'Working file:'
80 keylen = len(key)
81 for line in lines:
Guido van Rossumed5b3d81998-03-24 05:30:29 +000082 if line[:keylen] == key:
83 working_file = string.strip(line[keylen:])
84 break
Guido van Rossum6f0cf7e1997-08-14 22:04:00 +000085 else:
Guido van Rossumed5b3d81998-03-24 05:30:29 +000086 working_file = None
Guido van Rossum6f0cf7e1997-08-14 22:04:00 +000087 records = []
88 for lines in chunk[1:]:
Guido van Rossumed5b3d81998-03-24 05:30:29 +000089 revline = lines[0]
90 dateline = lines[1]
91 text = lines[2:]
92 words = string.split(dateline)
93 author = None
94 if len(words) >= 3 and words[0] == 'date:':
95 dateword = words[1]
96 timeword = words[2]
97 if timeword[-1:] == ';':
98 timeword = timeword[:-1]
99 date = dateword + ' ' + timeword
100 if len(words) >= 5 and words[3] == 'author:':
101 author = words[4]
102 if author[-1:] == ';':
103 author = author[:-1]
104 else:
105 date = None
106 text.insert(0, revline)
107 words = string.split(revline)
108 if len(words) >= 2 and words[0] == 'revision':
109 rev = words[1]
110 else:
111 rev = None
112 text.insert(0, revline)
113 records.append((date, working_file, rev, author, text))
Guido van Rossum6f0cf7e1997-08-14 22:04:00 +0000114 return records
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000115
Guido van Rossum6f0cf7e1997-08-14 22:04:00 +0000116def format_output(database):
117 prevtext = None
118 prev = []
Guido van Rossum9971f681997-10-06 21:09:32 +0000119 database.append((None, None, None, None, None)) # Sentinel
120 for (date, working_file, rev, author, text) in database:
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000121 if text != prevtext:
122 if prev:
123 print sep2,
124 for (p_date, p_working_file, p_rev, p_author) in prev:
125 print p_date, p_author, p_working_file
126 sys.stdout.writelines(prevtext)
127 prev = []
128 prev.append((date, working_file, rev, author))
129 prevtext = text
Guido van Rossum6f0cf7e1997-08-14 22:04:00 +0000130
131main()