Exemplo n.º 1
0
def __send_message(msg_type, tmpl, options, *args):
    """Message sending dispatcher.
    """
    domain = options.domain or config.get('stgit.domain')

    if domain:
        if sys.version_info < (3, 2):
            raise CmdException("Setting domain requires Python version 3.2+")
        msg_id = email.utils.make_msgid('stgit', domain=domain)
    else:
        msg_id = email.utils.make_msgid('stgit')

    if msg_type == 'cover':
        assert len(args) == 1, 'too many args for msg_type == "cover"'
        patches = args[0]
        msg = __build_cover(tmpl, msg_id, options, patches)
        outstr = 'the cover message'
    elif msg_type == 'patch':
        patch, patch_nr, total_nr, ref_id = args
        msg = __build_message(tmpl, msg_id, options, patch, patch_nr, total_nr,
                              ref_id)
        outstr = 'patch "%s"' % patch
    else:
        raise AssertionError('invalid msg_type: %s' %
                             msg_type)  # pragma: no cover

    if hasattr(msg, 'as_bytes'):
        msg_bytes = msg.as_bytes(options.mbox)
    else:
        msg_bytes = msg.as_string(options.mbox)
        # Python 3.3 only has Message.as_string(). We encode it back to bytes
        # and hope for the best.
        if isinstance(msg_bytes, text):
            msg_bytes = msg_bytes.encode('utf-8')
    if options.mbox:
        out.stdout_bytes(msg_bytes + b'\n')
        return msg_id

    if not options.git:
        from_addr, to_addrs = __parse_addresses(msg)
        out.start('Sending ' + outstr)

    smtpserver = options.smtp_server or config.get('stgit.smtpserver')
    if options.git:
        __send_message_git(msg_bytes, msg['From'], options)
    elif smtpserver.startswith('/'):
        # Use the sendmail tool
        __send_message_sendmail(smtpserver, msg_bytes)
    else:
        # Use the SMTP server (we have host and port information)
        __send_message_smtp(smtpserver, from_addr, to_addrs, msg_bytes,
                            options)

    # give recipients a chance of receiving related patches in correct order
    if msg_type == 'cover' or (msg_type == 'patch' and patch_nr < total_nr):
        sleep = options.sleep or config.getint('stgit.smtpdelay')
        time.sleep(sleep)
    if not options.git:
        out.done()
    return msg_id
Exemplo n.º 2
0
 def get_format_version():
     """Return the integer format version number, or None if the
     branch doesn't have any StGit metadata at all, of any version."""
     fv = config.get(key)
     ofv = config.get(old_key)
     if fv:
         # Great, there's an explicitly recorded format version
         # number, which means that the branch is initialized and
         # of that exact version.
         return int(fv)
     elif ofv:
         # Old name for the version info: upgrade it.
         config.set(key, ofv)
         config.unset(old_key)
         return int(ofv)
     elif os.path.isdir(os.path.join(branch_dir, 'patches')):
         # There's a .git/patches/<branch>/patches dirctory, which
         # means this is an initialized version 1 branch.
         return 1
     elif os.path.isdir(branch_dir):
         # There's a .git/patches/<branch> directory, which means
         # this is an initialized version 0 branch.
         return 0
     else:
         # The branch doesn't seem to be initialized at all.
         return None
Exemplo n.º 3
0
def fetch(repository, remote_repository):
    """Fetch changes from remote repository using 'git fetch' by default"""
    command = (config.get(
        'branch.%s.stgit.fetchcmd' % repository.current_branch_name)
               or config.get('stgit.fetchcmd')).split()
    command.append(remote_repository)
    repository.run(command).run()
Exemplo n.º 4
0
 def get_format_version():
     """Return the integer format version number, or None if the
     branch doesn't have any StGIT metadata at all, of any version."""
     fv = config.get(self.format_version_key())
     ofv = config.get('branch.%s.stgitformatversion' % self.get_name())
     if fv:
         # Great, there's an explicitly recorded format version
         # number, which means that the branch is initialized and
         # of that exact version.
         return int(fv)
     elif ofv:
         # Old name for the version info, upgrade it
         config.set(self.format_version_key(), ofv)
         config.unset('branch.%s.stgitformatversion' % self.get_name())
         return int(ofv)
     elif os.path.isdir(os.path.join(branch_dir, 'patches')):
         # There's a .git/patches/<branch>/patches dirctory, which
         # means this is an initialized version 1 branch.
         return 1
     elif os.path.isdir(branch_dir):
         # There's a .git/patches/<branch> directory, which means
         # this is an initialized version 0 branch.
         return 0
     else:
         # The branch doesn't seem to be initialized at all.
         return None
Exemplo n.º 5
0
def pull(repository, remote_repository):
    """Pull changes from remote repository using 'git pull' by default."""
    command = (config.get(
        'branch.%s.stgit.pullcmd' % repository.current_branch_name)
               or config.get('stgit.pullcmd')).split()
    command.append(remote_repository)
    repository.run(command).run()
    repository.refs.reset_cache()
Exemplo n.º 6
0
Arquivo: utils.py Projeto: snits/stgit
def get_editor():
    for editor in [os.environ.get('GIT_EDITOR'),
                   config.get('stgit.editor'), # legacy
                   config.get('core.editor'),
                   os.environ.get('VISUAL'),
                   os.environ.get('EDITOR'),
                   'vi']:
        if editor:
            return editor
Exemplo n.º 7
0
def user():
    """Return the user information.
    """
    global __user
    if not __user:
        name = config.get('user.name')
        email = config.get('user.email')
        __user = Person(name, email)
    return __user
