blob: a09fd46f13830d09d0f15c6524d5c59621b7ce59 [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
Guido van Rossumd9628782000-02-14 21:41:50 +000035 reverse = 0
36 opts, args = getopt.getopt(sys.argv[1:], "tr")
Guido van Rossum6f0cf7e1997-08-14 22:04:00 +000037 for o, a in opts:
Guido van Rossumed5b3d81998-03-24 05:30:29 +000038 if o == '-t':
39 truncate_last = 1
Guido van Rossumd9628782000-02-14 21:41:50 +000040 elif o == '-r':
41 reverse = 1
Guido van Rossum6f0cf7e1997-08-14 22:04:00 +000042 database = []
43 while 1:
Guido van Rossumed5b3d81998-03-24 05:30:29 +000044 chunk = read_chunk(sys.stdin)
45 if not chunk:
46 break
47 records = digest_chunk(chunk)
48 if truncate_last:
49 del records[-1]
50 database[len(database):] = records
Guido van Rossum6f0cf7e1997-08-14 22:04:00 +000051 database.sort()
Guido van Rossumd9628782000-02-14 21:41:50 +000052 if not reverse:
53 database.reverse()
Guido van Rossum6f0cf7e1997-08-14 22:04:00 +000054 format_output(database)
55
56def read_chunk(fp):
57 """Read a chunk -- data for one file, ending with sep1.
58
59 Split the chunk in parts separated by sep2.
60
61 """
62 chunk = []
63 lines = []
64 while 1:
Guido van Rossumed5b3d81998-03-24 05:30:29 +000065 line = fp.readline()
66 if not line:
67 break
68 if line == sep1:
69 if lines:
70 chunk.append(lines)
71 break
72 if line == sep2:
73 if lines:
74 chunk.append(lines)
75 lines = []
76 else:
77 lines.append(line)
Guido van Rossum6f0cf7e1997-08-14 22:04:00 +000078 return chunk
79
80def digest_chunk(chunk):
81 """Digest a chunk -- extrach working file name and revisions"""
82 lines = chunk[0]
83 key = 'Working file:'
84 keylen = len(key)
85 for line in lines:
Guido van Rossumed5b3d81998-03-24 05:30:29 +000086 if line[:keylen] == key:
87 working_file = string.strip(line[keylen:])
88 break
Guido van Rossum6f0cf7e1997-08-14 22:04:00 +000089 else:
Guido van Rossumed5b3d81998-03-24 05:30:29 +000090 working_file = None
Guido van Rossum6f0cf7e1997-08-14 22:04:00 +000091 records = []
92 for lines in chunk[1:]:
Guido van Rossumed5b3d81998-03-24 05:30:29 +000093 revline = lines[0]
94 dateline = lines[1]
95 text = lines[2:]
96 words = string.split(dateline)
97 author = None
98 if len(words) >= 3 and words[0] == 'date:':
99 dateword = words[1]
100 timeword = words[2]
101 if timeword[-1:] == ';':
102 timeword = timeword[:-1]
103 date = dateword + ' ' + timeword
104 if len(words) >= 5 and words[3] == 'author:':
105 author = words[4]
106 if author[-1:] == ';':
107 author = author[:-1]
108 else:
109 date = None
110 text.insert(0, revline)
111 words = string.split(revline)
112 if len(words) >= 2 and words[0] == 'revision':
113 rev = words[1]
114 else:
115 rev = None
116 text.insert(0, revline)
117 records.append((date, working_file, rev, author, text))
Guido van Rossum6f0cf7e1997-08-14 22:04:00 +0000118 return records
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000119
Guido van Rossum6f0cf7e1997-08-14 22:04:00 +0000120def format_output(database):
121 prevtext = None
122 prev = []
Guido van Rossum9971f681997-10-06 21:09:32 +0000123 database.append((None, None, None, None, None)) # Sentinel
124 for (date, working_file, rev, author, text) in database:
Guido van Rossumed5b3d81998-03-24 05:30:29 +0000125 if text != prevtext:
126 if prev:
127 print sep2,
128 for (p_date, p_working_file, p_rev, p_author) in prev:
129 print p_date, p_author, p_working_file
130 sys.stdout.writelines(prevtext)
131 prev = []
132 prev.append((date, working_file, rev, author))
133 prevtext = text
Guido van Rossum6f0cf7e1997-08-14 22:04:00 +0000134
135main()