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
)
57 if not os
.path
.exists (filename
):
60 contents
= open (filename
).read ()
67 def check_diff (self
):
68 chunks
= re
.split ('\ndiff --git ', self
.diff
)
72 m
= re
.search ('^a/([^ ]+)', c
)
78 c
= re
.sub('\n--- [^\n]+', '', c
)
79 ok
= ok
and self
.check_diff_chunk (file, c
)
85 def touched_files (self
):
88 files
.append (x
.group (1))
91 re
.sub ('\n--- a/([^\n]+)\n',
93 re
.sub('\n--- /dev/null\n\\+\\+\\+ b/([^\n]+)',
99 return self
.touched_files () <> []
101 def apply (self
, add_del_files
):
102 def note_add_file (x
):
103 add_del_files
.append (('add', x
.group (1)))
106 def note_del_file (x
):
107 add_del_files
.append (('del', x
.group (1)))
110 re
.sub('\n--- /dev/null\n\\+\\+\\+ b/([^\n]+)',
111 note_add_file
, self
.diff
)
113 re
.sub('\n--- a/([^\n]+)\n\\+\\+\\+ /dev/null',
114 note_del_file
, self
.diff
)
116 p
= os
.popen ('patch -f -p1 ', 'w')
120 raise PatchFailed
, self
.committish
123 def parse_commit_log (log
):
124 committish
= re
.search ('^([^\n]+)', log
).group (1)
125 author
= re
.search ('\nAuthor:\s+([^\n]+)', log
).group (1)
126 date_match
= re
.search ('\nDate:\s+([^\n]+)', log
)
127 date
= date_match
.group (1)
128 log
= log
[date_match
.end (1):]
130 message
= re
.sub ("\n *", '', log
)
131 message
= message
.strip ()
133 c
= Commit (locals ())
136 def parse_add_changes (from_commit
, max_count
=0):
142 assert max_count
== 1
143 opt
= '--max-count=%d' % max_count
146 log
= read_pipe ('git log %(opt)s %(from_commit)s%(rest)s' % locals ())
148 log
= log
[len ('commit '):]
154 commits
= map (parse_commit_log
, re
.split ('\ncommit ', log
))
161 return '%d-%02d-%02d %s <%s>\n' % (commit
.date
[:3] + (commit
.name
, commit
.email
))
163 def changelog_body (commit
):
165 s
+= ''.join ('\n* %s: ' % f
for f
in commit
.touched_files())
166 s
+= '\n' + commit
.message
168 s
= s
.replace ('\n', '\n\t')
173 p
= optparse
.OptionParser (usage
="usage git-update-changelog.py [options] [commits]",
175 Apply GIT patches and update change log.
177 Run this file from the CVS directory, with commits from the repository in --git-dir.
180 p
.add_option ("--start",
185 help="all commits starting with FIRST (exclusive).")
187 p
.add_option ("--git-dir",
191 help="the GIT directory to merge.")
193 (options
, args
) = p
.parse_args ()
195 log
= open ('ChangeLog').read ()
198 os
.environ
['GIT_DIR'] = options
.gitdir
202 if not options
.start
:
203 print 'Must set start committish.'
206 commits
= parse_add_changes (options
.start
)
210 commits
+= parse_add_changes (a
, max_count
=1)
218 first
= header (commits
[0]) + '\n'
219 if first
== log
[:len (first
)]:
220 log
= log
[len (first
):]
223 previously_done
= dict((c
, 1) for c
in open ('.git-commits-done').read ().split ('\n'))
227 commits
= [c
for c
in commits
if not previously_done
.has_key (c
.committish
)]
228 commits
= sorted (commits
, cmp=Commit
.compare
)
234 collated_message
= ''
239 if not c
.has_patch ():
240 print 'patchless commit (merge?)'
246 print "Patch doesn't seem to apply"
247 print 'skipping', c
.committish
248 print 'message:', c
.message
253 commits
= commits
[1:]
254 commits_done
.append (c
)
256 print 'patch ', c
.committish
258 c
.apply (file_adddel
)
262 if c
.touched_files () == ['ChangeLog']:
266 and c
.author
!= last_commit
.author
267 and c
.date
[:3] != last_commit
.date
[:3]):
269 new_log
+= header (last_commit
)
271 collated_log
= changelog_body (c
) + collated_log
274 collated_message
+= c
.message
+ '\n'
278 for (op
, f
) in file_adddel
:
280 system ('cvs remove %(f)s' % locals ())
282 system ('cvs add %(f)s' % locals ())
285 collated_log
= header (last_commit
) + collated_log
+ '\n'
287 log
= collated_log
+ log
290 os
.unlink ('ChangeLog~')
294 os
.rename ('ChangeLog', 'ChangeLog~')
295 open ('ChangeLog', 'w').write (log
)
297 open ('.msg','w').write (collated_message
)
298 print '\nCommit message\n**\n%s\n**\n' % collated_message
299 print '\nRun:\n\n\tcvs commit -F .msg\n\n'
300 print '\n\techo %s >> .git-commits-done\n\n' % ' '.join ([c
.committish
301 for c
in commits_done
])
305 print 'Commits left to do:'
306 print ' '.join ([c
.committish
for c
in commits
])