Exemplo n.º 8
0
    def clone(self, target_series):
        """Clones a series
        """
        try:
            # allow cloning of branches not under StGIT control
            base = self.get_base()
        except:
            base = git.get_head()
        Series(target_series).init(create_at=base)
        new_series = Series(target_series)

        # generate an artificial description file
        new_series.set_description('clone of "%s"' % self.get_name())

        # clone self's entire series as unapplied patches
        try:
            # allow cloning of branches not under StGIT control
            applied = self.get_applied()
            unapplied = self.get_unapplied()
            patches = applied + unapplied
            patches.reverse()
        except:
            patches = applied = unapplied = []
        for p in patches:
            patch = self.get_patch(p)
            newpatch = new_series.new_patch(
                p,
                message=patch.get_description(),
                can_edit=False,
                unapplied=True,
                bottom=patch.get_bottom(),
                top=patch.get_top(),
                author_name=patch.get_authname(),
                author_email=patch.get_authemail(),
                author_date=patch.get_authdate(),
            )
            if patch.get_log():
                out.info("Setting log to %s" % patch.get_log())
                newpatch.set_log(patch.get_log())
            else:
                out.info("No log for %s" % p)

        # fast forward the cloned series to self's top
        new_series.forward_patches(applied)

        # Clone parent informations
        value = config.get("branch.%s.remote" % self.get_name())
        if value:
            config.set("branch.%s.remote" % target_series, value)

        value = config.get("branch.%s.merge" % self.get_name())
        if value:
            config.set("branch.%s.merge" % target_series, value)

        value = config.get("branch.%s.stgit.parentbranch" % self.get_name())
        if value:
            config.set("branch.%s.stgit.parentbranch" % target_series, value)
Exemplo n.º 9
0
    def clone(self, target_series):
        """Clones a series
        """
        try:
            # allow cloning of branches not under StGIT control
            base = self.get_base()
        except BaseException:
            base = git.get_head()
        Series(target_series).init(create_at=base)
        new_series = Series(target_series)

        # generate an artificial description file
        new_series.set_description('clone of "%s"' % self.get_name())

        # clone self's entire series as unapplied patches
        try:
            # allow cloning of branches not under StGIT control
            applied = self.get_applied()
            unapplied = self.get_unapplied()
            patches = applied + unapplied
            patches.reverse()
        except BaseException:
            patches = applied = unapplied = []
        for p in patches:
            patch = self.get_patch(p)
            newpatch = new_series.new_patch(
                p,
                message=patch.get_description(),
                can_edit=False,
                unapplied=True,
                bottom=patch.get_bottom(),
                top=patch.get_top(),
                author_name=patch.get_authname(),
                author_email=patch.get_authemail(),
                author_date=patch.get_authdate(),
            )
            if patch.get_log():
                out.info('Setting log to %s' % patch.get_log())
                newpatch.set_log(patch.get_log())
            else:
                out.info('No log for %s' % p)

        # fast forward the cloned series to self's top
        new_series.forward_patches(applied)

        # Clone parent informations
        value = config.get('branch.%s.remote' % self.get_name())
        if value:
            config.set('branch.%s.remote' % target_series, value)

        value = config.get('branch.%s.merge' % self.get_name())
        if value:
            config.set('branch.%s.merge' % target_series, value)

        value = config.get('branch.%s.stgit.parentbranch' % self.get_name())
        if value:
            config.set('branch.%s.stgit.parentbranch' % target_series, value)
Exemplo n.º 10
0
def user():
    """Return the user information.
    """
    global __user
    if not __user:
        name=config.get('user.name')
        email=config.get('user.email')
        __user = Person(name, email)
    return __user;
Exemplo n.º 11
0
def get_editor():
    for editor in [
        environ_get('GIT_EDITOR'),
        config.get('stgit.editor'),  # legacy
        config.get('core.editor'),
        environ_get('VISUAL'),
        environ_get('EDITOR'),
        'vi',
    ]:
        if editor:
            return editor
Exemplo n.º 12
0
def get_editor():
    for editor in [
        os.environ.get("GIT_EDITOR"),
        config.get("stgit.editor"),  # legacy
        config.get("core.editor"),
        os.environ.get("VISUAL"),
        os.environ.get("EDITOR"),
        "vi",
    ]:
        if editor:
            return editor
Exemplo n.º 13
0
def pull(repository='origin', refspec=None):
    """Fetches changes from the remote repository, using 'git pull'
    by default.
    """
    # we update the HEAD
    __clear_head_cache()

    args = [repository]
    if refspec:
        args.append(refspec)

    command = (config.get('branch.%s.stgit.pullcmd' % get_head_file())
               or config.get('stgit.pullcmd'))
    Run(*(command.split() + args)).run()
Exemplo n.º 14
0
Arquivo: git.py Projeto: guanqun/stgit
def pull(repository = 'origin', refspec = None):
    """Fetches changes from the remote repository, using 'git pull'
    by default.
    """
    # we update the HEAD
    __clear_head_cache()

    args = [repository]
    if refspec:
        args.append(refspec)

    command = config.get('branch.%s.stgit.pullcmd' % get_head_file()) or \
              config.get('stgit.pullcmd')
    Run(*(command.split() + args)).run()
Exemplo n.º 15
0
def __send_message(msg_type, tmpl, options, *args):
    """Message sending dispatcher.
    """
    msg_id = email.utils.make_msgid('stgit',
                                    domain=options.domain
                                    or config.get('stgit.domain'))

    if msg_type == 'cover':
        assert len(args) == 1, 'too many args for msg_type == "cover"'
        patches = args[0]
        msg = __build_cover(tmpl, msg_id, options, patches)
        outstr = 'the cover message'
    elif msg_type == 'patch':
        patch, patch_nr, total_nr, ref_id = args
        msg = __build_message(tmpl, msg_id, options, patch, patch_nr, total_nr,
                              ref_id)
        outstr = 'patch "%s"' % patch
    else:
        raise AssertionError('invalid msg_type: %s' %
                             msg_type)  # pragma: no cover

    msg_bytes = msg.as_bytes(options.mbox)

    if options.mbox:
        out.stdout_bytes(msg_bytes + b'\n')
        return msg_id

    if not options.git:
        from_addr, to_addrs = __parse_addresses(msg)
        out.start('Sending ' + outstr)

    smtpserver = options.smtp_server or config.get('stgit.smtpserver')
    if options.git:
        __send_message_git(msg_bytes, msg['From'], options)
    elif smtpserver.startswith('/'):
        # Use the sendmail tool
        __send_message_sendmail(smtpserver, msg_bytes)
    else:
        # Use the SMTP server (we have host and port information)
        __send_message_smtp(smtpserver, from_addr, to_addrs, msg_bytes,
                            options)

    # give recipients a chance of receiving related patches in correct order
    if msg_type == 'cover' or (msg_type == 'patch' and patch_nr < total_nr):
        sleep = options.sleep or config.getint('stgit.smtpdelay')
        time.sleep(sleep)
    if not options.git:
        out.done()
    return msg_id
