Fix three PyChecker-detected gotchas.
[python/dscho.git] / Tools / scripts / logmerge.py
blobd036749bebcb7914c383fd5a304eab0e421fa0a9
1 #! /usr/bin/env python
3 """Consolidate a bunch of CVS or RCS logs read from stdin.
5 Input should be the output of a CVS or RCS logging command, e.g.
7 cvs log -rrelease14:
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!
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.
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.
22 XXX This code was created by reverse engineering CVS 1.9 and RCS 5.7
23 from their output.
25 """
27 import os, sys, getopt, string, re
29 sep1 = '='*77 + '\n' # file separator
30 sep2 = '-'*28 + '\n' # revision separator
32 def main():
33 """Main program"""
34 truncate_last = 0
35 reverse = 0
36 opts, args = getopt.getopt(sys.argv[1:], "tr")
37 for o, a in opts:
38 if o == '-t':
39 truncate_last = 1
40 elif o == '-r':
41 reverse = 1
42 database = []
43 while 1:
44 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
51 database.sort()
52 if not reverse:
53 database.reverse()
54 format_output(database)
56 def read_chunk(fp):
57 """Read a chunk -- data for one file, ending with sep1.
59 Split the chunk in parts separated by sep2.
61 """
62 chunk = []
63 lines = []
64 while 1:
65 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)
78 return chunk
80 def 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:
86 if line[:keylen] == key:
87 working_file = string.strip(line[keylen:])
88 break
89 else:
90 working_file = None
91 records = []
92 for lines in chunk[1:]:
93 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))
118 return records
120 def format_output(database):
121 prevtext = None
122 prev = []
123 database.append((None, None, None, None, None)) # Sentinel
124 for (date, working_file, rev, author, text) in database:
125 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, p_rev
130 sys.stdout.writelines(prevtext)
131 prev = []
132 prev.append((date, working_file, rev, author))
133 prevtext = text
135 main()