Mercurial > hg-stable
comparison hgext/patchbomb.py @ 7354:cad454f295b0
patchbomb: extract a bunch of nested functions
- clarifies dependencies on variables
- extracts potentially useful utility functions
- no need for separate confirm() function
- error message style conformance
- PEP 8 conformance
author | Dirkjan Ochtman <dirkjan@ochtman.nl> |
---|---|
date | Wed, 12 Nov 2008 14:36:16 +0100 |
parents | 982b55ec80be |
children | b0fa5dbd9cdd |
comparison
equal
deleted
inserted
replaced
7353:982b55ec80be | 7354:cad454f295b0 |
---|---|
79 | 79 |
80 def close(self): | 80 def close(self): |
81 self.container.append(''.join(self.lines).split('\n')) | 81 self.container.append(''.join(self.lines).split('\n')) |
82 self.lines = [] | 82 self.lines = [] |
83 | 83 |
84 def prompt(ui, prompt, default=None, rest=': ', empty_ok=False): | |
85 if not ui.interactive: | |
86 return default | |
87 if default: | |
88 prompt += ' [%s]' % default | |
89 prompt += rest | |
90 while True: | |
91 r = ui.prompt(prompt, default=default) | |
92 if r: | |
93 return r | |
94 if default is not None: | |
95 return default | |
96 if empty_ok: | |
97 return r | |
98 ui.warn(_('Please enter a valid value.\n')) | |
99 | |
100 def cdiffstat(ui, summary, patchlines): | |
101 s = patch.diffstat(patchlines) | |
102 if s: | |
103 if summary: | |
104 ui.write(summary, '\n') | |
105 ui.write(s, '\n') | |
106 ans = prompt(ui, _('Does the diffstat above look okay? '), 'y') | |
107 if not ans.lower().startswith('y'): | |
108 raise util.Abort(_('diffstat rejected')) | |
109 elif s is None: | |
110 ui.warn(_('no diffstat information available\n')) | |
111 s = '' | |
112 return s | |
113 | |
114 def makepatch(ui, repo, patch, opts, _charsets, idx, total, patchname=None): | |
115 | |
116 desc = [] | |
117 node = None | |
118 body = '' | |
119 | |
120 for line in patch: | |
121 if line.startswith('#'): | |
122 if line.startswith('# Node ID'): | |
123 node = line.split()[-1] | |
124 continue | |
125 if line.startswith('diff -r') or line.startswith('diff --git'): | |
126 break | |
127 desc.append(line) | |
128 | |
129 if not patchname and not node: | |
130 raise ValueError | |
131 | |
132 if opts.get('attach'): | |
133 body = ('\n'.join(desc[1:]).strip() or | |
134 'Patch subject is complete summary.') | |
135 body += '\n\n\n' | |
136 | |
137 if opts.get('plain'): | |
138 while patch and patch[0].startswith('# '): | |
139 patch.pop(0) | |
140 if patch: | |
141 patch.pop(0) | |
142 while patch and not patch[0].strip(): | |
143 patch.pop(0) | |
144 | |
145 if opts.get('diffstat'): | |
146 body += cdiffstat(ui, '\n'.join(desc), patch) + '\n\n' | |
147 | |
148 if opts.get('attach') or opts.get('inline'): | |
149 msg = email.MIMEMultipart.MIMEMultipart() | |
150 if body: | |
151 msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test'))) | |
152 p = mail.mimetextpatch('\n'.join(patch), 'x-patch', opts.get('test')) | |
153 binnode = bin(node) | |
154 # if node is mq patch, it will have patch file name as tag | |
155 if not patchname: | |
156 patchtags = [t for t in repo.nodetags(binnode) | |
157 if t.endswith('.patch') or t.endswith('.diff')] | |
158 if patchtags: | |
159 patchname = patchtags[0] | |
160 elif total > 1: | |
161 patchname = cmdutil.make_filename(repo, '%b-%n.patch', | |
162 binnode, idx, total) | |
163 else: | |
164 patchname = cmdutil.make_filename(repo, '%b.patch', binnode) | |
165 disposition = 'inline' | |
166 if opts.get('attach'): | |
167 disposition = 'attachment' | |
168 p['Content-Disposition'] = disposition + '; filename=' + patchname | |
169 msg.attach(p) | |
170 else: | |
171 body += '\n'.join(patch) | |
172 msg = mail.mimetextpatch(body, display=opts.get('test')) | |
173 | |
174 subj = desc[0].strip().rstrip('. ') | |
175 if total == 1: | |
176 subj = '[PATCH] ' + (opts.get('subject') or subj) | |
177 else: | |
178 tlen = len(str(total)) | |
179 subj = '[PATCH %0*d of %d] %s' % (tlen, idx, total, subj) | |
180 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test')) | |
181 msg['X-Mercurial-Node'] = node | |
182 return msg, subj | |
183 | |
84 def patchbomb(ui, repo, *revs, **opts): | 184 def patchbomb(ui, repo, *revs, **opts): |
85 '''send changesets by email | 185 '''send changesets by email |
86 | 186 |
87 By default, diffs are sent in the format generated by hg export, | 187 By default, diffs are sent in the format generated by hg export, |
88 one per message. The series starts with a "[PATCH 0 of N]" | 188 one per message. The series starts with a "[PATCH 0 of N]" |
123 Before using this command, you will need to enable email in your hgrc. | 223 Before using this command, you will need to enable email in your hgrc. |
124 See the [email] section in hgrc(5) for details. | 224 See the [email] section in hgrc(5) for details. |
125 ''' | 225 ''' |
126 | 226 |
127 _charsets = mail._charsets(ui) | 227 _charsets = mail._charsets(ui) |
128 | |
129 def prompt(prompt, default = None, rest = ': ', empty_ok = False): | |
130 if not ui.interactive: | |
131 return default | |
132 if default: | |
133 prompt += ' [%s]' % default | |
134 prompt += rest | |
135 while True: | |
136 r = ui.prompt(prompt, default=default) | |
137 if r: | |
138 return r | |
139 if default is not None: | |
140 return default | |
141 if empty_ok: | |
142 return r | |
143 ui.warn(_('Please enter a valid value.\n')) | |
144 | |
145 def confirm(s, denial): | |
146 if not prompt(s, default = 'y', rest = '? ').lower().startswith('y'): | |
147 raise util.Abort(denial) | |
148 | |
149 def cdiffstat(summary, patchlines): | |
150 s = patch.diffstat(patchlines) | |
151 if s: | |
152 if summary: | |
153 ui.write(summary, '\n') | |
154 ui.write(s, '\n') | |
155 confirm(_('Does the diffstat above look okay'), | |
156 _('diffstat rejected')) | |
157 elif s is None: | |
158 ui.warn(_('No diffstat information available.\n')) | |
159 s = '' | |
160 return s | |
161 | |
162 def makepatch(patch, idx, total, patchname=None): | |
163 desc = [] | |
164 node = None | |
165 body = '' | |
166 for line in patch: | |
167 if line.startswith('#'): | |
168 if line.startswith('# Node ID'): | |
169 node = line.split()[-1] | |
170 continue | |
171 if line.startswith('diff -r') or line.startswith('diff --git'): | |
172 break | |
173 desc.append(line) | |
174 if not patchname and not node: | |
175 raise ValueError | |
176 | |
177 if opts.get('attach'): | |
178 body = ('\n'.join(desc[1:]).strip() or | |
179 'Patch subject is complete summary.') | |
180 body += '\n\n\n' | |
181 | |
182 if opts.get('plain'): | |
183 while patch and patch[0].startswith('# '): | |
184 patch.pop(0) | |
185 if patch: | |
186 patch.pop(0) | |
187 while patch and not patch[0].strip(): | |
188 patch.pop(0) | |
189 if opts.get('diffstat'): | |
190 body += cdiffstat('\n'.join(desc), patch) + '\n\n' | |
191 if opts.get('attach') or opts.get('inline'): | |
192 msg = email.MIMEMultipart.MIMEMultipart() | |
193 if body: | |
194 msg.attach(mail.mimeencode(ui, body, _charsets, | |
195 opts.get('test'))) | |
196 p = mail.mimetextpatch('\n'.join(patch), 'x-patch', | |
197 opts.get('test')) | |
198 binnode = bin(node) | |
199 # if node is mq patch, it will have patch file name as tag | |
200 if not patchname: | |
201 patchtags = [t for t in repo.nodetags(binnode) | |
202 if t.endswith('.patch') or t.endswith('.diff')] | |
203 if patchtags: | |
204 patchname = patchtags[0] | |
205 elif total > 1: | |
206 patchname = cmdutil.make_filename(repo, '%b-%n.patch', | |
207 binnode, idx, total) | |
208 else: | |
209 patchname = cmdutil.make_filename(repo, '%b.patch', binnode) | |
210 disposition = 'inline' | |
211 if opts.get('attach'): | |
212 disposition = 'attachment' | |
213 p['Content-Disposition'] = disposition + '; filename=' + patchname | |
214 msg.attach(p) | |
215 else: | |
216 body += '\n'.join(patch) | |
217 msg = mail.mimetextpatch(body, display=opts.get('test')) | |
218 | |
219 subj = desc[0].strip().rstrip('. ') | |
220 if total == 1: | |
221 subj = '[PATCH] ' + (opts.get('subject') or subj) | |
222 else: | |
223 tlen = len(str(total)) | |
224 subj = '[PATCH %0*d of %d] %s' % (tlen, idx, total, subj) | |
225 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test')) | |
226 msg['X-Mercurial-Node'] = node | |
227 return msg, subj | |
228 | 228 |
229 def outgoing(dest, revs): | 229 def outgoing(dest, revs): |
230 '''Return the revisions present locally but not in dest''' | 230 '''Return the revisions present locally but not in dest''' |
231 dest = ui.expandpath(dest or 'default-push', dest or 'default') | 231 dest = ui.expandpath(dest or 'default-push', dest or 'default') |
232 revs = [repo.lookup(rev) for rev in revs] | 232 revs = [repo.lookup(rev) for rev in revs] |
310 name = None | 310 name = None |
311 for p, i in zip(patches, xrange(len(patches))): | 311 for p, i in zip(patches, xrange(len(patches))): |
312 jumbo.extend(p) | 312 jumbo.extend(p) |
313 if patchnames: | 313 if patchnames: |
314 name = patchnames[i] | 314 name = patchnames[i] |
315 msgs.append(makepatch(p, i + 1, len(patches), name)) | 315 msg = makepatch(ui, repo, p, opts, _charsets, i + 1, |
316 len(patches), name) | |
317 msgs.append(msg) | |
316 | 318 |
317 if len(patches) > 1: | 319 if len(patches) > 1: |
318 tlen = len(str(len(patches))) | 320 tlen = len(str(len(patches))) |
319 | 321 |
320 subj = '[PATCH %0*d of %d] %s' % ( | 322 subj = '[PATCH %0*d of %d] %s' % ( |
321 tlen, 0, len(patches), | 323 tlen, 0, len(patches), |
322 opts.get('subject') or | 324 opts.get('subject') or |
323 prompt('Subject:', | 325 prompt(ui, 'Subject:', |
324 rest=' [PATCH %0*d of %d] ' % (tlen, 0, len(patches)))) | 326 rest=' [PATCH %0*d of %d] ' % (tlen, 0, len(patches)))) |
325 | 327 |
326 body = '' | 328 body = '' |
327 if opts.get('diffstat'): | 329 if opts.get('diffstat'): |
328 d = cdiffstat(_('Final summary:\n'), jumbo) | 330 d = cdiffstat(ui, _('Final summary:\n'), jumbo) |
329 if d: | 331 if d: |
330 body = '\n' + d | 332 body = '\n' + d |
331 | 333 |
332 body = getdescription(body, sender) | 334 body = getdescription(body, sender) |
333 msg = mail.mimeencode(ui, body, _charsets, opts.get('test')) | 335 msg = mail.mimeencode(ui, body, _charsets, opts.get('test')) |
337 msgs.insert(0, (msg, subj)) | 339 msgs.insert(0, (msg, subj)) |
338 return msgs | 340 return msgs |
339 | 341 |
340 def getbundlemsgs(bundle): | 342 def getbundlemsgs(bundle): |
341 subj = (opts.get('subject') | 343 subj = (opts.get('subject') |
342 or prompt('Subject:', default='A bundle for your repository')) | 344 or prompt(ui, 'Subject:', 'A bundle for your repository')) |
343 | 345 |
344 body = getdescription('', sender) | 346 body = getdescription('', sender) |
345 msg = email.MIMEMultipart.MIMEMultipart() | 347 msg = email.MIMEMultipart.MIMEMultipart() |
346 if body: | 348 if body: |
347 msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test'))) | 349 msg.attach(mail.mimeencode(ui, body, _charsets, opts.get('test'))) |
354 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test')) | 356 msg['Subject'] = mail.headencode(ui, subj, _charsets, opts.get('test')) |
355 return [(msg, subj)] | 357 return [(msg, subj)] |
356 | 358 |
357 sender = (opts.get('from') or ui.config('email', 'from') or | 359 sender = (opts.get('from') or ui.config('email', 'from') or |
358 ui.config('patchbomb', 'from') or | 360 ui.config('patchbomb', 'from') or |
359 prompt('From', ui.username())) | 361 prompt(ui, 'From', ui.username())) |
360 | 362 |
361 patches = opts.get('patches') | 363 patches = opts.get('patches') |
362 if patches: | 364 if patches: |
363 msgs = getpatchmsgs(patches, opts.get('patchnames')) | 365 msgs = getpatchmsgs(patches, opts.get('patchnames')) |
364 elif opts.get('bundle'): | 366 elif opts.get('bundle'): |
372 msgs = getpatchmsgs(patches) | 374 msgs = getpatchmsgs(patches) |
373 | 375 |
374 def getaddrs(opt, prpt, default = None): | 376 def getaddrs(opt, prpt, default = None): |
375 addrs = opts.get(opt) or (ui.config('email', opt) or | 377 addrs = opts.get(opt) or (ui.config('email', opt) or |
376 ui.config('patchbomb', opt) or | 378 ui.config('patchbomb', opt) or |
377 prompt(prpt, default = default)).split(',') | 379 prompt(ui, prpt, default)).split(',') |
378 return [mail.addressencode(ui, a.strip(), _charsets, opts.get('test')) | 380 return [mail.addressencode(ui, a.strip(), _charsets, opts.get('test')) |
379 for a in addrs if a.strip()] | 381 for a in addrs if a.strip()] |
380 | 382 |
381 to = getaddrs('to', 'To') | 383 to = getaddrs('to', 'To') |
382 cc = getaddrs('cc', 'Cc', '') | 384 cc = getaddrs('cc', 'Cc', '') |