Exemplo n.º 16
0
def interactive_merge(filename):
    """Run the interactive merger on the given file. Note that the
    index should not have any conflicts.
    """
    extensions = file_extensions()

    ancestor = filename + extensions['ancestor']
    current = filename + extensions['current']
    patched = filename + extensions['patched']

    if os.path.isfile(ancestor):
        three_way = True
        files_dict = {
            'branch1': current,
            'ancestor': ancestor,
            'branch2': patched,
            'output': filename
        }
        imerger = config.get('stgit.i3merge')
    else:
        three_way = False
        files_dict = {
            'branch1': current,
            'branch2': patched,
            'output': filename
        }
        imerger = config.get('stgit.i2merge')

    if not imerger:
        raise GitMergeException, 'No interactive merge command configured'

    # check whether we have all the files for the merge
    for fn in [filename, current, patched]:
        if not os.path.isfile(fn):
            raise GitMergeException, \
                  'Cannot run the interactive merge: "%s" missing' % fn

    mtime = os.path.getmtime(filename)

    out.info('Trying the interactive %s merge' %
             (three_way and 'three-way' or 'two-way'))

    err = os.system(imerger % files_dict)
    if err != 0:
        raise GitMergeException, 'The interactive merge failed: %d' % err
    if not os.path.isfile(filename):
        raise GitMergeException, 'The "%s" file is missing' % filename
    if mtime == os.path.getmtime(filename):
        raise GitMergeException, 'The "%s" file was not modified' % filename
Exemplo n.º 17
0
def pull(repository='origin', refspec=None):
    """Fetches changes from the remote repository, using 'git-pull'
    by default.
    """
    # we update the HEAD
    __clear_head_cache()

    args = [repository]
    if refspec:
        args.append(refspec)

    command = config.get('branch.%s.stgit.pullcmd' % get_head_file()) or \
              config.get('stgit.pullcmd')
    if __run(command, args) != 0:
        raise GitException, 'Failed "%s %s"' % (command, repository)
Exemplo n.º 18
0
def pull(repository = 'origin', refspec = None):
    """Fetches changes from the remote repository, using 'git-pull'
    by default.
    """
    # we update the HEAD
    __clear_head_cache()

    args = [repository]
    if refspec:
        args.append(refspec)

    command = config.get('branch.%s.stgit.pullcmd' % get_head_file()) or \
              config.get('stgit.pullcmd')
    if __run(command, args) != 0:
        raise GitException, 'Failed "%s %s"' % (command, repository)
Exemplo n.º 19
0
def update_commit_data(cd, options):
    """Return a new CommitData object updated according to the command line
    options."""
    # Set the commit message from commandline.
    if options.message is not None:
        cd = cd.set_message(options.message)

    # Modify author data.
    cd = cd.set_author(options.author(cd.author))

    # Add Signed-off-by: or similar.
    if options.sign_str != None:
        sign_str = options.sign_str
    else:
        sign_str = config.get("stgit.autosign")
    if sign_str != None:
        cd = cd.set_message(
            add_sign_line(cd.message, sign_str,
                          cd.committer.name, cd.committer.email))

    # Let user edit the commit message manually, unless
    # --save-template or --message was specified.
    if not getattr(options, 'save_template', None) and options.message is None:
        cd = cd.set_message(edit_string(cd.message, '.stgit-new.txt'))

    return cd
Exemplo n.º 20
0
def __build_address_headers(msg, options, extra_cc = []):
    """Build the address headers and check existing headers in the
    template.
    """
    def __replace_header(header, addr):
        if addr:
            crt_addr = msg[header]
            del msg[header]

            if crt_addr:
                msg[header] = address_or_alias(', '.join([crt_addr, addr]))
            else:
                msg[header] = address_or_alias(addr)

    to_addr = ''
    cc_addr = ''
    bcc_addr = ''

    autobcc = config.get('stgit.autobcc') or ''

    if options.to:
        to_addr = ', '.join(options.to)
    if options.cc:
        cc_addr = ', '.join(options.cc + extra_cc)
    elif extra_cc:
        cc_addr = ', '.join(extra_cc)
    if options.bcc:
        bcc_addr = ', '.join(options.bcc + [autobcc])
    elif autobcc:
        bcc_addr = autobcc

    __replace_header('To', to_addr)
    __replace_header('Cc', cc_addr)
    __replace_header('Bcc', bcc_addr)
Exemplo n.º 21
0
def __create_branch(branch_name, committish):
    repository = directory.repository

    branch_commit = None
    if committish is not None:
        parentbranch = None
        try:
            branchpoint = repository.run(
                ['git', 'rev-parse', '--symbolic-full-name', committish]
            ).output_one_line()

            if branchpoint.startswith('refs/heads/') or branchpoint.startswith(
                'refs/remotes/'
            ):
                # committish is a valid ref from the branchpoint setting above
                parentbranch = committish

        except RunException:
            out.info(
                'Do not know how to determine parent branch from "%s"' % committish
            )
            # exception in branch = rev_parse() leaves branchpoint unbound
            branchpoint = None

        branch_commit = git_commit(branchpoint or committish, repository)

        if parentbranch:
            out.info('Recording "%s" as parent branch' % parentbranch)
        else:
            out.info(
                'Do not know how to determine parent branch from "%s"' % committish
            )
    else:
        try:
            # branch stack off current branch
            parentbranch = repository.head_ref
        except DetachedHeadException:
            parentbranch = None

    if parentbranch:
        parentremote = config.get('branch.%s.remote' % parentbranch)
        if parentremote:
            out.info('Using remote "%s" to pull parent from' % parentremote)
        else:
            out.info('Recording as a local branch')
    else:
        # no known parent branch, can't guess the remote
        parentremote = None

    stack = Stack.create(
        repository,
        name=branch_name,
        msg='create %s' % branch_name,
        create_at=branch_commit,
        parent_remote=parentremote,
        parent_branch=parentbranch,
        switch_to=True,
    )

    return stack
