source: python/trunk/Tools/scripts/logmerge.py@ 403

Last change on this file since 403 was 2, checked in by Yuri Dario, 15 years ago

Initial import for vendor code.

  • Property svn:eol-style set to native
File size: 5.4 KB
Line 
1#! /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
7 cvs log -rrelease14:
8
9which dumps all log messages from release1.4 upwards (assuming that
10release 1.4 was tagged with tag 'release14'). Note the trailing
11colon!
12
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
22The -r option reverses the output (oldest first; the default is oldest
23last).
24
25The -b tag option restricts the output to *only* checkin messages
26belonging to the given branch tag. The form -b HEAD restricts the
27output to checkin messages belonging to the CVS head (trunk). (It
28produces some output if tag is a non-branch tag, but this output is
29not very useful.)
30
31-h prints this message and exits.
32
33XXX This code was created by reverse engineering CVS 1.9 and RCS 5.7
34from their output.
35"""
36
37import sys, errno, getopt, re
38
39sep1 = '='*77 + '\n' # file separator
40sep2 = '-'*28 + '\n' # revision separator
41
42def 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
72def 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
96def 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
165def 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
180if __name__ == '__main__':
181 try:
182 main()
183 except IOError, e:
184 if e.errno != errno.EPIPE:
185 raise
Note: See TracBrowser for help on using the repository browser.