On 10/23/2012 09:00 AM, Simo Sorce wrote:
I strongly suggest you use git-send-email instead of thunderbird, it makes everything a lot faster, see the instructions I sent in my followup email.
I wrote a python script to manage my patch submissions a while ago which might be useful to folks, it's attached.
The basic idea is you keep a directory of your patch submissions. Inside the directory is also a file that has then next number to use for your submission. By default it runs git format-patch selecting the last commit. It creates a patch file using the patch submission format defined for IPA. If you use the -s option it also sends it to the list. It doesn't use git-send-email, rather it builds an email with a mime attachment according to our IPA recommendations. I don't recall why I didn't use git-send-email, but there was some reason (probably because I couldn't get it follow the IPA conventions, not sure though).
If you have to rework a patch use the -n option to specify which patch you're modifying. The script automatically searches the patch directory and finds the next revision number for the patch.
The config dict at the top will have to be modified to match your username, smtp server, etc. look for anything in UPPERCASE and replace with your specifics.
I like to use it because I don't have to remember my patch numbers and the result will always follow the IPA conventions without any fumbling around.
Petr3 will probably complain about using getopt and a config dict instead of optparse but it works and it wasn't worth it to me to port it to a different mechanism. Anybody which wants to is more than welcome.
-- John Dennis <jden...@redhat.com> Looking to carve out IT costs? www.redhat.com/carveoutcosts/
#!/usr/bin/python import getopt import os import errno import sys import subprocess import re import smtplib import email import traceback from cStringIO import StringIO from email.generator import Generator from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart from email.mime.base import MIMEBase #------------------------------------------------------------------------------- prog_name = os.path.basename(sys.argv[0]) config = { 'project_name' : 'freeipa', 'user_name' : 'USERNAME', 'patch_dir' : '/home/USERNAME/freeipa-patches', 'smtp_server' : 'SMTP_SERVER', 'email_from_addr' : 'FULLNAME <USERNAME@USER_DOMAIN>', 'email_to_addrs' : ['freeipa-devel@redhat.com'], 'start_number_basename' : 'StartNumber', 'default_number' : 1, 'number' : None, 'send' : False, 'run_format' : True, 'dry_run' : False, 'verbose' : False, 'revision_range' : '-1', } signature = ''' -- FULLNAME <USERNAME@USER_DOMAIN> ''' #------------------------------------------------------------------------------- git_filename_re = re.compile(r'^(\d+)-(.*).patch$') ipa_filename_re = re.compile(r'^([^-]+)-([^-]+)-(\d+)(-(\d+))?-(.*)\.patch$') patch_subject_re = re.compile(r'Subject:\s+\[PATCH\s+(\d+)(-(\d+))?\]') #------------------------------------------------------------------------------- class CommandError(Exception): def __init__(self, cmd, msg): self.cmd = cmd self.msg = msg def __str__(self): return "COMMAND ERROR: cmd='%s'\n%s" % (self.cmd, self.msg) #------------------------------------------------------------------------------- class PatchInfo: def __init__(self, project, user, number, revision, description, path=None): self.project = project self.user = user self.number = int(number) self.revision = int(revision) self.description = description self.path = path self.extension = 'patch' def __str__(self): extension = 'patch' if self.revision: filename = '%s-%s-%04d-%d-%s.%s' % \ (self.project, self.user, self.number, self.revision, self.description, self.extension) else: filename = '%s-%s-%04d-%s.%s' % \ (self.project, self.user, self.number, self.description, self.extension) return filename def __cmp__(self, other): result = cmp(self.project, other.project) if result != 0: return result result = cmp(self.user, other.user) if result != 0: return result result = cmp(self.number, other.number) if result != 0: return result result = cmp(self.revision, other.revision) if result != 0: return result result = cmp(self.description, other.description) if result != 0: return result return 0 #------------------------------------------------------------------------------- def run_cmd(cmd): p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) status = os.waitpid(p.pid, 0)[1] msg = p.stdout.read().strip() if (status != 0): err_msg = p.stderr.read().strip() raise CommandError(cmd, err_msg) return msg def next_number(): try: f = open(config['start_number_filepath'], 'r') number = int(f.read()) f.close() if config['verbose']: print "number %d read from '%s'" % (number, config['start_number_filepath']) except Exception, e: if e.errno == errno.ENOENT: number = config['default_number'] if config['verbose']: print "'%s' does not exist yet, using default %d" % (config['start_number_filepath'], number) else: raise if not config['dry_run']: f = open(config['start_number_filepath'], 'w') f.write('%d\n' % (number + 1)) f.close() return number def get_patch_filename(number, revision, description): project = config['project_name'] user = config['user_name'] info = PatchInfo(project, user, number, revision, description) return str(info) def find_patch(number, patch_dir): for filename in os.listdir(patch_dir): match = git_filename_re.search(filename) if match: number = int(match.group(1)) description = match.group(2) if patch_number == number: patch_filename = filename return os.path.join(patch_dir, patch_filename) return None def rename_git_patchfile(git_patchfile, number, revision): directory = os.path.dirname(git_patchfile) old_basename = os.path.basename(git_patchfile) match = git_filename_re.search(old_basename) if match: git_number = int(match.group(1)) description = match.group(2) new_basename = get_patch_filename(number, revision, description) old_path = git_patchfile new_path = os.path.join(directory, new_basename) os.rename(old_path, new_path) else: raise ValueError("git_patchfile cannot be parsed (%s)" % (git_patchfile)) return new_path def parse_patch_filename(patch_filename): match = ipa_filename_re.search(patch_filename) if match is None: return None project = match.group(1) user = match.group(2) number = int(match.group(3)) if match.group(4) is not None: revision = int(match.group(5)) else: revision = 0 description = match.group(6) return PatchInfo(project, user, number, revision, description, patch_filename) def get_patch_revisions(number, patch_dir): patches = [] for filename in os.listdir(patch_dir): info = parse_patch_filename(filename) if info is not None: if number == info.number: patches.append(info) if len(patches) == 0: return None patches.sort() revisions = [x.revision for x in patches] return revisions def send_patch(filename): f = open(filename) patch = email.email.message_from_file(f) f.close() patch_name = os.path.basename(filename) # Get the entire raw message, including headers if False: f = StringIO() g = Generator(f, mangle_from_=False, maxheaderlen=0) g.flatten(patch) raw_msg = f.getvalue() else: f = open(filename) raw_msg = f.read() f.close() payload = patch.get_payload() i = payload.find('\n---\n') if i == -1: commit_msg = '' else: commit_msg = payload[:i] msg = MIMEMultipart() mime_part = MIMEText(commit_msg + '\n' + signature) msg.attach(mime_part) mime_part = MIMEBase('text', 'x-patch', name=patch_name) mime_part.set_charset('utf-8') mime_part.add_header('Content-Disposition', 'attachment', filename=patch_name) mime_part.set_payload(raw_msg) email.encoders.encode_base64(mime_part) msg.attach(mime_part) msg['Subject'] = patch['subject'] msg['From'] = config['email_from_addr'] msg['To'] = ', '.join(config['email_to_addrs']) if config['dry_run']: print msg else: s = smtplib.SMTP(config['smtp_server']) s.sendmail(config['email_from_addr'], config['email_to_addrs'], msg.as_string()) s.quit() def usage(): ''' Print command help. ''' print '''\ -h --help print help -n --number nn use this number for patch instead of next sequence number -r --revision-range use this revision range instead of default -1 -s --send send patch using git send-email -N --dry-run don't execute, just report what would have been done -F --no-format don't run git format-patch Examples: %(prog_name)s ''' % {'prog_name' : prog_name, } #------------------------------------------------------------------------------- def main(): try: opts, args = getopt.getopt(sys.argv[1:], 'hn:r:sNF', ['help', 'number=', 'revision-range=', 'send', 'dry-run', 'no-format']) except getopt.GetoptError, err: print >>sys.stderr, str(err) usage() sys.exit(2) for o, a in opts: if o in ('-h', '--help'): usage() sys.exit() elif o in ('-n', '--number'): config['number'] = int(a) elif o in ('-r', '--revision-range'): config['revision_range'] = a elif o in ('-s', '--send'): config['send'] = True elif o in ('-N', '--dry-run'): config['dry_run'] = True elif o in ('-F', '--no-format'): config['run_format'] = False else: assert False, 'unhandled option' config['start_number_filepath'] = os.path.join(config['patch_dir'], config['start_number_basename']) try: if config['dry_run']: patch_dir = '.' else: patch_dir = config['patch_dir'] revision = 0 if config['number'] is None: number = next_number() else: number = config['number'] revisions = get_patch_revisions(number, patch_dir) if revisions is not None: revision = revisions[-1] + 1 if config['run_format']: if len(args) > 0: extra_args = ' '.join(args) else: extra_args = config['revision_range'] if revision: subject_number = '%d-%d' % (number, revision) else: subject_number = number cmd = 'git format-patch --start-number %d --subject-prefix "PATCH %s" -N -o %s' % \ (number, subject_number, patch_dir) cmd += ' ' + extra_args git_patchfile = run_cmd(cmd) patch_file = rename_git_patchfile(git_patchfile, number, revision) print patch_file if config['send']: send_patch(patch_file) return 0 except Exception, e: print >>sys.stderr, e traceback.print_exc() return 1 #------------------------------------------------------------------------------- if __name__ == '__main__': main()
_______________________________________________ Freeipa-devel mailing list Freeipa-devel@redhat.com https://www.redhat.com/mailman/listinfo/freeipa-devel