Exemplo n.º 22
0
def __build_address_headers(msg, options, extra_cc=[]):
    """Build the address headers and check existing headers in the
    template.
    """
    def __replace_header(header, addr):
        if addr:
            crt_addr = msg[header]
            del msg[header]

            if crt_addr:
                msg[header] = address_or_alias(', '.join([crt_addr, addr]))
            else:
                msg[header] = address_or_alias(addr)

    to_addr = ''
    cc_addr = ''
    bcc_addr = ''

    autobcc = config.get('stgit.autobcc') or ''

    if options.to:
        to_addr = ', '.join(options.to)
    if options.cc:
        cc_addr = ', '.join(options.cc + extra_cc)
    elif extra_cc:
        cc_addr = ', '.join(extra_cc)
    if options.bcc:
        bcc_addr = ', '.join(options.bcc + [autobcc])
    elif autobcc:
        bcc_addr = autobcc

    __replace_header('To', to_addr)
    __replace_header('Cc', cc_addr)
    __replace_header('Bcc', bcc_addr)
Exemplo n.º 23
0
def patch_desc(repo, cd, append_diff, diff_flags, replacement_diff):
    """Return a description text for the patch.

    The returned description is suitable for editing and/or reimporting with
    :func:`update_patch_description()`.

    :param cd: the :class:`stgit.lib.git.CommitData` to generate a description of
    :param append_diff: whether to append the patch diff to the description
    :type append_diff: bool
    :param diff_flags: extra parameters to pass to `git diff`
    :param replacement_diff: diff text to use; or None if it should be computed from cd
    :type replacement_diff: str or None

    """
    commit_encoding = config.get('i18n.commitencoding')
    desc = '\n'.join([
        'From: %s' % cd.author.name_email,
        'Date: %s' % cd.author.date.isoformat(),
        '',
        cd.message_str,
    ]).encode(commit_encoding)
    if append_diff:
        parts = [desc.rstrip(), b'', b'---', b'']
        if replacement_diff:
            parts.append(replacement_diff)
        else:
            diff = repo.diff_tree(cd.parent.data.tree, cd.tree, diff_flags)
            if diff:
                diffstat = repo.default_iw.diffstat(diff).encode(
                    commit_encoding)
                parts.extend([diffstat, diff])
        desc = b'\n'.join(parts)
    return desc
Exemplo n.º 24
0
def update_commit_data(cd, options):
    """Return a new CommitData object updated according to the command line
    options."""
    # Set the commit message from commandline.
    if options.message is not None:
        cd = cd.set_message(options.message)

    # Modify author data.
    cd = cd.set_author(options.author(cd.author))

    # Add Signed-off-by: or similar.
    if options.sign_str is not None:
        sign_str = options.sign_str
    else:
        sign_str = config.get("stgit.autosign")
    if sign_str is not None:
        cd = cd.set_message(
            add_sign_line(cd.message, sign_str, cd.committer.name,
                          cd.committer.email))

    # Let user edit the commit message manually, unless
    # --save-template or --message was specified.
    if not getattr(options, 'save_template', None) and options.message is None:
        tmpl = templates.get_template('patchdescr.tmpl')
        if tmpl:
            cd = cd.set_message(cd.message + tmpl)
        cd = cd.set_message(edit_string(cd.message, '.stgit-new.txt'))

    return cd
Exemplo n.º 25
0
def update_commit_data(cd,
                       message=None,
                       author=None,
                       sign_str=None,
                       edit=False):
    """Return a new CommitData object updated according to the command line
    options."""
    # Set the commit message from commandline.
    if message is not None:
        cd = cd.set_message(message)

    # Modify author data.
    if author is not None:
        cd = cd.set_author(author)

    # Add Signed-off-by: or similar.
    if sign_str is None:
        sign_str = config.get("stgit.autosign")
    if sign_str:
        cd = cd.set_message(
            add_trailer(cd.message_str, sign_str, cd.committer.name,
                        cd.committer.email))

    if edit:
        tmpl = templates.get_template('patchdescr.tmpl')
        if tmpl:
            cd = cd.set_message(cd.message + tmpl)
        cd = cd.set_message(edit_bytes(cd.message, '.stgit-new.txt'))

    return cd
Exemplo n.º 26
0
 def set_message(self, message):
     commit_encoding = config.get('i18n.commitencoding')
     if isinstance(message, bytes):
         message.decode(commit_encoding)
     else:
         message = message.encode(commit_encoding)
     return self._replace(message=message, encoding=commit_encoding)
Exemplo n.º 27
0
def __build_address_headers(msg, options, extra_cc=[]):
    """Build the address headers and check existing headers in the
    template.
    """
    to_addr = ''
    cc_addr = ''
    extra_cc_addr = ''
    bcc_addr = ''

    autobcc = config.get('stgit.autobcc') or ''

    if options.to:
        to_addr = ', '.join(options.to)
    if options.cc:
        cc_addr = ', '.join(options.cc)
    if extra_cc:
        extra_cc_addr = ', '.join(extra_cc)
    if options.bcc:
        bcc_addr = ', '.join(options.bcc + [autobcc])
    elif autobcc:
        bcc_addr = autobcc

    # if an address is on a header, ignore it from the rest
    from_set = __update_header(msg, 'From')
    to_set = __update_header(msg, 'To', to_addr)
    # --auto generated addresses, don't include the sender
    __update_header(msg, 'Cc', extra_cc_addr, from_set)
    cc_set = __update_header(msg, 'Cc', cc_addr, to_set)
    __update_header(msg, 'Bcc', bcc_addr, to_set.union(cc_set))
Exemplo n.º 28
0
def patch_desc(repo, cd, append_diff, diff_flags, replacement_diff):
    """Return a description text for the patch, suitable for editing
    and/or reimporting with L{update_patch_description()}.

    @param cd: The L{CommitData<stgit.lib.git.CommitData>} to generate
               a description of
    @param append_diff: Whether to append the patch diff to the
                        description
    @type append_diff: C{bool}
    @param diff_flags: Extra parameters to pass to C{git diff}
    @param replacement_diff: Diff text to use; or C{None} if it should
                             be computed from C{cd}
    @type replacement_diff: C{str} or C{None}"""
    commit_encoding = config.get('i18n.commitencoding')
    desc = '\n'.join([
        'From: %s' % cd.author.name_email,
        'Date: %s' % cd.author.date.isoformat(),
        '',
        cd.message_str,
    ]).encode(commit_encoding)
    if append_diff:
        desc += b'\n---\n'
        if replacement_diff:
            desc += b'\n' + replacement_diff
        else:
            diff = repo.diff_tree(cd.parent.data.tree, cd.tree, diff_flags)
            if diff:
                desc += b'\n'.join(
                    [b'', diffstat(diff).encode(commit_encoding), diff])
    return desc
