Mercurial > hg
comparison contrib/patchbomb @ 876:14cfaaec2e8e
Get patchbomb script to not use MIME attachments.
Adding patches as attachments makes it difficult or impossible for some
mail clients to quote them effectively.
author | Bryan O'Sullivan <bos@serpentine.com> |
---|---|
date | Tue, 09 Aug 2005 20:53:50 -0800 |
parents | d3f836bf6cc1 |
children | 25430c523677 |
comparison
equal
deleted
inserted
replaced
875:d3f836bf6cc1 | 876:14cfaaec2e8e |
---|---|
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'), |