72 try: os.unlink(name) |
72 try: os.unlink(name) |
73 except: pass |
73 except: pass |
74 |
74 |
75 def patchbomb(ui, repo, *revs, **opts): |
75 def patchbomb(ui, repo, *revs, **opts): |
76 def prompt(prompt, default = None, rest = ': ', empty_ok = False): |
76 def prompt(prompt, default = None, rest = ': ', empty_ok = False): |
77 try: |
77 if default: prompt += ' [%s]' % default |
78 if default: prompt += ' [%s]' % default |
78 prompt += rest |
79 prompt += rest |
79 while True: |
80 r = raw_input(prompt) |
80 r = raw_input(prompt) |
81 if not r and not empty_ok: raise EOFError |
81 if r: return r |
82 return r |
82 if default is not None: return default |
83 except EOFError: |
83 if empty_ok: return r |
84 if default is None: raise |
84 print >> sys.stderr, 'Please enter a valid value.' |
85 return default |
|
86 |
85 |
87 def confirm(s): |
86 def confirm(s): |
88 if not prompt(s, default = 'y', rest = '? ').lower().startswith('y'): |
87 if not prompt(s, default = 'y', rest = '? ').lower().startswith('y'): |
89 raise ValueError |
88 raise ValueError |
90 |
89 |
95 ui.write(summary, '\n') |
94 ui.write(summary, '\n') |
96 ui.write(s, '\n') |
95 ui.write(s, '\n') |
97 confirm('Does the diffstat above look okay') |
96 confirm('Does the diffstat above look okay') |
98 return s |
97 return s |
99 |
98 |
100 def make_patch(patch, idx, total): |
99 def makepatch(patch, idx, total): |
101 desc = [] |
100 desc = [] |
102 node = None |
101 node = None |
103 for line in patch: |
102 for line in patch: |
104 if line.startswith('#'): |
103 if line.startswith('#'): |
105 if line.startswith('# Node ID'): node = line.split()[-1] |
104 if line.startswith('# Node ID'): node = line.split()[-1] |
106 continue |
105 continue |
107 if line.startswith('diff -r'): break |
106 if line.startswith('diff -r'): break |
108 desc.append(line) |
107 desc.append(line) |
109 if not node: raise ValueError |
108 if not node: raise ValueError |
110 msg = MIMEMultipart() |
109 body = ('\n'.join(desc[1:]).strip() or |
111 msg['X-Mercurial-Node'] = node |
110 'Patch subject is complete summary.') |
|
111 body += '\n\n\n' + cdiffstat('\n'.join(desc), patch) + '\n\n' |
|
112 body += '\n'.join(patch) |
|
113 msg = MIMEText(body) |
112 subj = '[PATCH %d of %d] %s' % (idx, total, desc[0].strip()) |
114 subj = '[PATCH %d of %d] %s' % (idx, total, desc[0].strip()) |
113 if subj.endswith('.'): subj = subj[:-1] |
115 if subj.endswith('.'): subj = subj[:-1] |
114 msg['Subject'] = subj |
116 msg['Subject'] = subj |
115 body = '\n'.join(desc[1:]).strip() + '\n' |
117 msg['X-Mercurial-Node'] = node |
116 summary = subj |
|
117 if body != '\n': |
|
118 msg.attach(MIMEText(body)) |
|
119 summary += '\n\n' + body |
|
120 else: |
|
121 summary += '\n' |
|
122 d = cdiffstat(summary, patch) |
|
123 if d: msg.attach(MIMEText(d)) |
|
124 p = MIMEText('\n'.join(patch), 'x-patch') |
|
125 p['Content-Disposition'] = commands.make_filename(repo, None, |
|
126 'inline; filename=%b-%n.patch', |
|
127 seqno = idx) |
|
128 msg.attach(p) |
|
129 return msg |
118 return msg |
130 |
119 |
131 start_time = int(time.time()) |
120 start_time = int(time.time()) |
132 |
121 |
133 def make_msgid(id): |
122 def genmsgid(id): |
134 return '<%s.%s@%s>' % (id[:20], start_time, socket.getfqdn()) |
123 return '<%s.%s@%s>' % (id[:20], start_time, socket.getfqdn()) |
135 |
124 |
136 patches = [] |
125 patches = [] |
137 |
126 |
138 class exportee: |
127 class exportee: |
139 def __init__(self, container): |
128 def __init__(self, container): |
140 self.lines = [] |
129 self.lines = [] |
141 self.container = container |
130 self.container = container |
|
131 self.name = 'email' |
142 |
132 |
143 def write(self, data): |
133 def write(self, data): |
144 self.lines.append(data) |
134 self.lines.append(data) |
145 |
135 |
146 def close(self): |
136 def close(self): |
154 |
144 |
155 ui.write('This patch series consists of %d patches.\n\n' % len(patches)) |
145 ui.write('This patch series consists of %d patches.\n\n' % len(patches)) |
156 |
146 |
157 for p, i in zip(patches, range(len(patches))): |
147 for p, i in zip(patches, range(len(patches))): |
158 jumbo.extend(p) |
148 jumbo.extend(p) |
159 msgs.append(make_patch(p, i + 1, len(patches))) |
149 msgs.append(makepatch(p, i + 1, len(patches))) |
160 |
150 |
161 ui.write('\nWrite the introductory message for the patch series.\n\n') |
151 ui.write('\nWrite the introductory message for the patch series.\n\n') |
162 |
152 |
163 sender = opts['sender'] or prompt('From', ui.username()) |
153 sender = opts['sender'] or prompt('From', ui.username()) |
164 |
154 |
186 d = cdiffstat('Final summary:\n', jumbo) |
176 d = cdiffstat('Final summary:\n', jumbo) |
187 if d: msg.attach(MIMEText(d)) |
177 if d: msg.attach(MIMEText(d)) |
188 |
178 |
189 msgs.insert(0, msg) |
179 msgs.insert(0, msg) |
190 |
180 |
191 s = smtplib.SMTP() |
181 if not opts['test']: |
192 s.connect(host = ui.config('smtp', 'host', 'mail'), |
182 s = smtplib.SMTP() |
193 port = int(ui.config('smtp', 'port', 25))) |
183 s.connect(host = ui.config('smtp', 'host', 'mail'), |
194 |
184 port = int(ui.config('smtp', 'port', 25))) |
195 refs = [] |
185 |
196 parent = None |
186 parent = None |
197 tz = time.strftime('%z') |
187 tz = time.strftime('%z') |
198 for m in msgs: |
188 for m in msgs: |
199 try: |
189 try: |
200 m['Message-Id'] = make_msgid(m['X-Mercurial-Node']) |
190 m['Message-Id'] = genmsgid(m['X-Mercurial-Node']) |
201 except TypeError: |
191 except TypeError: |
202 m['Message-Id'] = make_msgid('patchbomb') |
192 m['Message-Id'] = genmsgid('patchbomb') |
203 if parent: |
193 if parent: |
204 m['In-Reply-To'] = parent |
194 m['In-Reply-To'] = parent |
205 parent = m['Message-Id'] |
195 else: |
206 if len(refs) > 1: |
196 parent = m['Message-Id'] |
207 m['References'] = ' '.join(refs[:-1]) |
|
208 refs.append(parent) |
|
209 m['Date'] = time.strftime('%a, %m %b %Y %T ', time.localtime(start_time)) + tz |
197 m['Date'] = time.strftime('%a, %m %b %Y %T ', time.localtime(start_time)) + tz |
210 start_time += 1 |
198 start_time += 1 |
211 m['From'] = sender |
199 m['From'] = sender |
212 m['To'] = ', '.join(to) |
200 m['To'] = ', '.join(to) |
213 if cc: m['Cc'] = ', '.join(cc) |
201 if cc: m['Cc'] = ', '.join(cc) |
217 fp.write(m.as_string(0)) |
205 fp.write(m.as_string(0)) |
218 fp.write('\n') |
206 fp.write('\n') |
219 fp.close() |
207 fp.close() |
220 else: |
208 else: |
221 s.sendmail(sender, to + cc, m.as_string(0)) |
209 s.sendmail(sender, to + cc, m.as_string(0)) |
222 s.close() |
210 if not opts['test']: |
|
211 s.close() |
223 |
212 |
224 if __name__ == '__main__': |
213 if __name__ == '__main__': |
225 optspec = [('c', 'cc', [], 'email addresses of copy recipients'), |
214 optspec = [('c', 'cc', [], 'email addresses of copy recipients'), |
226 ('n', 'test', None, 'print messages that would be sent'), |
215 ('n', 'test', None, 'print messages that would be sent'), |
227 ('s', 'sender', '', 'email address of sender'), |
216 ('s', 'sender', '', 'email address of sender'), |