Exemplo n.º 29
0
Arquivo: mail.py Projeto: samv/stgit
def __build_address_headers(msg, options, extra_cc = []):
    """Build the address headers and check existing headers in the
    template.
    """
    to_addr = ''
    cc_addr = ''
    extra_cc_addr = ''
    bcc_addr = ''

    autobcc = config.get('stgit.autobcc') or ''

    if options.to:
        to_addr = ', '.join(options.to)
    if options.cc:
        cc_addr = ', '.join(options.cc)
    if extra_cc:
        extra_cc_addr = ', '.join(extra_cc)
    if options.bcc:
        bcc_addr = ', '.join(options.bcc + [autobcc])
    elif autobcc:
        bcc_addr = autobcc

    # if an address is on a header, ignore it from the rest
    from_set = __update_header(msg, 'From')
    to_set = __update_header(msg, 'To', to_addr)
    # --auto generated addresses, don't include the sender
    __update_header(msg, 'Cc', extra_cc_addr, from_set)
    cc_set = __update_header(msg, 'Cc', cc_addr, to_set)
    bcc_set = __update_header(msg, 'Bcc', bcc_addr, to_set.union(cc_set))
Exemplo n.º 30
0
    def __init__(self,
                 tree,
                 parents,
                 message,
                 encoding=None,
                 author=None,
                 committer=None):
        self.tree = tree
        self.parents = parents
        self.encoding = (encoding if encoding is not None else
                         config.get('i18n.commitencoding'))
        if isinstance(message, bytes):
            self.message = message
        else:
            self.message = message.encode(self.encoding)

        if author is None:
            self._author = Person.author()
        else:
            assert isinstance(author, (Person, bytes))
            self._author = author

        if committer is None:
            self._committer = Person.committer()
        else:
            assert isinstance(committer, (Person, bytes))
            self._committer = committer
Exemplo n.º 31
0
def get_patch_description(repo, cd, patch_name, append_diff, diff_flags):
    """Return a description text for the patch.

    The returned description is suitable for editing and/or reimporting with
    :func:`_update_patch_description()`.

    :param cd: the :class:`stgit.lib.git.CommitData` to generate a description of
    :param append_diff: whether to append the patch diff to the description
    :type append_diff: bool
    :param diff_flags: extra parameters to pass to `git diff`

    """
    commit_encoding = config.get('i18n.commitencoding')
    desc = '\n'.join([
        'Patch: %s' % patch_name,
        'From: %s' % cd.author.name_email,
        'Date: %s' % cd.author.date.isoformat(),
        '',
        cd.message_str,
        EDIT_MESSAGE_INSTRUCTIONS,
    ]).encode(commit_encoding)
    if append_diff:
        parts = [desc.rstrip(), b'---', b'']
        diff = repo.diff_tree(cd.parent.data.tree,
                              cd.tree,
                              diff_flags,
                              binary=False,
                              full_index=True)
        if diff:
            diffstat = repo.default_iw.diffstat(diff).encode(commit_encoding)
            parts.extend([diffstat, diff])
        desc = b'\n'.join(parts)
    return desc
