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