曾与蒿藜同雨露,한때 잡초와 쑥과 함께 비와 이슬을 나누던 곳이 이제는 소나무와 삼나무와 함께 서리와 눈을 견뎌내고 있다.终随松柏到冰霜.かつては雑草やヨモギと共に雨や露を分かち合っていたが、今では松やヒノキと共に霜や雪に耐えている。曾与蒿藜同雨露,Once sharing rain and dew with weeds and wormwood, now enduring frost and snow with pines and cypresses.终随松柏到冰霜.曾与蒿藜同雨露한때 잡초와 쑥과 함께 비와 이슬을 나누던 곳이 이제는 소나무와 삼나무와 함께 서리와 눈을 견뎌내고 있다.,终随松柏到冰霜.譖セ荳手珍阯懷酔髮ィ髴イ�檎サ磯囂譚セ譟丞芦蜀ー髴�曾与蒿藜同雨露,鏇句笌钂胯棞鍚岄洦闇诧紝缁堥殢鏉炬煆鍒板啺闇�终随松柏到冰霜.曾与蒿藜同雨露,한때 잡초와 쑥과 함께 비와 이슬을 나누던 곳이 이제는 소나무와 삼나무와 함께 서리와 눈을 견뎌내고 있다.终随松柏到冰霜.曾与蒿藜同雨露,终随松柏到冰霜.
#! /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'). Note the trailing
colon!
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.
The -r option reverses the output (oldest first; the default is oldest
last).
The -b tag option restricts the output to *only* checkin messages
belonging to the given branch tag. The form -b HEAD restricts the
output to checkin messages belonging to the CVS head (trunk). (It
produces some output if tag is a non-branch tag, but this output is
not very useful.)
-h prints this message and exits.
XXX This code was created by reverse engineering CVS 1.9 and RCS 5.7
from their output.
"""
import sys, errno, getopt, re
sep1 = '='*77 + '\n' # file separator
sep2 = '-'*28 + '\n' # revision separator
def main():
"""Main program"""
truncate_last = 0
reverse = 0
branch = None
opts, args = getopt.getopt(sys.argv[1:], "trb:h")
for o, a in opts:
if o == '-t':
truncate_last = 1
elif o == '-r':
reverse = 1
elif o == '-b':
branch = a
elif o == '-h':
print __doc__
sys.exit(0)
database = []
while 1:
chunk = read_chunk(sys.stdin)
if not chunk:
break
records = digest_chunk(chunk, branch)
if truncate_last:
del records[-1]
database[len(database):] = records
database.sort()
if not reverse:
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, branch=None):
"""Digest a chunk -- extract working file name and revisions"""
lines = chunk[0]
key = 'Working file:'
keylen = len(key)
for line in lines:
if line[:keylen] == key:
working_file = line[keylen:].strip()
break
else:
working_file = None
if branch is None:
pass
elif branch == "HEAD":
branch = re.compile(r"^\d+\.\d+$")
else:
revisions = {}
key = 'symbolic names:\n'
found = 0
for line in lines:
if line == key:
found = 1
elif found:
if line[0] in '\t ':
tag, rev = line.split()
if tag[-1] == ':':
tag = tag[:-1]
revisions[tag] = rev
else:
found = 0
rev = revisions.get(branch)
branch = re.compile(r"^<>$") # <> to force a mismatch by default
if rev:
if rev.find('.0.') >= 0:
rev = rev.replace('.0.', '.')
branch = re.compile(r"^" + re.escape(rev) + r"\.\d+$")
records = []
for lines in chunk[1:]:
revline = lines[0]
dateline = lines[1]
text = lines[2:]
words = dateline.split()
author = None
if len(words) >= 3 and words[0] == 'date:':
dateword = words[1]
timeword = words[2]
if timeword[-1:] == ';':
timeword = timeword[:-1]
date = dateword + ' ' + timeword
if len(words) >= 5 and words[3] == 'author:':
author = words[4]
if author[-1:] == ';':
author = author[:-1]
else:
date = None
text.insert(0, revline)
words = revline.split()
if len(words) >= 2 and words[0] == 'revision':
rev = words[1]
else:
# No 'revision' line -- weird...
rev = None
text.insert(0, revline)
if branch:
if rev is None or not branch.match(rev):
continue
records.append((date, working_file, rev, author, text))
return records
def format_output(database):
prevtext = None
prev = []
database.append((None, None, None, None, None)) # Sentinel
for (date, working_file, rev, author, text) in database:
if text != prevtext:
if prev:
print sep2,
for (p_date, p_working_file, p_rev, p_author) in prev:
print p_date, p_author, p_working_file, p_rev
sys.stdout.writelines(prevtext)
prev = []
prev.append((date, working_file, rev, author))
prevtext = text
if __name__ == '__main__':
try:
main()
except IOError, e:
if e.errno != errno.EPIPE:
raise