aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/scripts/logmerge.py')
-rwxr-xr-xTools/scripts/logmerge.py124
1 files changed, 124 insertions, 0 deletions
diff --git a/Tools/scripts/logmerge.py b/Tools/scripts/logmerge.py
new file mode 100755
index 00000000000..5999c2eb534
--- /dev/null
+++ b/Tools/scripts/logmerge.py
@@ -0,0 +1,124 @@
+#! /usr/bin/env python
+
+"""Consolidate a bunch of CVS or RCS logs read from stdin.
+
+Input should be the output of a CVS or RCS logging command, e.g.
+
+ cvs log -rrelease14
+
+which dumps all log messages from release1.4 upwards (assuming that
+release 1.4 was tagged with tag 'release14').
+
+This collects all the revision records and outputs them sorted by date
+rather than by file, collapsing duplicate revision record, i.e.,
+records with the same message for different files.
+
+The -t option causes it to truncate (discard) the last revision log
+entry; this is useful when using something like the above cvs log
+command, which shows the revisions including the given tag, while you
+probably want everything *since* that tag.
+
+XXX This code was created by reverse engineering CVS 1.9 and RCS 5.7.
+
+"""
+
+import os, sys, getopt, string, re
+
+sep1 = '='*77 + '\n' # file separator
+sep2 = '-'*28 + '\n' # revision separator
+
+def main():
+ """Main program"""
+ truncate_last = 0
+ opts, args = getopt.getopt(sys.argv[1:], "-t")
+ for o, a in opts:
+ if o == '-t':
+ truncate_last = 1
+ database = []
+ while 1:
+ chunk = read_chunk(sys.stdin)
+ if not chunk:
+ break
+ records = digest_chunk(chunk)
+ if truncate_last:
+ del records[-1]
+ database[len(database):] = records
+ database.sort()
+ database.reverse()
+ format_output(database)
+
+def read_chunk(fp):
+ """Read a chunk -- data for one file, ending with sep1.
+
+ Split the chunk in parts separated by sep2.
+
+ """
+ chunk = []
+ lines = []
+ while 1:
+ line = fp.readline()
+ if not line:
+ break
+ if line == sep1:
+ if lines:
+ chunk.append(lines)
+ break
+ if line == sep2:
+ if lines:
+ chunk.append(lines)
+ lines = []
+ else:
+ lines.append(line)
+ return chunk
+
+def digest_chunk(chunk):
+ """Digest a chunk -- extrach working file name and revisions"""
+ lines = chunk[0]
+ key = 'Working file:'
+ keylen = len(key)
+ for line in lines:
+ if line[:keylen] == key:
+ working_file = string.strip(line[keylen:])
+ break
+ else:
+ working_file = None
+ records = []
+ for lines in chunk[1:]:
+ revline = lines[0]
+ dateline = lines[1]
+ text = lines[2:]
+ words = string.split(dateline)
+ if len(words) >= 3 and words[0] == 'date:':
+ dateword = words[1]
+ timeword = words[2]
+ if timeword[-1:] == ';':
+ timeword = timeword[:-1]
+ date = dateword + ' ' + timeword
+ else:
+ date = None
+ text.insert(0, revline)
+ words = string.split(revline)
+ if len(words) >= 2 and words[0] == 'revision':
+ rev = words[1]
+ else:
+ rev = None
+ text.insert(0, revline)
+ records.append((date, working_file, rev, text))
+ return records
+
+def format_output(database):
+ prevtext = None
+ prev = []
+ database.append((None, None, None, None)) # Sentinel
+ for (date, working_file, rev, text) in database:
+ if text != prevtext:
+ if prev:
+ print sep2,
+ for (date, working_file, rev) in prev:
+ print date, working_file
+ sys.stdout.writelines(prevtext)
+ prev = []
+ prev.append((date, working_file, rev))
+ prevtext = text
+
+main()