11 return os.popen (x).read ()
17 class PatchFailed(Exception):
30 def __init__ (self, dict):
35 self.__dict__[v] = dict[v]
37 self.date = ' '.join (self.date.split (' ')[:-1])
38 self.date = time.strptime (self.date, '%a %b %d %H:%M:%S %Y')
40 m = re.search ('(.*)<(.*)>', self.author)
41 self.email = m.group (2).strip ()
42 self.name = m.group (1).strip ()
43 self.diff = read_pipe ('git show %s' % self.committish)
44 def compare (self, other):
45 return sign (time.mktime (self.date) - time.mktime (other.date))
48 def check_diff_chunk (self, filename, chunk):
51 removals.append (m.group (1))
53 re.sub ('\n-([^\n]+)', note_removal, chunk)
58 if not os.path.exists (filename):
61 contents = open (filename).read ()
68 def check_diff (self):
69 chunks = re.split ('\ndiff --git ', self.diff)
73 m = re.search ('^a/([^ ]+)', c)
79 c = re.sub('\n--- [^\n]+', '', c)
80 ok = ok and self.check_diff_chunk (file, c)
86 def touched_files (self):
89 files.append (x.group (1))
92 re.sub ('\n--- a/([^\n]+)\n',
94 re.sub('\n--- /dev/null\n\\+\\+\\+ b/([^\n]+)',
100 return self.touched_files () <> []
102 def apply (self, add_del_files):
103 def note_add_file (x):
104 add_del_files.append (('add', x.group (1)))
107 def note_del_file (x):
108 add_del_files.append (('del', x.group (1)))
111 re.sub('\n--- /dev/null\n\\+\\+\\+ b/([^\n]+)',
112 note_add_file, self.diff)
114 re.sub('\n--- a/([^\n]+)\n\\+\\+\\+ /dev/null',
115 note_del_file, self.diff)
117 p = os.popen ('patch -f -p1 ', 'w')
121 raise PatchFailed, self.committish
124 def parse_commit_log (log):
125 committish = re.search ('^([^\n]+)', log).group (1)
126 author = re.search ('\nAuthor:\s+([^\n]+)', log).group (1)
127 date_match = re.search ('\nDate:\s+([^\n]+)', log)
128 date = date_match.group (1)
129 log = log[date_match.end (1):]
131 message = re.sub ("\n *", '', log)
132 message = message.strip ()
134 c = Commit (locals ())
137 def parse_add_changes (from_commit, max_count=0):
143 assert max_count == 1
144 opt = '--max-count=%d' % max_count
147 log = read_pipe ('git log %(opt)s %(from_commit)s%(rest)s' % locals ())
149 log = log[len ('commit '):]
155 commits = map (parse_commit_log, re.split ('\ncommit ', log))
162 return '%d-%02d-%02d %s <%s>\n' % (commit.date[:3] + (commit.name, commit.email))
164 def changelog_body (commit):
166 s += ''.join ('\n* %s: ' % f for f in commit.touched_files())
167 s += '\n' + commit.message
169 s = s.replace ('\n', '\n\t')
174 p = optparse.OptionParser (usage="usage git-update-changelog.py [options] [commits]",
176 Apply GIT patches and update change log.
178 Run this file from the CVS directory, with commits from the repository in --git-dir.
184 p.add_option ("--start",
189 help="all commits starting with FIRST.")
191 p.add_option ("--git-dir",
195 help="the GIT directory to merge.")
197 (options, args) = p.parse_args ()
199 log = open ('ChangeLog').read ()
202 os.environ['GIT_DIR'] = options.gitdir
206 if not options.start:
207 print 'Must set start committish.'
210 commits = parse_add_changes (options.start)
214 commits += parse_add_changes (a, max_count=1)
222 first = header (commits[0]) + '\n'
223 if first == log[:len (first)]:
224 log = log[len (first):]
227 previously_done = dict((c, 1) for c in open ('.git-commits-done').read ().split ('\n'))
231 commits = [c for c in commits if not previously_done.has_key (c.committish)]
232 commits = sorted (commits, cmp=Commit.compare)
238 collated_message = ''
243 if not c.has_patch ():
244 print 'patchless commit (merge?)'
250 print "Patch doesn't seem to apply"
251 print 'skipping', c.committish
252 print 'message:', c.message
257 commits = commits[1:]
258 commits_done.append (c)
260 print 'patch ', c.committish
262 c.apply (file_adddel)
266 if c.touched_files () == ['ChangeLog']:
270 and c.author != last_commit.author
271 and c.date[:3] != last_commit.date[:3]):
273 new_log += header (last_commit)
275 collated_log = changelog_body (c) + collated_log
278 collated_message += c.message + '\n'
282 for (op, f) in file_adddel:
284 system ('cvs remove %(f)s' % locals ())
286 system ('cvs add %(f)s' % locals ())
289 collated_log = header (last_commit) + collated_log + '\n'
291 log = collated_log + log
294 os.unlink ('ChangeLog~')
298 os.rename ('ChangeLog', 'ChangeLog~')
299 open ('ChangeLog', 'w').write (log)
301 open ('.msg','w').write (collated_message)
302 print '\nCommit message\n**\n%s\n**\n' % collated_message
303 print '\nRun:\n\n\tcvs commit -F .msg\n\n'
304 print '\n\techo %s >> .git-commits-done\n\n' % ' '.join ([c.committish
305 for c in commits_done])
309 print 'Commits left to do:'
310 print ' '.join ([c.committish for c in commits])