Exemplo n.º 32
0
Arquivo: mail.py Projeto: samv/stgit
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 = str(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, err:
        raise CmdException, 'Unknown patch template variable: %s' \
              % err
Exemplo n.º 33
0
 def parent_remote(self):
     remote = config.get(self._remote_key)
     if remote is None:
         raise BranchException(
             '%s: no parent remote; consider configuring "%s"' %
             (self.name, self._remote_key))
     else:
         return remote
Exemplo n.º 34
0
Arquivo: utils.py Projeto: vlrpl/stgit
def get_hooks_path(repository):
    hooks_path = config.get('core.hookspath')
    if hooks_path is None:
        return os.path.join(repository.directory, 'hooks')
    elif os.path.isabs(hooks_path):
        return hooks_path
    else:
        return os.path.join(repository.default_worktree.directory, hooks_path)
Exemplo n.º 35
0
def keep_option():
    return [
        opt('-k',
            '--keep',
            action='store_true',
            short='Keep the local changes',
            default=config.get('stgit.autokeep') == 'yes')
    ]
Exemplo n.º 36
0
def interactive_merge(filename):
    """Run the interactive merger on the given file. Note that the
    index should not have any conflicts.
    """
    extensions = file_extensions()

    ancestor = filename + extensions['ancestor']
    current = filename + extensions['current']
    patched = filename + extensions['patched']

    if os.path.isfile(ancestor):
        three_way = True
        files_dict = {'branch1': current,
                      'ancestor': ancestor,
                      'branch2': patched,
                      'output': filename}
        imerger = config.get('stgit.i3merge')
    else:
        three_way = False
        files_dict = {'branch1': current,
                      'branch2': patched,
                      'output': filename}
        imerger = config.get('stgit.i2merge')

    if not imerger:
        raise GitMergeException, 'No interactive merge command configured'

    # check whether we have all the files for the merge
    for fn in [filename, current, patched]:
        if not os.path.isfile(fn):
            raise GitMergeException, \
                  'Cannot run the interactive merge: "%s" missing' % fn

    mtime = os.path.getmtime(filename)

    out.info('Trying the interactive %s merge'
             % (three_way and 'three-way' or 'two-way'))

    err = os.system(imerger % files_dict)
    if err != 0:
        raise GitMergeException, 'The interactive merge failed: %d' % err
    if not os.path.isfile(filename):
        raise GitMergeException, 'The "%s" file is missing' % filename
    if mtime == os.path.getmtime(filename):
        raise GitMergeException, 'The "%s" file was not modified' % filename
Exemplo n.º 37
0
def pull(repository, remote_repository):
    """Pull changes from remote repository using 'git pull' by default."""
    command = (config.get(
        'branch.%s.stgit.pullcmd' % repository.current_branch_name)
               or config.get('stgit.pullcmd')).split()
    command.append(remote_repository)
    runner = repository.run(command)

    # Assumption: pullcmd will return 1 for a conflict and some non-1 code for
    # other errors. This is probably an incorrect assumption because pullcmd
    # may not be `git pull` and even `git pull` does not make any clear
    # guarantees about its return code. However, the consequence of any of this
    # going wrong will be that `stg pull` will return 3 (conflict) when 2
    # (generic error) might be more correct.
    runner.returns([0, 1]).run()
    if runner.exitcode:
        raise MergeConflictException([])

    repository.refs.reset_cache()
Exemplo n.º 38
0
def rebase(stack, iw, target_commit=None):
    command = (config.get('branch.%s.stgit.rebasecmd' % stack.name)
               or config.get('stgit.rebasecmd'))
    if not command and not target_commit:
        raise CmdException('Default rebasing requires a commit')
    elif target_commit:
        out.start('Rebasing to "%s"' % target_commit.sha1)
    else:
        out.start('Rebasing to the default target')

    if command:
        command = command.split()
        if target_commit is not None:
            command.append(target_commit.sha1)
        iw.run(command).run()
    else:
        iw.checkout_hard(target_commit)
        stack.set_head(target_commit, 'rebase')
    out.done()
Exemplo n.º 39
0
def keep_option():
    return [
        opt(
            "-k",
            "--keep",
            action="store_true",
            short="Keep the local changes",
            default=config.get("stgit.autokeep") == "yes",
        )
    ]
Exemplo n.º 40
0
def rebase(tree_id=None):
    """Rebase the current tree to the give tree_id. The tree_id
    argument may be something other than a GIT id if an external
    command is invoked.
    """
    command = (config.get('branch.%s.stgit.rebasecmd' % get_head_file())
               or config.get('stgit.rebasecmd'))
    if tree_id:
        args = [tree_id]
    elif command:
        args = []
    else:
        raise GitException('Default rebasing requires a commit id')
    if command:
        # clear the HEAD cache as the custom rebase command will update it
        __clear_head_cache()
        Run(*(command.split() + args)).run()
    else:
        # default rebasing
        reset(tree_id)
Exemplo n.º 41
0
 def __address_or_alias(addr):
     if not addr:
         return None
     if addr.find('@') >= 0:
         # it's an e-mail address
         return addr
     alias = config.get('mail.alias.'+addr)
     if alias:
         # it's an alias
         return alias
     raise CmdException, 'unknown e-mail alias: %s' % addr
Exemplo n.º 42
0
Arquivo: git.py Projeto: guanqun/stgit
def rebase(tree_id = None):
    """Rebase the current tree to the give tree_id. The tree_id
    argument may be something other than a GIT id if an external
    command is invoked.
    """
    command = config.get('branch.%s.stgit.rebasecmd' % get_head_file()) \
                or config.get('stgit.rebasecmd')
    if tree_id:
        args = [tree_id]
    elif command:
        args = []
    else:
        raise GitException, 'Default rebasing requires a commit id'
    if command:
        # clear the HEAD cache as the custom rebase command will update it
        __clear_head_cache()
        Run(*(command.split() + args)).run()
    else:
        # default rebasing
        reset(tree_id = tree_id)
Exemplo n.º 43
0
 def get_parent_branch(self):
     value = config.get("branch.%s.stgit.parentbranch" % self.get_name())
     if value:
         return value
     elif git.rev_parse("heads/origin"):
         out.note(
             ('No parent branch declared for stack "%s",' ' defaulting to "heads/origin".' % self.get_name()),
             ('Consider setting "branch.%s.stgit.parentbranch"' ' with "git config".' % self.get_name()),
         )
         return "heads/origin"
     else:
         raise StackException, 'Cannot find a parent branch for "%s"' % self.get_name()
Exemplo n.º 44
0
def diff_opts_option():
    def diff_opts_callback(option, opt_str, value, parser):
        if value:
            parser.values.diff_flags.extend(value.split())
        else:
            parser.values.diff_flags = []
    return [
        opt('-O', '--diff-opts', dest = 'diff_flags',
            default = (config.get('stgit.diff-opts') or '').split(),
            action = 'callback', callback = diff_opts_callback,
            type = 'string', metavar = 'OPTIONS',
            args = [strings('-M', '-C')],
            short = 'Extra options to pass to "git diff"')]
Exemplo n.º 45
0
def address_or_alias(addr_pair):
    """Return a name-email tuple the e-mail address is valid or look up
    the aliases in the config files.
    """
    addr = addr_pair[1]
    if "@" in addr:
        # it's an e-mail address
        return addr_pair
    alias = config.get("mail.alias." + addr)
    if alias:
        # it's an alias
        return name_email(alias)
    raise CmdException, "unknown e-mail alias: %s" % addr
Exemplo n.º 46
0
def __set_smtp_credentials(options):
    """Set the (smtpuser, smtppassword, smtpusetls) credentials if the method
    of sending is SMTP.
    """
    global __smtp_credentials

    smtpserver = options.smtp_server or config.get('stgit.smtpserver')
    if options.mbox or options.git or smtpserver.startswith('/'):
        return

    smtppassword = options.smtp_password or config.get('stgit.smtppassword')
    smtpuser = options.smtp_user or config.get('stgit.smtpuser')
    smtpusetls = options.smtp_tls or config.get('stgit.smtptls') == 'yes'

    if (smtppassword and not smtpuser):
        raise CmdException('SMTP password supplied, username needed')
    if (smtpusetls and not smtpuser):
        raise CmdException('SMTP over TLS requested, username needed')
    if (smtpuser and not smtppassword):
        smtppassword = getpass.getpass("Please enter SMTP password: ")

    __smtp_credentials = (smtpuser, smtppassword, smtpusetls)
Exemplo n.º 47
0
Arquivo: mail.py Projeto: samv/stgit
def __set_smtp_credentials(options):
    """Set the (smtpuser, smtppassword, smtpusetls) credentials if the method
    of sending is SMTP.
    """
    global __smtp_credentials

    smtpserver = options.smtp_server or config.get('stgit.smtpserver')
    if options.mbox or options.git or smtpserver.startswith('/'):
        return

    smtppassword = options.smtp_password or config.get('stgit.smtppassword')
    smtpuser = options.smtp_user or config.get('stgit.smtpuser')
    smtpusetls = options.smtp_tls or config.get('stgit.smtptls') == 'yes'

    if (smtppassword and not smtpuser):
        raise CmdException('SMTP password supplied, username needed')
    if (smtpusetls and not smtpuser):
        raise CmdException('SMTP over TLS requested, username needed')
    if (smtpuser and not smtppassword):
        smtppassword = getpass.getpass("Please enter SMTP password: ")

    __smtp_credentials = (smtpuser, smtppassword, smtpusetls)
Exemplo n.º 48
0
def address_or_alias(addr_pair):
    """Return a name-email tuple the e-mail address is valid or look up
    the aliases in the config files.
    """
    addr = addr_pair[1]
    if '@' in addr:
        # it's an e-mail address
        return addr_pair
    alias = config.get('mail.alias.' + addr)
    if alias:
        # it's an alias
        return name_email(alias)
    raise CmdException('unknown e-mail alias: %s' % addr)
Exemplo n.º 49
0
 def get_parent_remote(self):
     value = config.get('branch.%s.remote' % self.get_name())
     if value:
         return value
     elif 'origin' in git.remotes_list():
         out.note(('No parent remote declared for stack "%s",'
                   ' defaulting to "origin".' % self.get_name()),
                  ('Consider setting "branch.%s.remote" and'
                   ' "branch.%s.merge" with "git config".'
                   % (self.get_name(), self.get_name())))
         return 'origin'
     else:
         raise StackException, 'Cannot find a parent remote for "%s"' % self.get_name()
Exemplo n.º 50
0
def __build_address_headers(msg, options, extra_cc = []):
    """Build the address headers and check existing headers in the
    template.
    """
    def __addr_pairs(msg, header, extra):
        pairs = email.Utils.getaddresses(msg.get_all(header, []) + extra)
        # remove pairs without an address and resolve the aliases
        return [address_or_alias(p) for p in pairs if p[1]]

    def __update_header(header, addr = '', ignore = ()):
        addr_pairs = __addr_pairs(msg, header, [addr])
        del msg[header]
        # remove the duplicates and filter the addresses
        addr_dict = dict((addr, email.Utils.formataddr((name, addr)))
                         for name, addr in addr_pairs if addr not in ignore)
        if addr_dict:
            msg[header] = ', '.join(addr_dict.itervalues())
        return set(addr_dict.iterkeys())

    to_addr = ''
    cc_addr = ''
    extra_cc_addr = ''
    bcc_addr = ''

    autobcc = config.get('stgit.autobcc') or ''

    if options.to:
        to_addr = ', '.join(options.to)
    if options.cc:
        cc_addr = ', '.join(options.cc)
    if extra_cc:
        extra_cc_addr = ', '.join(extra_cc)
    if options.bcc:
        bcc_addr = ', '.join(options.bcc + [autobcc])
    elif autobcc:
        bcc_addr = autobcc

    # if an address is on a header, ignore it from the rest
    to_set = __update_header('To', to_addr)
    cc_set = __update_header('Cc', cc_addr, to_set)
    bcc_set = __update_header('Bcc', bcc_addr, to_set.union(cc_set))

    # --auto generated addresses, don't include the sender
    from_set = __update_header('From')
    __update_header('Cc', extra_cc_addr, to_set.union(bcc_set).union(from_set))

    # update other address headers
    __update_header('Reply-To')
    __update_header('Mail-Reply-To')
    __update_header('Mail-Followup-To')
Exemplo n.º 51
0
def __get_sender():
    """Return the 'authname <authemail>' string as read from the
    configuration file
    """
    sender=config.get('stgit.sender')
    if not sender:
        try:
            sender = str(git.user())
        except git.GitException:
            sender = str(git.author())

    if not sender:
        raise CmdException, 'unknown sender details'

    return address_or_alias(sender)
Exemplo n.º 52
0
Arquivo: git.py Projeto: guanqun/stgit
def merge_recursive(base, head1, head2):
    """Perform a 3-way merge between base, head1 and head2 into the
    local tree
    """
    refresh_index()
    p = GRun('merge-recursive', base, '--', head1, head2).env(
        { 'GITHEAD_%s' % base: 'ancestor',
          'GITHEAD_%s' % head1: 'current',
          'GITHEAD_%s' % head2: 'patched'}).returns([0, 1])
    output = p.output_lines()
    if p.exitcode:
        # There were conflicts
        if config.get('stgit.autoimerge') == 'yes':
            mergetool()
        else:
            conflicts = [l for l in output if l.startswith('CONFLICT')]
            out.info(*conflicts)
            raise GitException, "%d conflict(s)" % len(conflicts)
Exemplo n.º 53
0
def call_editor(filename):
    """Run the editor on the specified filename."""

    # the editor
    editor = config.get('stgit.editor')
    if editor:
        pass
    elif 'EDITOR' in os.environ:
        editor = os.environ['EDITOR']
    else:
        editor = 'vi'
    editor += ' %s' % filename

    out.start('Invoking the editor: "%s"' % editor)
    err = os.system(editor)
    if err:
        raise EditorException, 'editor failed, exit code: %d' % err
    out.done()
Exemplo n.º 54
0
def __build_cover(tmpl, total_nr, msg_id, options):
    """Build the cover message (series description) to be sent via SMTP
    """
    sender = __get_sender()

    if options.version:
        version_str = ' %s' % options.version
    else:
        version_str = ''

    if options.prefix:
        prefix_str = options.prefix + ' '
    else:
        confprefix = config.get('stgit.mail.prefix')
        if confprefix:
            prefix_str = confprefix + ' '
        else:
            prefix_str = ''
        
    total_nr_str = str(total_nr)
    patch_nr_str = '0'.zfill(len(total_nr_str))
    if total_nr > 1:
        number_str = ' %s/%s' % (patch_nr_str, total_nr_str)
    else:
        number_str = ''

    tmpl_dict = {'sender':       sender,
                 # for backward template compatibility
                 'maintainer':   sender,
                 # for backward template compatibility
                 'endofheaders': '',
                 # for backward template compatibility
                 'date':         '',
                 'version':      version_str,
                 'prefix':	 prefix_str,
                 'patchnr':      patch_nr_str,
                 'totalnr':      total_nr_str,
                 'number':       number_str}

    try:
        msg_string = tmpl % tmpl_dict
    except KeyError, err:
        raise CmdException, 'Unknown patch template variable: %s' \
              % err
Exemplo n.º 55
0
Arquivo: mail.py Projeto: snits/stgit
def __get_sender():
    """Return the 'authname <authemail>' string as read from the
    configuration file
    """
    sender = config.get('stgit.sender')
    if not sender:
        try:
            sender = git.user()
        except git.GitException:
            try:
                sender = 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))
