def initialise(cls, repository, name=None, switch_to=False): """Initialise a Git branch to handle patch series. @param repository: The L{Repository} where the L{Stack} will be created @param name: The name of the L{Stack} """ if not name: name = repository.current_branch_name # make sure that the corresponding Git branch exists branch = Branch(repository, name) dir = os.path.join(repository.directory, cls._repo_subdir, name) if os.path.exists(dir): raise StackException('%s: branch already initialized' % name) if switch_to: branch.switch_to() # create the stack directory and files utils.create_dirs(dir) compat_dir = os.path.join(dir, 'patches') utils.create_dirs(compat_dir) PatchOrder.create(dir) config.set(stackupgrade.format_version_key(name), text(stackupgrade.FORMAT_VERSION)) return repository.get_stack(name)
def __decode_header(header): """Decode a qp-encoded e-mail header as per rfc2047""" try: decoded_words = email.header.decode_header(header) return text(email.header.make_header(decoded_words)) except Exception as ex: raise CmdException('header decoding error: %s' % str(ex))
def get_commands(allow_cached=True): """Return list of tuples of command name, module name, command type, and one-line command help.""" if allow_cached: try: from stgit.commands.cmdlist import command_list return command_list except ImportError: # cmdlist.py doesn't exist, so do it the expensive way. pass return sorted(( text(getattr(mod, 'name', mod_name)), text(mod_name), _kinds[mod.kind], mod.help, ) for mod_name, mod in _find_commands())
def __decode_header(header): """Decode a qp-encoded e-mail header as per rfc2047""" try: words_enc = decode_header(header) hobj = make_header(words_enc) except Exception as ex: raise CmdException('header decoding error: %s' % str(ex)) return text(hobj)
def __decode_header(header): """Decode a qp-encoded e-mail header as per rfc2047""" try: words_enc = decode_header(header) hobj = make_header(words_enc) except Exception as ex: raise CmdException('header decoding error: %s' % str(ex)) return text(hobj)
def env(self): env = {} for p, v1 in ((self.author, 'AUTHOR'), (self.committer, 'COMMITTER')): if p is not None: for attr, v2 in (('name', 'NAME'), ('email', 'EMAIL'), ('date', 'DATE')): if getattr(p, attr) is not None: env['GIT_%s_%s' % (v1, v2)] = text(getattr(p, attr)) return env
def _find_commands(): for p in __path__: for fn in os.listdir(p): if not fn.endswith('.py'): continue mod = text(strip_suffix('.py', fn)) m = get_command(mod) if not hasattr(m, 'usage'): continue yield mod, m
def __get_sender(): """Return the 'authname <authemail>' string as read from the configuration file """ sender = config.get('stgit.sender') if not sender: try: sender = text(git.user()) except git.GitException: try: sender = text(git.author()) except git.GitException: pass if not sender: raise CmdException('Unknown sender name and e-mail; you should for ' 'example set git config user.name and user.email') sender = email.utils.parseaddr(sender) return email.utils.formataddr(address_or_alias(sender))
def _find_commands(): for p in __path__: for fn in os.listdir(p): if not fn.endswith('.py'): continue mod = text(strip_suffix('.py', fn)) m = get_command(mod) if not hasattr(m, 'usage'): continue yield mod, m
def get_commands(allow_cached=True): """Return a map from command name to a tuple of module name, command type, and one-line command help.""" if allow_cached: try: from stgit.commands.cmdlist import command_list return command_list except ImportError: # cmdlist.py doesn't exist, so do it the expensive way. pass return dict((text(getattr(m, 'name', mod)), (mod, _kinds[m.kind], m.help)) for mod, m in _find_commands())
def get_commands(allow_cached = True): """Return a map from command name to a tuple of module name, command type, and one-line command help.""" if allow_cached: try: from stgit.commands.cmdlist import command_list return command_list except ImportError: # cmdlist.py doesn't exist, so do it the expensive way. pass return dict((text(getattr(m, 'name', mod)), (mod, _kinds[m.kind], m.help)) for mod, m in _find_commands())
def init(self, create_at=False, parent_remote=None, parent_branch=None): """Initialises the stgit series """ if self.is_initialised(): raise StackException('%s already initialized' % self.get_name()) for d in [self._dir()]: if os.path.exists(d): raise StackException('%s already exists' % d) if (create_at!=False): git.create_branch(self.get_name(), create_at) os.makedirs(self.__patch_dir) self.set_parent(parent_remote, parent_branch) self.create_empty_field('applied') self.create_empty_field('unapplied') config.set(stackupgrade.format_version_key(self.get_name()), text(stackupgrade.FORMAT_VERSION))
def init(self, create_at=False, parent_remote=None, parent_branch=None): """Initialises the stgit series """ if self.is_initialised(): raise StackException('%s already initialized' % self.get_name()) for d in [self._dir()]: if os.path.exists(d): raise StackException('%s already exists' % d) if create_at is not False: git.create_branch(self.get_name(), create_at) os.makedirs(self.__patch_dir) self.set_parent(parent_remote, parent_branch) self.create_empty_field('applied') self.create_empty_field('unapplied') config.set(stackupgrade.format_version_key(self.get_name()), text(stackupgrade.FORMAT_VERSION))
def initialise(cls, repository, name = None): """Initialise a Git branch to handle patch series. @param repository: The L{Repository} where the L{Stack} will be created @param name: The name of the L{Stack} """ if not name: name = repository.current_branch_name # make sure that the corresponding Git branch exists git.Branch(repository, name) dir = os.path.join(repository.directory, cls.__repo_subdir, name) compat_dir = os.path.join(dir, 'patches') if os.path.exists(dir): raise StackException('%s: branch already initialized' % name) # create the stack directory and files utils.create_dirs(dir) utils.create_dirs(compat_dir) PatchOrder.create(dir) config.set(stackupgrade.format_version_key(name), text(stackupgrade.FORMAT_VERSION)) return repository.get_stack(name)
def __build_message(tmpl, msg_id, options, patch, patch_nr, total_nr, ref_id): """Build the message to be sent via SMTP """ repository = directory.repository stack = repository.current_stack p = stack.patches.get(patch) if p.commit.data.message: descr = p.commit.data.message.strip() else: # provide a place holder and force the edit message option on descr = '<empty message>' options.edit_patches = True descr_lines = descr.split('\n') short_descr = descr_lines[0].strip() long_descr = '\n'.join(l.rstrip() for l in descr_lines[1:]).lstrip('\n') author = p.commit.data.author committer = p.commit.data.committer sender = __get_sender() if author.name_email != sender: fromauth = 'From: %s\n\n' % author.name_email else: fromauth = '' if options.version: version_str = '%s' % options.version version_space = ' ' else: version_str = '' version_space = '' if options.prefix: prefix_str = options.prefix else: prefix_str = config.get('stgit.mail.prefix') if prefix_str: prefix_space = ' ' else: prefix_str = '' prefix_space = '' total_nr_str = text(total_nr) patch_nr_str = text(patch_nr).zfill(len(total_nr_str)) if not options.unrelated and total_nr > 1: number_str = '%s/%s' % (patch_nr_str, total_nr_str) number_space = ' ' else: number_str = '' number_space = '' diff = repository.diff_tree( p.commit.data.parent.data.tree, p.commit.data.tree, diff_opts=options.diff_flags, ) tmpl_dict = { 'patch': patch, 'sender': sender, 'maintainer': sender, # for backward template compatibility 'shortdescr': short_descr, 'longdescr': long_descr, 'endofheaders': '', # for backward template compatibility 'diff': diff, 'diffstat': diffstat(diff), 'date': '', # for backward template compatibility 'version': version_str, 'vspace': version_space, 'prefix': prefix_str, 'pspace': prefix_space, 'patchnr': patch_nr_str, 'totalnr': total_nr_str, 'number': number_str, 'nspace': number_space, 'snumber': number_str.strip(), 'fromauth': fromauth, 'authname': author.name, 'authemail': author.email, 'authdate': author.date.rfc2822_format(), 'commname': committer.name, 'commemail': committer.email, } try: msg_bytes = templates.specialize_template(tmpl, tmpl_dict) except KeyError as err: raise CmdException('Unknown patch template variable: %s' % err) except TypeError: raise CmdException('Only "%(name)s" variables are ' 'supported in the patch template') if options.edit_patches: msg_bytes = edit_bytes(msg_bytes, '.stgitmail.txt') # The Python email message try: msg = message_from_bytes(msg_bytes) except Exception as ex: raise CmdException('template parsing error: %s' % str(ex)) if options.auto: extra_cc = __get_signers_list(descr) else: extra_cc = [] if not options.git: __build_address_headers(msg, options, extra_cc) __build_extra_headers(msg, msg_id, ref_id) __encode_message(msg) return msg
def __build_cover(tmpl, msg_id, options, patches): """Build the cover message (series description) to be sent via SMTP """ sender = __get_sender() if options.version: version_str = '%s' % options.version version_space = ' ' else: version_str = '' version_space = '' if options.prefix: prefix_str = options.prefix else: prefix_str = config.get('stgit.mail.prefix') if prefix_str: prefix_space = ' ' else: prefix_str = '' prefix_space = '' total_nr_str = text(len(patches)) patch_nr_str = '0'.zfill(len(total_nr_str)) if len(patches) > 1: number_str = '%s/%s' % (patch_nr_str, total_nr_str) number_space = ' ' else: number_str = '' number_space = '' repository = directory.repository stack = repository.current_stack tmpl_dict = { 'sender': sender, # for backward template compatibility 'maintainer': sender, # for backward template compatibility 'endofheaders': '', # for backward template compatibility 'date': '', 'version': version_str, 'vspace': version_space, 'prefix': prefix_str, 'pspace': prefix_space, 'patchnr': patch_nr_str, 'totalnr': total_nr_str, 'number': number_str, 'nspace': number_space, 'snumber': number_str.strip(), 'shortlog': __shortlog(stack, patches), 'diffstat': repository.diff_tree( stack.base.data.tree, stack.top.data.tree, diff_opts=options.diff_flags, stat=True, ), } try: msg_bytes = templates.specialize_template(tmpl, tmpl_dict) except KeyError as err: raise CmdException('Unknown patch template variable: %s' % err) except TypeError: raise CmdException('Only "%(name)s" variables are ' 'supported in the patch template') if options.edit_cover: msg_bytes = edit_bytes(msg_bytes, '.stgitmail.txt') # The Python email message try: msg = message_from_bytes(msg_bytes) except Exception as ex: raise CmdException('template parsing error: %s' % str(ex)) extra_cc = [] if options.auto: for pn in patches: p = stack.patches.get(pn) if p.commit.data.message: descr = p.commit.data.message.strip() extra_cc.extend(__get_signers_list(descr)) extra_cc = list(set(extra_cc)) if not options.git: __build_address_headers(msg, options, extra_cc) __build_extra_headers(msg, msg_id, options.in_reply_to) __encode_message(msg) return msg
def __build_message(tmpl, msg_id, options, patch, patch_nr, total_nr, ref_id): """Build the message to be sent via SMTP """ p = crt_series.get_patch(patch) if p.get_description(): descr = p.get_description().strip() else: # provide a place holder and force the edit message option on descr = '<empty message>' options.edit_patches = True descr_lines = descr.split('\n') short_descr = descr_lines[0].strip() long_descr = '\n'.join(l.rstrip() for l in descr_lines[1:]).lstrip('\n') authname = p.get_authname() authemail = p.get_authemail() commname = p.get_commname() commemail = p.get_commemail() sender = __get_sender() fromauth = '%s <%s>' % (authname, authemail) if fromauth != sender: fromauth = 'From: %s\n\n' % fromauth else: fromauth = '' if options.version: version_str = '%s' % options.version version_space = ' ' else: version_str = '' version_space = '' if options.prefix: prefix_str = options.prefix else: prefix_str = config.get('stgit.mail.prefix') if prefix_str: prefix_space = ' ' else: prefix_str = '' prefix_space = '' total_nr_str = text(total_nr) patch_nr_str = text(patch_nr).zfill(len(total_nr_str)) if not options.unrelated and total_nr > 1: number_str = '%s/%s' % (patch_nr_str, total_nr_str) number_space = ' ' else: number_str = '' number_space = '' diff = git.diff(rev1=git_id(crt_series, '%s^' % patch), rev2=git_id(crt_series, '%s' % patch), diff_flags=options.diff_flags) tmpl_dict = { 'patch': patch, 'sender': sender, # for backward template compatibility 'maintainer': sender, 'shortdescr': short_descr, 'longdescr': long_descr, # for backward template compatibility 'endofheaders': '', 'diff': diff, 'diffstat': gitlib.diffstat(diff), # for backward template compatibility 'date': '', 'version': version_str, 'vspace': version_space, 'prefix': prefix_str, 'pspace': prefix_space, 'patchnr': patch_nr_str, 'totalnr': total_nr_str, 'number': number_str, 'nspace': number_space, 'snumber': number_str.strip(), 'fromauth': fromauth, 'authname': authname, 'authemail': authemail, 'authdate': p.get_authdate(), 'commname': commname, 'commemail': commemail } # change None to '' for key in tmpl_dict: if not tmpl_dict[key]: tmpl_dict[key] = '' try: msg_string = tmpl % tmpl_dict except KeyError as err: raise CmdException('Unknown patch template variable: %s' % err) except TypeError: raise CmdException('Only "%(name)s" variables are ' 'supported in the patch template') if options.edit_patches: msg_string = __edit_message(msg_string) # The Python email message try: msg = email.message_from_string(msg_string) except Exception as ex: raise CmdException('template parsing error: %s' % str(ex)) if options.auto: extra_cc = __get_signers_list(descr) else: extra_cc = [] if not options.git: __build_address_headers(msg, options, extra_cc) __build_extra_headers(msg, msg_id, ref_id) __encode_message(msg) return msg
def __build_cover(tmpl, msg_id, options, patches): """Build the cover message (series description) to be sent via SMTP """ sender = __get_sender() if options.version: version_str = '%s' % options.version version_space = ' ' else: version_str = '' version_space = '' if options.prefix: prefix_str = options.prefix else: prefix_str = config.get('stgit.mail.prefix') if prefix_str: prefix_space = ' ' else: prefix_str = '' prefix_space = '' total_nr_str = text(len(patches)) patch_nr_str = '0'.zfill(len(total_nr_str)) if len(patches) > 1: number_str = '%s/%s' % (patch_nr_str, total_nr_str) number_space = ' ' else: number_str = '' number_space = '' tmpl_dict = { 'sender': sender, # for backward template compatibility 'maintainer': sender, # for backward template compatibility 'endofheaders': '', # for backward template compatibility 'date': '', 'version': version_str, 'vspace': version_space, 'prefix': prefix_str, 'pspace': prefix_space, 'patchnr': patch_nr_str, 'totalnr': total_nr_str, 'number': number_str, 'nspace': number_space, 'snumber': number_str.strip(), 'shortlog': stack.shortlog(crt_series.get_patch(p) for p in reversed(patches)), 'diffstat': gitlib.diffstat( git.diff( rev1=git_id(crt_series, '%s^' % patches[0]), rev2=git_id(crt_series, '%s' % patches[-1]), diff_flags=options.diff_flags, )), } try: msg_bytes = templates.specialize_template(tmpl, tmpl_dict) except KeyError as err: raise CmdException('Unknown patch template variable: %s' % err) except TypeError: raise CmdException('Only "%(name)s" variables are ' 'supported in the patch template') if options.edit_cover: msg_bytes = edit_bytes(msg_bytes, '.stgitmail.txt') # The Python email message try: msg = message_from_bytes(msg_bytes) except Exception as ex: raise CmdException('template parsing error: %s' % str(ex)) extra_cc = [] if options.auto: for patch in patches: p = crt_series.get_patch(patch) if p.get_description(): descr = p.get_description().strip() extra_cc.extend(__get_signers_list(descr)) extra_cc = list(set(extra_cc)) if not options.git: __build_address_headers(msg, options, extra_cc) __build_extra_headers(msg, msg_id, options.in_reply_to) __encode_message(msg) return msg
def __create_patch(filename, message, author_name, author_email, author_date, diff, options): """Create a new patch on the stack """ if options.name: patch = options.name elif filename: patch = os.path.basename(filename) else: patch = '' if options.stripname: patch = __strip_patch_name(patch) if not patch: if options.ignore or options.replace: def unacceptable_name(name): return False else: unacceptable_name = crt_series.patch_exists patch = make_patch_name(message, unacceptable_name) else: # fix possible invalid characters in the patch name patch = re.sub(r'[^\w.]+', '-', patch).strip('-') if options.ignore and patch in crt_series.get_applied(): out.info('Ignoring already applied patch "%s"' % patch) return if options.replace and patch in crt_series.get_unapplied(): crt_series.delete_patch(patch, keep_log=True) # override the automatically parsed settings author = options.author(Person()) if author.name: author_name = author.name if author.email: author_email = author.email if author.date: author_date = text(author.date) sign_str = options.sign_str if not options.sign_str: sign_str = config.get('stgit.autosign') crt_series.new_patch( patch, message=message, can_edit=False, author_name=author_name, author_email=author_email, author_date=author_date, sign_str=sign_str, ) if not diff: out.warn('No diff found, creating empty patch') else: out.start('Importing patch "%s"' % patch) if options.base: base = git_id(crt_series, options.base) else: base = None try: git.apply_patch( diff=diff, base=base, reject=options.reject, strip=options.strip, ) except git.GitException: if not options.reject: crt_series.delete_patch(patch) raise crt_series.refresh_patch( edit=options.edit, show_patch=options.showdiff, author_date=author_date, backup=False, ) out.done()
def write_string(filename, line, multiline=False, encoding='utf-8'): """Writes 'line' to file and truncates it """ with mkdir_file(filename, 'w+', encoding) as f: line = text(line) print(line, end='' if multiline else '\n', file=f)
def __build_message(tmpl, msg_id, options, patch, patch_nr, total_nr, ref_id): """Build the message to be sent via SMTP """ p = crt_series.get_patch(patch) if p.get_description(): descr = p.get_description().strip() else: # provide a place holder and force the edit message option on descr = '<empty message>' options.edit_patches = True descr_lines = descr.split('\n') short_descr = descr_lines[0].strip() long_descr = '\n'.join(l.rstrip() for l in descr_lines[1:]).lstrip('\n') authname = p.get_authname() authemail = p.get_authemail() commname = p.get_commname() commemail = p.get_commemail() sender = __get_sender() fromauth = '%s <%s>' % (authname, authemail) if fromauth != sender: fromauth = 'From: %s\n\n' % fromauth else: fromauth = '' if options.version: version_str = '%s' % options.version version_space = ' ' else: version_str = '' version_space = '' if options.prefix: prefix_str = options.prefix else: prefix_str = config.get('stgit.mail.prefix') if prefix_str: prefix_space = ' ' else: prefix_str = '' prefix_space = '' total_nr_str = text(total_nr) patch_nr_str = text(patch_nr).zfill(len(total_nr_str)) if not options.unrelated and total_nr > 1: number_str = '%s/%s' % (patch_nr_str, total_nr_str) number_space = ' ' else: number_str = '' number_space = '' diff = git.diff(rev1 = git_id(crt_series, '%s^' % patch), rev2 = git_id(crt_series, '%s' % patch), diff_flags = options.diff_flags) tmpl_dict = {'patch': patch, 'sender': sender, # for backward template compatibility 'maintainer': sender, 'shortdescr': short_descr, 'longdescr': long_descr, # for backward template compatibility 'endofheaders': '', 'diff': diff, 'diffstat': gitlib.diffstat(diff), # for backward template compatibility 'date': '', 'version': version_str, 'vspace': version_space, 'prefix': prefix_str, 'pspace': prefix_space, 'patchnr': patch_nr_str, 'totalnr': total_nr_str, 'number': number_str, 'nspace': number_space, 'snumber': number_str.strip(), 'fromauth': fromauth, 'authname': authname, 'authemail': authemail, 'authdate': p.get_authdate(), 'commname': commname, 'commemail': commemail} # change None to '' for key in tmpl_dict: if not tmpl_dict[key]: tmpl_dict[key] = '' try: msg_string = tmpl % tmpl_dict except KeyError as err: raise CmdException('Unknown patch template variable: %s' % err) except TypeError: raise CmdException('Only "%(name)s" variables are ' 'supported in the patch template') if options.edit_patches: msg_string = __edit_message(msg_string) # The Python email message try: msg = email.message_from_string(msg_string) except Exception as ex: raise CmdException('template parsing error: %s' % str(ex)) if options.auto: extra_cc = __get_signers_list(descr) else: extra_cc = [] if not options.git: __build_address_headers(msg, options, extra_cc) __build_extra_headers(msg, msg_id, ref_id) __encode_message(msg) return msg
def write_string(filename, line, multiline=False, encoding='utf-8'): """Writes 'line' to file and truncates it """ with mkdir_file(filename, 'w+', encoding) as f: line = text(line) print(line, end='' if multiline else '\n', file=f)
def __build_cover(tmpl, msg_id, options, patches): """Build the cover message (series description) to be sent via SMTP """ sender = __get_sender() if options.version: version_str = '%s' % options.version version_space = ' ' else: version_str = '' version_space = '' if options.prefix: prefix_str = options.prefix else: prefix_str = config.get('stgit.mail.prefix') if prefix_str: prefix_space = ' ' else: prefix_str = '' prefix_space = '' total_nr_str = text(len(patches)) patch_nr_str = '0'.zfill(len(total_nr_str)) if len(patches) > 1: number_str = '%s/%s' % (patch_nr_str, total_nr_str) number_space = ' ' else: number_str = '' number_space = '' tmpl_dict = {'sender': sender, # for backward template compatibility 'maintainer': sender, # for backward template compatibility 'endofheaders': '', # for backward template compatibility 'date': '', 'version': version_str, 'vspace': version_space, 'prefix': prefix_str, 'pspace': prefix_space, 'patchnr': patch_nr_str, 'totalnr': total_nr_str, 'number': number_str, 'nspace': number_space, 'snumber': number_str.strip(), 'shortlog': stack.shortlog(crt_series.get_patch(p) for p in reversed(patches)), 'diffstat': gitlib.diffstat(git.diff( rev1 = git_id(crt_series, '%s^' % patches[0]), rev2 = git_id(crt_series, '%s' % patches[-1]), diff_flags = options.diff_flags))} try: msg_string = tmpl % tmpl_dict except KeyError as err: raise CmdException('Unknown patch template variable: %s' % err) except TypeError: raise CmdException('Only "%(name)s" variables are ' 'supported in the patch template') if options.edit_cover: msg_string = __edit_message(msg_string) # The Python email message try: msg = email.message_from_string(msg_string) except Exception as ex: raise CmdException('template parsing error: %s' % str(ex)) extra_cc = [] if options.auto: for patch in patches: p = crt_series.get_patch(patch) if p.get_description(): descr = p.get_description().strip() extra_cc.extend(__get_signers_list(descr)) extra_cc = list(set(extra_cc)) if not options.git: __build_address_headers(msg, options, extra_cc) __build_extra_headers(msg, msg_id, options.in_reply_to) __encode_message(msg) return msg