]> git.donarmstrong.com Git - lilypond.git/blob - buildscripts/git-update-changelog.py
* emacsclient.patch:
[lilypond.git] / buildscripts / git-update-changelog.py
1 #!/usr/bin/python
2
3 import time
4 import os
5 import re
6 import optparse
7
8 def read_pipe (x):
9     print 'pipe', x
10     return os.popen (x).read ()
11 def system (x):
12     print x
13     return os.system (x)
14     
15 class PatchFailed(Exception):
16     pass
17
18 class Commit:
19     def __init__ (self, dict):
20         for v in ('message',
21                   'date',
22                   'author',
23                   'committish'):
24             self.__dict__[v] = dict[v]
25
26         # Sat Oct 28 18:52:30 2006 +0200
27         
28         self.date = ' '.join  (self.date.split (' ')[:-1])
29         self.date = time.strptime (self.date, '%a %b %d %H:%M:%S %Y')
30         
31         m = re.search ('(.*)<(.*)>', self.author)
32         self.email = m.group (2).strip ()
33         self.name = m.group (1).strip ()
34         self.diff = read_pipe ('git show %s' % self.committish)
35     def touched_files (self):
36
37         files = []
38         def note_file (x):
39             files.append (x.group (1))
40             return ''
41
42         re.sub ('\n--- a/([^\n]+)\n',
43                 note_file, self.diff)
44         re.sub('\n--- /dev/null\n\\+\\+\\+ b/([^\n]+)',
45                note_file, self.diff)
46
47         return files
48
49     def apply (self, add_del_files):
50         def note_add_file (x):
51             add_del_files.append (('add', x.group (1)))
52             return ''
53         
54         def note_del_file (x):
55             add_del_files.append (('del', x.group (1)))
56             return ''
57         
58         
59         re.sub('\n--- /dev/null\n\\+\\+\\+ b/([^\n]+)',
60                note_add_file, self.diff)
61         
62         re.sub('\n--- a/([^\n]+)\n\\+\\+\\+ /dev/null',
63                note_del_file, self.diff)
64
65         p = os.popen ('patch -f -p1 ', 'w')
66         p.write (self.diff)
67
68         if p.close ():
69             raise PatchFailed, self.committish
70         
71     
72 def parse_commit_log (log):
73     committish = re.search ('^([^\n]+)', log).group (1)
74     author = re.search ('\nAuthor:\s+([^\n]+)', log).group (1)
75     date_match = re.search ('\nDate:\s+([^\n]+)', log)
76     date = date_match.group (1)
77     log = log[date_match.end (1):]
78
79     message = re.sub ("\n *", '', log)
80     message = message.strip ()
81
82     c = Commit (locals ())
83     return c
84
85 def parse_add_changes (from_commit):
86     
87     log = read_pipe ('git log %(from_commit)s..' % locals ())
88
89     log = log[len ('commit '):]
90     log = log.strip ()
91
92     if not log:
93         return []
94         
95     commits = map (parse_commit_log, re.split ('\ncommit ', log))
96     commits.reverse ()
97     
98     return commits
99
100
101 def header (commit):
102     return '%d-%02d-%02d  %s  <%s>\n' % (commit.date[:3] + (commit.name, commit.email))
103
104 def changelog_body (commit):
105
106     s = ''
107     s += "\ngit commit %s\n" % commit.committish    
108     s += ''.join ('\n* %s: ' % f for f in commit.touched_files())
109     s += '\n' + commit.message
110     
111     s = s.replace ('\n', '\n\t')
112     s += '\n'
113     return s
114         
115 def find_last_checked_in_commit (log):
116     m = re.match ('^(\\d+-\\d+-\\d+)[^\n]+\n*\tgit commit ([a-f0-9]+)', log)
117     
118     if m:
119         return (m.group (1), m.group (2))
120
121     return None
122
123
124
125
126 def main ():
127     p = optparse.OptionParser (usage="usage git-update-changelog.py [options]",
128                                description="""
129 Apply GIT patches and update change log.
130
131 Run this file from the CVS directory, with --git-dir 
132 """)
133     p.add_option ("--start",
134                   action='store',
135                   default='',
136                   dest="start",
137                   help="start of log messages to merge.")
138     
139     p.add_option ("--git-dir",
140                   action='store',
141                   default='',
142                   dest="gitdir",
143                   help="the GIT directory to merge.")
144
145     (options, args) = p.parse_args ()
146     
147     log = open ('ChangeLog').read ()
148
149     if not options.start:
150         (time, id) = find_last_checked_in_commit (log)
151         options.start = id
152
153         print 'processing commits from ', id, options.start
154
155     if options.gitdir:
156         os.environ['GIT_DIR'] = options.gitdir
157     
158     commits = parse_add_changes (options.start)
159     if not commits:
160         return
161     
162     new_log = ''
163     last_commit = None
164
165     first = header (commits[0]) + '\n'
166     if first == log[:len (first)]:
167         log = log[len (first):]
168
169     file_adddel = []
170     for c in commits:
171         print 'patch ', c.committish
172         try:
173             c.apply (file_adddel)
174         except PatchFailed:
175             break
176         
177         if c.touched_files () == ['ChangeLog']:
178             continue
179         
180         if (last_commit
181             and c.author != last_commit.author
182             and c.date[:3] != last_commit.date[:3]):
183
184             new_log += header (last_commit)
185
186         new_log += changelog_body (c)  
187         last_commit = c
188         
189     for (op, f) in file_adddel:
190         if op == 'del':
191             system ('cvs remove %(f)s' % locals ())
192         if op == 'add':
193             system ('cvs add %(f)s' % locals ())
194
195     new_log = header (last_commit) + new_log + '\n'
196
197     log = new_log + log
198
199     try:
200         os.unlink ('ChangeLog~')
201     except OSError:
202         pass
203     
204     os.rename ('ChangeLog', 'ChangeLog~')
205     open ('ChangeLog', 'w').write (log)
206     
207 main ()
208     
209     
210