Exemplo n.º 56
0
 def push_patch(self, pn, iw = None, allow_interactive = False,
                already_merged = False):
     """Attempt to push the named patch. If this results in conflicts,
     halts the transaction. If index+worktree are given, spill any
     conflicts to them."""
     out.start('Pushing patch "%s"' % pn)
     orig_cd = self.patches[pn].data
     cd = orig_cd.set_committer(None)
     oldparent = cd.parent
     cd = cd.set_parent(self.top)
     if already_merged:
         # the resulting patch is empty
         tree = cd.parent.data.tree
     else:
         base = oldparent.data.tree
         ours = cd.parent.data.tree
         theirs = cd.tree
         tree, self.temp_index_tree = self.temp_index.merge(
             base, ours, theirs, self.temp_index_tree)
     s = ''
     merge_conflict = False
     if not tree:
         if iw == None:
             self.__halt('%s does not apply cleanly' % pn)
         try:
             self.__checkout(ours, iw, allow_bad_head = False)
         except git.CheckoutException:
             self.__halt('Index/worktree dirty')
         try:
             interactive = (allow_interactive and
                            config.get('stgit.autoimerge') == 'yes')
             iw.merge(base, ours, theirs, interactive = interactive)
             tree = iw.index.write_tree()
             self.__current_tree = tree
             s = 'modified'
         except git.MergeConflictException, e:
             tree = ours
             merge_conflict = True
             self.__conflicts = e.conflicts
             s = 'conflict'
         except git.MergeException, e:
             self.__halt(str(e))
