+++ /dev/null
-#!/usr/bin/python
-
-import sys
-import time
-import os
-import re
-import optparse
-
-def read_pipe (x):
- print 'pipe', x
- return os.popen (x).read ()
-
-def system (x):
- print x
- return os.system (x)
-
-class PatchFailed(Exception):
- pass
-
-def sign (x):
- if x < 0:
- return -1
- if x > 0:
- return 1
-
- return 0
-
-
-class Commit:
- def __init__ (self, dict):
- for v in ('message',
- 'date',
- 'author',
- 'committish'):
- self.__dict__[v] = dict[v]
-
- self.date = ' '.join (self.date.split (' ')[:-1])
- self.date = time.strptime (self.date, '%a %b %d %H:%M:%S %Y')
-
- m = re.search ('(.*)<(.*)>', self.author)
- self.email = m.group (2).strip ()
- self.name = m.group (1).strip ()
- self.diff = read_pipe ('git show %s' % self.committish)
- def compare (self, other):
- return sign (time.mktime (self.date) - time.mktime (other.date))
-
-
- def check_diff_chunk (self, filename, chunk):
- removals = []
- def note_removal (m):
- removals.append (m.group (1))
-
- re.sub ('\n-([^\n]+)', note_removal, chunk)
-
- if removals == []:
- return True
- if not os.path.exists (filename):
- return False
-
- contents = open (filename).read ()
- for r in removals:
- if r not in contents:
- return False
-
- return True
-
- def check_diff (self):
- chunks = re.split ('\ndiff --git ', self.diff)
-
- ok = True
- for c in chunks:
- m = re.search ('^a/([^ ]+)', c)
- if not m:
- continue
-
- file = m.group (1)
-
- c = re.sub('\n--- [^\n]+', '', c)
- ok = ok and self.check_diff_chunk (file, c)
- if not ok:
- break
-
- return ok
-
- def touched_files (self):
- files = []
- def note_file (x):
- files.append (x.group (1))
- return ''
-
- re.sub ('\n--- a/([^\n]+)\n',
- note_file, self.diff)
- re.sub('\n--- /dev/null\n\\+\\+\\+ b/([^\n]+)',
- note_file, self.diff)
-
- return files
-
- def has_patch (self):
- return self.touched_files () <> []
-
- def apply (self, add_del_files):
- def note_add_file (x):
- add_del_files.append (('add', x.group (1)))
- return ''
-
- def note_del_file (x):
- add_del_files.append (('del', x.group (1)))
- return ''
-
- re.sub('\n--- /dev/null\n\\+\\+\\+ b/([^\n]+)',
- note_add_file, self.diff)
-
- re.sub('\n--- a/([^\n]+)\n\\+\\+\\+ /dev/null',
- note_del_file, self.diff)
-
- p = os.popen ('patch -f -p1 ', 'w')
- p.write (self.diff)
-
- if p.close ():
- raise PatchFailed, self.committish
-
-
-def parse_commit_log (log):
- committish = re.search ('^([^\n]+)', log).group (1)
- author = re.search ('\nAuthor:\s+([^\n]+)', log).group (1)
- date_match = re.search ('\nDate:\s+([^\n]+)', log)
- date = date_match.group (1)
- log = log[date_match.end (1):]
-
- message = re.sub ("\n *", '', log)
- message = message.strip ()
-
- c = Commit (locals ())
- return c
-
-def parse_add_changes (from_commit, max_count=0):
- opt = ''
- rest = '..'
- if max_count:
-
- # fixme.
- assert max_count == 1
- opt = '--max-count=%d' % max_count
- rest = ''
-
- log = read_pipe ('git log %(opt)s %(from_commit)s%(rest)s' % locals ())
-
- log = log[len ('commit '):]
- log = log.strip ()
-
- if not log:
- return []
-
- commits = map (parse_commit_log, re.split ('\ncommit ', log))
- commits.reverse ()
-
- return commits
-
-
-def header (commit):
- return '%d-%02d-%02d %s <%s>\n' % (commit.date[:3] + (commit.name, commit.email))
-
-def changelog_body (commit):
- s = ''
- s += ''.join ('\n* %s: ' % f for f in commit.touched_files())
- s += '\n' + commit.message
-
- s = s.replace ('\n', '\n\t')
- s += '\n'
- return s
-
-def main ():
- p = optparse.OptionParser (usage="usage git-update-changelog.py [options] [commits]",
- description="""
-Apply GIT patches and update change log.
-
-Run this file from the CVS directory, with commits from the repository in --git-dir.
-
-""")
- p.add_option ("--start",
- action='store',
- default='',
- metavar="FIRST",
- dest="start",
- help="all commits starting with FIRST (exclusive).")
-
- p.add_option ("--git-dir",
- action='store',
- default='',
- dest="gitdir",
- help="the GIT directory to merge.")
-
- (options, args) = p.parse_args ()
-
- log = open ('ChangeLog').read ()
-
- if options.gitdir:
- os.environ['GIT_DIR'] = options.gitdir
-
-
- if not args:
- if not options.start:
- print 'Must set start committish.'
- sys.exit (1)
-
- commits = parse_add_changes (options.start)
- else:
- commits = []
- for a in args:
- commits += parse_add_changes (a, max_count=1)
-
- if not commits:
- return
-
- new_log = ''
- last_commit = None
-
- first = header (commits[0]) + '\n'
- if first == log[:len (first)]:
- log = log[len (first):]
-
- try:
- previously_done = dict((c, 1) for c in open ('.git-commits-done').read ().split ('\n'))
- except IOError:
- previously_done = {}
-
- commits = [c for c in commits if not previously_done.has_key (c.committish)]
- commits = sorted (commits, cmp=Commit.compare)
-
- system ('cvs up')
-
- file_adddel = []
- collated_log = ''
- collated_message = ''
- commits_done = []
- while commits:
- c = commits[0]
-
- if not c.has_patch ():
- print 'patchless commit (merge?)'
- continue
-
- ok = c.check_diff ()
-
- if not ok:
- print "Patch doesn't seem to apply"
- print 'skipping', c.committish
- print 'message:', c.message
-
- break
-
-
- commits = commits[1:]
- commits_done.append (c)
-
- print 'patch ', c.committish
- try:
- c.apply (file_adddel)
- except PatchFailed:
- break
-
- if c.touched_files () == ['ChangeLog']:
- continue
-
- if (last_commit
- and c.author != last_commit.author
- and c.date[:3] != last_commit.date[:3]):
-
- new_log += header (last_commit)
-
- collated_log = changelog_body (c) + collated_log
- last_commit = c
-
- collated_message += c.message + '\n'
-
-
-
- for (op, f) in file_adddel:
- if op == 'del':
- system ('cvs remove %(f)s' % locals ())
- if op == 'add':
- system ('cvs add %(f)s' % locals ())
-
- if last_commit:
- collated_log = header (last_commit) + collated_log + '\n'
-
- log = collated_log + log
-
- try:
- os.unlink ('ChangeLog~')
- except OSError:
- pass
-
- os.rename ('ChangeLog', 'ChangeLog~')
- open ('ChangeLog', 'w').write (log)
-
- open ('.msg','w').write (collated_message)
- print '\nCommit message\n**\n%s\n**\n' % collated_message
- print '\nRun:\n\n\tcvs commit -F .msg\n\n'
- print '\n\techo %s >> .git-commits-done\n\n' % ' '.join ([c.committish
- for c in commits_done])
-
-
- if commits:
- print 'Commits left to do:'
- print ' '.join ([c.committish for c in commits])
-
-main ()
-
-
-