Exemplo n.º 57
0
def diff_opts_option():
    def diff_opts_callback(option, opt_str, value, parser):
        if value:
            parser.values.diff_flags.extend(value.split())
        else:
            parser.values.diff_flags = []

    return [
        opt(
            "-O",
            "--diff-opts",
            dest="diff_flags",
            default=(config.get("stgit.diff-opts") or "").split(),
            action="callback",
            callback=diff_opts_callback,
            type="string",
            metavar="OPTIONS",
            args=[strings("-M", "-C")],
            short='Extra options to pass to "git diff"',
        )
    ]
Exemplo n.º 58
0
Arquivo: mail.py Projeto: samv/stgit
def __send_message(type, tmpl, options, *args):
    """Message sending dispatcher.
    """
    (build, outstr) = {'cover': (__build_cover, 'the cover message'),
                       'patch': (__build_message, 'patch "%s"' % args[0])}[type]
    if type == 'patch':
        (patch_nr, total_nr) = (args[1], args[2])

    msg_id = email.Utils.make_msgid('stgit')
    msg = build(tmpl, msg_id, options, *args)

    msg_str = msg.as_string(options.mbox)
    if options.mbox:
        out.stdout_raw(msg_str + '\n')
        return msg_id

    if not options.git:
        from_addr, to_addrs = __parse_addresses(msg)
        out.start('Sending ' + outstr)

    smtpserver = options.smtp_server or config.get('stgit.smtpserver')
    if options.git:
        __send_message_git(msg, options)
    elif smtpserver.startswith('/'):
        # Use the sendmail tool
        __send_message_sendmail(smtpserver, msg_str)
    else:
        # Use the SMTP server (we have host and port information)
        __send_message_smtp(smtpserver, from_addr, to_addrs, msg_str, options)

    # give recipients a chance of receiving related patches in correct order
    if type == 'cover' or (type == 'patch' and patch_nr < total_nr):
        sleep = options.sleep or config.getint('stgit.smtpdelay')
        time.sleep(sleep)
    if not options.git:
        out.done()
    return msg_id
Exemplo n.º 59
0
def func(parser, options, args):
    """Show the patch series
    """
    if options.all and options.short:
        raise common.CmdException, 'combining --all and --short is meaningless'

    stack = directory.repository.get_stack(options.branch)
    if options.missing:
        cmp_stack = stack
        stack = directory.repository.get_stack(options.missing)

    # current series patches
    applied = unapplied = hidden = ()
    if options.applied or options.unapplied or options.hidden:
        if options.all:
            raise common.CmdException, \
                '--all cannot be used with --applied/unapplied/hidden'
        if options.applied:
            applied = stack.patchorder.applied
        if options.unapplied:
            unapplied = stack.patchorder.unapplied
        if options.hidden:
            hidden = stack.patchorder.hidden
    elif options.all:
        applied = stack.patchorder.applied
        unapplied = stack.patchorder.unapplied
        hidden = stack.patchorder.hidden
    else:
        applied = stack.patchorder.applied
        unapplied = stack.patchorder.unapplied

    if options.missing:
        cmp_patches = cmp_stack.patchorder.all
    else:
        cmp_patches = ()

    # the filtering range covers the whole series
    if args:
        show_patches = parse_patches(args, applied + unapplied + hidden,
                                     len(applied))
    else:
        show_patches = applied + unapplied + hidden

    # missing filtering
    show_patches = [p for p in show_patches if p not in cmp_patches]

    # filter the patches
    applied = [p for p in applied if p in show_patches]
    unapplied = [p for p in unapplied if p in show_patches]
    hidden = [p for p in hidden if p in show_patches]

    if options.short:
        nr = int(config.get('stgit.shortnr'))
        if len(applied) > nr:
            applied = applied[-(nr+1):]
        n = len(unapplied)
        if n > nr:
            unapplied = unapplied[:nr]
        elif n < nr:
            hidden = hidden[:nr-n]

    patches = applied + unapplied + hidden

    if options.count:
        out.stdout(len(patches))
        return

    if not patches:
        return

    if options.showbranch:
        branch_str = stack.name + ':'
    else:
        branch_str = ''

    max_len = 0
    if len(patches) > 0:
        max_len = max([len(i + branch_str) for i in patches])

    if applied:
        for p in applied[:-1]:
            __print_patch(stack, p, branch_str, '+ ', max_len, options, config.get("stgit.color.applied"))
        __print_patch(stack, applied[-1], branch_str, '> ', max_len, options, config.get("stgit.color.current"))

    for p in unapplied:
        __print_patch(stack, p, branch_str, '- ', max_len, options, config.get("stgit.color.unapplied"))

    for p in hidden:
        __print_patch(stack, p, branch_str, '! ', max_len, options, config.get("stgit.color.hidden"))