Esempio n. 1
0
def func(parser, options, args):
    """Integrate a GNU diff patch into the current patch"""
    if len(args) > 1:
        parser.error('incorrect number of arguments')

    repository = directory.repository
    stack = repository.get_stack()

    check_local_changes(repository)
    check_conflicts(repository.default_iw)
    check_head_top_equal(stack)

    if len(args) == 1:
        filename = args[0]
    else:
        filename = None

    applied = stack.patchorder.applied
    if not applied:
        raise CmdException('No patches applied')

    current = applied[-1]

    if filename:
        if os.path.exists(filename):
            out.start('Folding patch "%s"' % filename)
            with io.open(filename, 'rb') as f:
                diff = f.read()
        else:
            raise CmdException('No such file: %s' % filename)
    else:
        out.start('Folding patch from stdin')
        diff = sys.stdin.buffer.read()

    if options.threeway:
        top_patch = stack.patches.get(current)
        apply_patch(
            stack,
            diff,
            base=top_patch.commit.data.parent,
            strip=options.strip,
            reject=options.reject,
        )
    elif options.base:
        apply_patch(
            stack,
            diff,
            base=git_commit(options.base, repository),
            reject=options.reject,
            strip=options.strip,
        )
    else:
        apply_patch(
            stack,
            diff,
            strip=options.strip,
            reject=options.reject,
        )

    out.done()
Esempio n. 2
0
 def check_merged(self, patches, tree=None, quiet=False):
     """Return a subset of patches already merged."""
     if not quiet:
         out.start('Checking for patches merged upstream')
     merged = []
     if tree:
         self.temp_index.read_tree(tree)
         self.temp_index_tree = tree
     elif self.temp_index_tree != self.stack.head.data.tree:
         self.temp_index.read_tree(self.stack.head.data.tree)
         self.temp_index_tree = self.stack.head.data.tree
     for pn in reversed(patches):
         # check whether patch changes can be reversed in the current index
         cd = self.patches[pn].data
         if cd.is_nochange():
             continue
         try:
             self.temp_index.apply_treediff(
                 cd.tree,
                 cd.parent.data.tree,
                 quiet=True,
             )
             merged.append(pn)
             # The self.temp_index was modified by apply_treediff() so
             # force read_tree() the next time merge() is used.
             self.temp_index_tree = None
         except MergeException:
             pass
     if not quiet:
         out.done('%d found' % len(merged))
     return merged
Esempio n. 3
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
Esempio n. 4
0
File: git.py Progetto: snits/stgit
def tree_status(files=None, tree_id='HEAD', verbose=False):
    """Get the status of all changed files, or of a selected set of
    files. Returns a list of pairs - (status, filename).

    If 'not files', it will check all files. If 'files' is a list, it will only
    check the files in the list.
    """
    if verbose:
        out.start('Checking for changes in the working directory')

    refresh_index()

    if files is None:
        files = []
    cache_files = []

    # conflicted files
    conflicts = get_conflicts()
    cache_files += [('C', filename) for filename in conflicts
                    if not files or filename in files]
    reported_files = set(conflicts)
    files_left = [f for f in files if f not in reported_files]

    # files in the index. Only execute this code if no files were
    # specified when calling the function (i.e. report all files) or
    # files were specified but already found in the previous step
    if not files or files_left:
        args = [tree_id]
        if files_left:
            args += ['--'] + files_left
        diff_index_lines = GRun('diff-index', '-z', *args).output_lines('\0')
        for t, fn in parse_git_ls(diff_index_lines):
            # the condition is needed in case files is emtpy and
            # diff-index lists those already reported
            if fn not in reported_files:
                cache_files.append((t, fn))
                reported_files.add(fn)
        files_left = [f for f in files if f not in reported_files]

    # files in the index but changed on (or removed from) disk. Only
    # execute this code if no files were specified when calling the
    # function (i.e. report all files) or files were specified but
    # already found in the previous step
    if not files or files_left:
        args = []
        if files_left:
            args += ['--'] + files_left
        diff_files_lines = GRun('diff-files', '-z', *args).output_lines('\0')
        for t, fn in parse_git_ls(diff_files_lines):
            # the condition is needed in case files is empty and
            # diff-files lists those already reported
            if fn not in reported_files:
                cache_files.append((t, fn))
                reported_files.add(fn)

    if verbose:
        out.done()

    return cache_files
Esempio n. 5
0
def tree_status(files=None, tree_id='HEAD', verbose=False):
    """Get the status of all changed files, or of a selected set of
    files. Returns a list of pairs - (status, filename).

    If 'not files', it will check all files. If 'files' is a list, it will only
    check the files in the list.
    """
    if verbose:
        out.start('Checking for changes in the working directory')

    refresh_index()

    if files is None:
        files = []
    cache_files = []

    # conflicted files
    conflicts = get_conflicts()
    cache_files += [('C', filename) for filename in conflicts
                    if not files or filename in files]
    reported_files = set(conflicts)
    files_left = [f for f in files if f not in reported_files]

    # files in the index. Only execute this code if no files were
    # specified when calling the function (i.e. report all files) or
    # files were specified but already found in the previous step
    if not files or files_left:
        args = [tree_id]
        if files_left:
            args += ['--'] + files_left
        diff_index_lines = GRun('diff-index', '-z', *args).output_lines('\0')
        for t, fn in parse_git_ls(diff_index_lines):
            # the condition is needed in case files is emtpy and
            # diff-index lists those already reported
            if fn not in reported_files:
                cache_files.append((t, fn))
                reported_files.add(fn)
        files_left = [f for f in files if f not in reported_files]

    # files in the index but changed on (or removed from) disk. Only
    # execute this code if no files were specified when calling the
    # function (i.e. report all files) or files were specified but
    # already found in the previous step
    if not files or files_left:
        args = []
        if files_left:
            args += ['--'] + files_left
        diff_files_lines = GRun('diff-files', '-z', *args).output_lines('\0')
        for t, fn in parse_git_ls(diff_files_lines):
            # the condition is needed in case files is empty and
            # diff-files lists those already reported
            if fn not in reported_files:
                cache_files.append((t, fn))
                reported_files.add(fn)

    if verbose:
        out.done()

    return cache_files
Esempio n. 6
0
def call_editor(filename):
    """Run the editor on the specified filename."""
    cmd = '%s %s' % (get_editor(), filename)
    out.start('Invoking the editor: "%s"' % cmd)
    err = os.system(cmd)
    if err:
        raise EditorException('editor failed, exit code: %d' % err)
    out.done()
Esempio n. 7
0
def __cleanup_branch(name, force = False):
    branch = stack.Series(name)
    if branch.get_protected():
        raise CmdException('This branch is protected. Clean up is not permitted')

    out.start('Cleaning up branch "%s"' % name)
    branch.delete(force = force, cleanup = True)
    out.done()
Esempio n. 8
0
def prepare_rebase(crt_series):
    # pop all patches
    applied = crt_series.get_applied()
    if len(applied) > 0:
        out.start('Popping all applied patches')
        crt_series.pop_patch(applied[0])
        out.done()
    return applied
Esempio n. 9
0
def prepare_rebase(crt_series):
    # pop all patches
    applied = crt_series.get_applied()
    if len(applied) > 0:
        out.start('Popping all applied patches')
        crt_series.pop_patch(applied[0])
        out.done()
    return applied
Esempio n. 10
0
File: utils.py Progetto: snits/stgit
def call_editor(filename):
    """Run the editor on the specified filename."""
    cmd = '%s %s' % (get_editor(), filename)
    out.start('Invoking the editor: "%s"' % cmd)
    err = os.system(cmd)
    if err:
        raise EditorException('editor failed, exit code: %d' % err)
    out.done()
Esempio n. 11
0
def __cleanup_branch(name, force=False):
    branch = stack.Series(name)
    if branch.get_protected():
        raise CmdException(
            'This branch is protected. Clean up is not permitted')

    out.start('Cleaning up branch "%s"' % name)
    branch.delete(force=force, cleanup=True)
    out.done()
Esempio n. 12
0
 def check_and_append(c, n):
     next = n.data.parents
     try:
         [next] = next
     except ValueError:
         out.done()
         raise common.CmdException(
             'Trying to uncommit %s, which does not have exactly one parent'
             % n.sha1)
     return c.append(n)
Esempio n. 13
0
 def check_and_append(c, n):
     next = n.data.parents
     try:
         [next] = next
     except ValueError:
         out.done()
         raise CmdException(
             'Trying to uncommit %s, which does not have exactly one parent'
             % n.sha1)
     return c.append(n)
Esempio n. 14
0
def check_local_changes(repository):
    out.start('Checking for changes in the working directory')
    iw = repository.default_iw
    iw.refresh_index()
    tree = repository.refs.get(repository.head_ref).data.tree
    local_changes = iw.changed_files(tree)
    out.done()
    if local_changes:
        raise CmdException(
            'local changes in the tree. Use "refresh" or "reset --hard"')
Esempio n. 15
0
def __cleanup_branch(name, force=False):
    stack = directory.repository.get_stack(name)
    if stack.protected:
        raise CmdException('This branch is protected. Clean up is not permitted')
    if not force and stack.patchorder.all:
        raise CmdException('Cannot clean up: the series still contains patches')

    out.start('Cleaning up branch "%s"' % name)
    stack.cleanup()
    out.done()
Esempio n. 16
0
def __delete_branch(doomed_name, force = False):
    doomed = stack.Series(doomed_name)

    if __is_current_branch(doomed_name):
        raise CmdException('Cannot delete the current branch')
    if doomed.get_protected():
        raise CmdException('This branch is protected. Delete is not permitted')

    out.start('Deleting branch "%s"' % doomed_name)
    doomed.delete(force)
    out.done()
Esempio n. 17
0
def __delete_branch(doomed_name, force=False):
    doomed = stack.Series(doomed_name)

    if __is_current_branch(doomed_name):
        raise CmdException('Cannot delete the current branch')
    if doomed.get_protected():
        raise CmdException('This branch is protected. Delete is not permitted')

    out.start('Deleting branch "%s"' % doomed_name)
    doomed.delete(force)
    out.done()
Esempio n. 18
0
def check_local_changes(stack=None):
    out.start('Checking for changes in the working directory')
    if stack:
        local_changes = stack.repository.default_iw.changed_files(
            stack.head.data.tree)
    else:
        local_changes = git.local_changes()
    out.done()
    if local_changes:
        raise CmdException(
            'local changes in the tree. Use "refresh" or "reset --hard"')
Esempio n. 19
0
def rebase(crt_series, target):
    try:
        tree_id = git_id(crt_series, target)
    except:
        # it might be that we use a custom rebase command with its own
        # target type
        tree_id = target
    if target:
        out.start('Rebasing to "%s"' % target)
    else:
        out.start('Rebasing to the default target')
    git.rebase(tree_id = tree_id)
    out.done()
Esempio n. 20
0
def rebase(crt_series, target):
    try:
        tree_id = git_id(crt_series, target)
    except StgException:
        # it might be that we use a custom rebase command with its own
        # target type
        tree_id = target
    if target:
        out.start('Rebasing to "%s"' % target)
    else:
        out.start('Rebasing to the default target')
    git.rebase(tree_id=tree_id)
    out.done()
Esempio n. 21
0
def func(parser, options, args):
    """Integrate a GNU diff patch into the current patch
    """
    if len(args) > 1:
        parser.error('incorrect number of arguments')

    check_local_changes()
    check_conflicts()
    check_head_top_equal(crt_series)

    if len(args) == 1:
        filename = args[0]
    else:
        filename = None

    current = crt_series.get_current()
    if not current:
        raise CmdException('No patches applied')

    if filename:
        if os.path.exists(filename):
            out.start('Folding patch "%s"' % filename)
        else:
            raise CmdException('No such file: %s' % filename)
    else:
        out.start('Folding patch from stdin')

    if options.threeway:
        crt_patch = crt_series.get_patch(current)
        bottom = crt_patch.get_bottom()
        git.apply_patch(
            filename=filename,
            base=bottom,
            strip=options.strip,
            reject=options.reject,
        )
    elif options.base:
        git.apply_patch(
            filename=filename,
            reject=options.reject,
            strip=options.strip,
            base=git_id(crt_series, options.base),
        )
    else:
        git.apply_patch(
            filename=filename,
            strip=options.strip,
            reject=options.reject,
        )

    out.done()
Esempio n. 22
0
def pop_patches(crt_series, patches, keep=False):
    """Pop the patches in the list from the stack. It is assumed that
    the patches are listed in the stack reverse order.
    """
    if len(patches) == 0:
        out.info('Nothing to push/pop')
    else:
        p = patches[-1]
        if len(patches) == 1:
            out.start('Popping patch "%s"' % p)
        else:
            out.start('Popping patches "%s" - "%s"' % (patches[0], p))
        crt_series.pop_patch(p, keep)
        out.done()
Esempio n. 23
0
def pop_patches(crt_series, patches, keep = False):
    """Pop the patches in the list from the stack. It is assumed that
    the patches are listed in the stack reverse order.
    """
    if len(patches) == 0:
        out.info('Nothing to push/pop')
    else:
        p = patches[-1]
        if len(patches) == 1:
            out.start('Popping patch "%s"' % p)
        else:
            out.start('Popping patches "%s" - "%s"' % (patches[0], p))
        crt_series.pop_patch(p, keep)
        out.done()
Esempio n. 24
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
Esempio n. 25
0
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)

    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 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
Esempio n. 26
0
def func(parser, options, args):
    """Rename a patch in the series
    """
    crt = crt_series.get_current()

    if len(args) == 2:
        old, new = args
    elif len(args) == 1:
        if not crt:
            raise CmdException("No applied top patch to rename exists.")
        old, [new] = crt, args
    else:
        parser.error('incorrect number of arguments')

    out.start('Renaming patch "%s" to "%s"' % (old, new))
    crt_series.rename_patch(old, new)

    out.done()
Esempio n. 27
0
def func(parser, options, args):
    """Rename a patch in the series
    """
    crt = crt_series.get_current()

    if len(args) == 2:
        old, new = args
    elif len(args) == 1:
        if not crt:
            raise CmdException("No applied top patch to rename exists.")
        old, [new] = crt, args
    else:
        parser.error('incorrect number of arguments')

    out.start('Renaming patch "%s" to "%s"' % (old, new))
    crt_series.rename_patch(old, new)

    out.done()
Esempio n. 28
0
def func(parser, options, args):
    """Rename a patch in the series"""
    stack = directory.repository.get_stack(options.branch)

    if len(args) == 2:
        old, new = args
    elif len(args) == 1:
        if not stack.patchorder.applied:
            raise CmdException("No applied top patch to rename exists.")
        old = stack.patchorder.applied[-1]
        new = args[0]
    else:
        parser.error('incorrect number of arguments')

    out.start('Renaming patch "%s" to "%s"' % (old, new))
    stack.rename_patch(old, new)
    log_entry(stack, 'rename %s to %s' % (old, new))
    out.done()
Esempio n. 29
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()
Esempio n. 30
0
def prepare_rebase(stack, cmd_name):
    # pop all patches
    iw = stack.repository.default_iw
    trans = StackTransaction(stack, '%s (pop)' % cmd_name, check_clean_iw=iw)
    out.start('Popping all applied patches')
    try:
        trans.reorder_patches(
            applied=[],
            unapplied=trans.applied + trans.unapplied,
            iw=iw,
            allow_interactive=True,
        )
    except TransactionException:
        pass
    retval = trans.run(iw, print_current_patch=False)
    if retval:
        out.done('Failed to pop applied patches')
    else:
        out.done()
    return retval
Esempio n. 31
0
File: fold.py Progetto: snits/stgit
def func(parser, options, args):
    """Integrate a GNU diff patch into the current patch
    """
    if len(args) > 1:
        parser.error('incorrect number of arguments')

    check_local_changes()
    check_conflicts()
    check_head_top_equal(crt_series)

    if len(args) == 1:
        filename = args[0]
    else:
        filename = None

    current = crt_series.get_current()
    if not current:
        raise CmdException('No patches applied')

    if filename:
        if os.path.exists(filename):
            out.start('Folding patch "%s"' % filename)
        else:
            raise CmdException('No such file: %s' % filename)
    else:
        out.start('Folding patch from stdin')

    if options.threeway:
        crt_patch = crt_series.get_patch(current)
        bottom = crt_patch.get_bottom()
        git.apply_patch(filename = filename, base = bottom,
                        strip = options.strip, reject = options.reject)
    elif options.base:
        git.apply_patch(filename = filename, reject = options.reject,
                        strip = options.strip,
                        base = git_id(crt_series, options.base))
    else:
        git.apply_patch(filename = filename, strip = options.strip,
                        reject = options.reject)

    out.done()
Esempio n. 32
0
def read_commit_dag(branch):
    out.start('Reading commit DAG')
    commits = {}
    patches = set()
    for line in Run('git', 'rev-list', '--parents', '--all').output_lines():
        cs = line.split()
        for id in cs:
            if id not in commits:
                commits[id] = Commit(id)
        for id in cs[1:]:
            commits[cs[0]].parents.add(commits[id])
            commits[id].children.add(commits[cs[0]])
    for line in Run('git', 'show-ref').output_lines():
        id, ref = line.split()
        m = re.match(r'^refs/patches/%s/(.+)$' % re.escape(branch), ref)
        if m and not m.group(1).endswith('.log'):
            c = commits[id]
            c.patch = m.group(1)
            patches.add(c)
    out.done()
    return commits, patches
Esempio n. 33
0
def __delete_branch(doomed_name, force=False):
    if __is_current_branch(doomed_name):
        raise CmdException('Cannot delete the current branch')

    branch = Branch(directory.repository, doomed_name)
    try:
        stack = directory.repository.get_stack(doomed_name)
    except StackException:
        stack = None

    if stack:
        if stack.protected:
            raise CmdException('This branch is protected. Delete is not permitted')
        if not force and stack.patchorder.all:
            raise CmdException('Cannot delete: the series still contains patches')

    out.start('Deleting branch "%s"' % doomed_name)
    if stack:
        stack.cleanup()
    branch.delete()
    out.done()
Esempio n. 34
0
def read_commit_dag(branch):
    out.start('Reading commit DAG')
    commits = {}
    patches = set()
    for line in Run('git', 'rev-list', '--parents', '--all').output_lines():
        cs = line.split()
        for id in cs:
            if id not in commits:
                commits[id] = Commit(id)
        for id in cs[1:]:
            commits[cs[0]].parents.add(commits[id])
            commits[id].children.add(commits[cs[0]])
    for line in Run('git', 'show-ref').output_lines():
        id, ref = line.split()
        m = re.match(r'^refs/patches/%s/(.+)$' % re.escape(branch), ref)
        if m and not m.group(1).endswith('.log'):
            c = commits[id]
            c.patch = m.group(1)
            patches.add(c)
    out.done()
    return commits, patches
Esempio n. 35
0
File: mail.py Progetto: snits/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
Esempio n. 36
0
def push_patches(crt_series, patches, check_merged = False):
    """Push multiple patches onto the stack. This function is shared
    between the push and pull commands
    """
    forwarded = crt_series.forward_patches(patches)
    if forwarded > 1:
        out.info('Fast-forwarded patches "%s" - "%s"'
                 % (patches[0], patches[forwarded - 1]))
    elif forwarded == 1:
        out.info('Fast-forwarded patch "%s"' % patches[0])

    names = patches[forwarded:]

    # check for patches merged upstream
    if names and check_merged:
        out.start('Checking for patches merged upstream')

        merged = crt_series.merged_patches(names)

        out.done('%d found' % len(merged))
    else:
        merged = []

    for p in names:
        out.start('Pushing patch "%s"' % p)

        if p in merged:
            crt_series.push_empty_patch(p)
            out.done('merged upstream')
        else:
            modified = crt_series.push_patch(p)

            if crt_series.empty_patch(p):
                out.done('empty patch')
            elif modified:
                out.done('modified')
            else:
                out.done()
Esempio n. 37
0
def push_patches(crt_series, patches, check_merged=False):
    """Push multiple patches onto the stack. This function is shared
    between the push and pull commands
    """
    forwarded = crt_series.forward_patches(patches)
    if forwarded > 1:
        out.info('Fast-forwarded patches "%s" - "%s"'
                 % (patches[0], patches[forwarded - 1]))
    elif forwarded == 1:
        out.info('Fast-forwarded patch "%s"' % patches[0])

    names = patches[forwarded:]

    # check for patches merged upstream
    if names and check_merged:
        out.start('Checking for patches merged upstream')

        merged = crt_series.merged_patches(names)

        out.done('%d found' % len(merged))
    else:
        merged = []

    for p in names:
        out.start('Pushing patch "%s"' % p)

        if p in merged:
            crt_series.push_empty_patch(p)
            out.done('merged upstream')
        else:
            modified = crt_series.push_patch(p)

            if crt_series.empty_patch(p):
                out.done('empty patch')
            elif modified:
                out.done('modified')
            else:
                out.done()
Esempio n. 38
0
File: mail.py Progetto: snits/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
Esempio n. 39
0
def push_patches(crt_series, patches):
    """Push multiple patches onto the stack. This function is shared
    between the push and pull commands
    """
    forwarded = crt_series.forward_patches(patches)
    if forwarded > 1:
        out.info('Fast-forwarded patches "%s" - "%s"' %
                 (patches[0], patches[forwarded - 1]))
    elif forwarded == 1:
        out.info('Fast-forwarded patch "%s"' % patches[0])

    names = patches[forwarded:]

    for p in names:
        out.start('Pushing patch "%s"' % p)

        modified = crt_series.push_patch(p)

        if crt_series.empty_patch(p):
            out.done('empty patch')
        elif modified:
            out.done('modified')
        else:
            out.done()
Esempio n. 40
0
def func(parser, options, args):
    """Repair inconsistencies in StGit metadata."""
    if args:
        parser.error('incorrect number of arguments')

    repository = directory.repository
    stack = repository.get_stack()

    if stack.protected:
        raise CmdException(
            'This branch is protected. Modification is not permitted.')

    patchorder = stack.patchorder
    patches = [stack.patches.get(pn) for pn in patchorder.all]

    # Find commits that aren't patches, and applied patches.
    patchify = []  # commits to definitely patchify
    maybe_patchify = []  # commits to patchify if we find a patch below them
    applied = []
    c = stack.head
    while len(c.data.parents) == 1:
        for p in patches:
            if p.commit == c:
                applied.append(p)
                patchify.extend(maybe_patchify)
                maybe_patchify = []
                break
        else:
            maybe_patchify.append(c)
        c = c.data.parent
    applied.reverse()
    patchify.reverse()

    # Find patches unreachable behind a merge.
    merge = c
    todo = set([c])
    seen = set()
    unreachable = set()
    while todo:
        c = todo.pop()
        seen.add(c)
        todo |= set(c.data.parents) - seen
        if any(p.commit == c for p in patches):
            unreachable.add(c)
    if unreachable:
        out.warn(('%d patch%s are hidden below the merge commit' %
                  (len(unreachable), ['es', ''][len(unreachable) == 1])),
                 '%s,' % merge.sha1, 'and will be considered unapplied.')

    # Make patches of any linear sequence of commits on top of a patch.
    if applied and patchify:
        out.start('Creating %d new patch%s' %
                  (len(patchify), ['es', ''][len(patchify) == 1]))

        for c in patchify:
            pn = make_patch_name(
                c.data.message,
                unacceptable=lambda name: any(p.name == name for p in patches),
            )
            out.info('Creating patch %s from commit %s' % (pn, c.sha1))
            applied.append(stack.patches.new(pn, c, 'repair'))
        out.done()

    # Figure out hidden
    hidden = [p for p in patches if p.name in patchorder.hidden]

    # Write the applied/unapplied files.
    out.start('Checking patch appliedness')
    unapplied = [p for p in patches if p not in applied and p not in hidden]
    for pn in patchorder.all:
        if all(pn != p.name for p in patches):
            out.info('%s is gone' % pn)
    for p in applied:
        if p.name not in patchorder.applied:
            out.info('%s is now applied' % p.name)
    for p in unapplied:
        if p.name not in patchorder.unapplied:
            out.info('%s is now unapplied' % p.name)
    for p in hidden:
        if p.name not in patchorder.hidden:
            out.info('%s is now hidden' % p.name)
    out.done()

    orig_order = dict((pn, i) for i, pn in enumerate(patchorder.all))

    def patchname_key(p):
        i = orig_order.get(p, len(orig_order))
        return i, p

    trans = StackTransaction(stack,
                             'repair',
                             check_clean_iw=False,
                             allow_bad_head=True)
    try:
        trans.applied = [p.name for p in applied]
        trans.unapplied = sorted((p.name for p in unapplied),
                                 key=patchname_key)
        trans.hidden = sorted((p.name for p in hidden), key=patchname_key)
    except TransactionHalted:
        pass
    return trans.run()
Esempio n. 41
0
def func(parser, options, args):
    """Uncommit a number of patches.
    """
    stack = directory.repository.current_stack
    if options.to:
        if options.number:
            parser.error('cannot give both --to and --number')
        if len(args) != 0:
            parser.error('cannot specify patch name with --to')
        patch_nr = patchnames = None
        to_commit = stack.repository.rev_parse(options.to)
        # check whether the --to commit is on a different branch
        merge_bases = directory.repository.get_merge_bases(to_commit, stack.base)
        if to_commit not in merge_bases:
            to_commit = merge_bases[0]
            options.exclusive = True
    elif options.number:
        if options.number <= 0:
            parser.error('invalid value passed to --number')
        patch_nr = options.number
        if len(args) == 0:
            patchnames = None
        elif len(args) == 1:
            # prefix specified
            patchnames = ['%s%d' % (args[0], i)
                          for i in range(patch_nr, 0, -1)]
        else:
            parser.error('when using --number, specify at most one patch name')
    elif len(args) == 0:
        patchnames = None
        patch_nr = 1
    else:
        patchnames = args
        patch_nr = len(patchnames)

    def check_and_append(c, n):
        next = n.data.parents
        try:
            [next] = next
        except ValueError:
            out.done()
            raise common.CmdException(
                'Trying to uncommit %s, which does not have exactly one parent'
                % n.sha1)
        return c.append(n)

    commits = []
    next_commit = stack.base
    if patch_nr:
        out.start('Uncommitting %d patches' % patch_nr)
        for i in range(patch_nr):
            check_and_append(commits, next_commit)
            next_commit = next_commit.data.parent
    else:
        if options.exclusive:
            out.start('Uncommitting to %s (exclusive)' % to_commit.sha1)
        else:
            out.start('Uncommitting to %s' % to_commit.sha1)
        while True:
            if next_commit == to_commit:
                if not options.exclusive:
                    check_and_append(commits, next_commit)
                break
            check_and_append(commits, next_commit)
            next_commit = next_commit.data.parent
        patch_nr = len(commits)

    taken_names = set(stack.patchorder.all)
    if patchnames:
        for pn in patchnames:
            if pn in taken_names:
                raise common.CmdException('Patch name "%s" already taken' % pn)
            taken_names.add(pn)
    else:
        patchnames = []
        for c in reversed(commits):
            pn = utils.make_patch_name(c.data.message,
                                       lambda pn: pn in taken_names)
            patchnames.append(pn)
            taken_names.add(pn)
        patchnames.reverse()

    trans = transaction.StackTransaction(stack, 'uncommit',
                                         allow_conflicts = True,
                                         allow_bad_head = True)
    for commit, pn in zip(commits, patchnames):
        trans.patches[pn] = commit
    trans.applied = list(reversed(patchnames)) + trans.applied
    trans.run(set_head = False)
    out.done()
Esempio n. 42
0
File: pick.py Progetto: snits/stgit
def __pick_commit(commit_id, patchname, options):
    """Pick a commit id.
    """
    commit = git.Commit(commit_id)

    if options.name:
        patchname = options.name
    elif patchname and options.revert:
        patchname = 'revert-' + patchname
    if patchname:
        patchname = find_patch_name(patchname, crt_series.patch_exists)

    if options.parent:
        parent = git_id(crt_series, options.parent)
    else:
        parent = commit.get_parent()

    if not options.revert:
        bottom = parent
        top = commit_id
    else:
        bottom = commit_id
        top = parent

    if options.fold:
        out.start('Folding commit %s' % commit_id)

        # try a direct git apply first
        if not git.apply_diff(bottom, top, files = options.file):
            if options.file:
                raise CmdException('Patch folding failed')
            else:
                git.merge_recursive(bottom, git.get_head(), top)

        out.done()
    elif options.update:
        rev1 = git_id(crt_series, 'HEAD^')
        rev2 = git_id(crt_series, 'HEAD')
        files = git.barefiles(rev1, rev2).split('\n')

        out.start('Updating with commit %s' % commit_id)

        if not git.apply_diff(bottom, top, files = files):
            raise CmdException('Patch updating failed')

        out.done()
    else:
        message = commit.get_log()
        if options.revert:
            if message:
                lines = message.splitlines()
                subject = lines[0]
                body = '\n'.join(lines[2:])
            else:
                subject = commit.get_id_hash()
                body = ''
            message = 'Revert "%s"\n\nThis reverts commit %s.\n\n%s\n' \
                    % (subject, commit.get_id_hash(), body)
        elif options.expose:
            if not message.endswith('\n'):
                message += '\n'
            message += '(imported from commit %s)\n' % commit.get_id_hash()
        author_name, author_email, author_date = \
                     name_email_date(commit.get_author())
        if options.revert:
            author_name = author_email = None

        out.start('Importing commit %s' % commit_id)

        newpatch = crt_series.new_patch(patchname, message = message, can_edit = False,
                                        unapplied = True, bottom = bottom, top = top,
                                        author_name = author_name,
                                        author_email = author_email,
                                        author_date = author_date)
        # in case the patch name was automatically generated
        patchname = newpatch.get_name()

        # find a patchlog to fork from
        refbranchname, refpatchname = parse_rev(patchname)
        if refpatchname:
            if refbranchname:
                # assume the refseries is OK, since we already resolved
                # commit_str to a git_id
                refseries = Series(refbranchname)
            else:
                refseries = crt_series
            patch = refseries.get_patch(refpatchname)
            if patch.get_log():
                out.info("Log was %s" % newpatch.get_log())
                out.info("Setting log to %s\n" %  patch.get_log())
                newpatch.set_log(patch.get_log())
                out.info("Log is now %s" % newpatch.get_log())
            else:
                out.info("No log for %s\n" % patchname)

        if not options.unapplied:
            modified = crt_series.push_patch(patchname)
        else:
            modified = False

        if crt_series.empty_patch(patchname):
            out.done('empty patch')
        elif modified:
            out.done('modified')
        else:
            out.done()
Esempio n. 43
0
def func(parser, options, args):
    """Repair inconsistencies in StGit metadata."""

    orig_applied = crt_series.get_applied()
    orig_unapplied = crt_series.get_unapplied()
    orig_hidden = crt_series.get_hidden()

    if crt_series.get_protected():
        raise CmdException(
            'This branch is protected. Modification is not permitted.')

    # Find commits that aren't patches, and applied patches.
    head = git.get_commit(git.get_head()).get_id_hash()
    commits, patches = read_commit_dag(crt_series.get_name())
    c = commits[head]
    patchify = []       # commits to definitely patchify
    maybe_patchify = [] # commits to patchify if we find a patch below them
    applied = []
    while len(c.parents) == 1:
        parent, = c.parents
        if c.patch:
            applied.append(c)
            patchify.extend(maybe_patchify)
            maybe_patchify = []
        else:
            maybe_patchify.append(c)
        c = parent
    applied.reverse()
    patchify.reverse()

    # Find patches hidden behind a merge.
    merge = c
    todo = set([c])
    seen = set()
    hidden = set()
    while todo:
        c = todo.pop()
        seen.add(c)
        todo |= c.parents - seen
        if c.patch:
            hidden.add(c)
    if hidden:
        out.warn(('%d patch%s are hidden below the merge commit'
                  % (len(hidden), ['es', ''][len(hidden) == 1])),
                 '%s,' % merge.id, 'and will be considered unapplied.')

    # Make patches of any linear sequence of commits on top of a patch.
    names = set(p.patch for p in patches)
    def name_taken(name):
        return name in names
    if applied and patchify:
        out.start('Creating %d new patch%s'
                  % (len(patchify), ['es', ''][len(patchify) == 1]))
        for p in patchify:
            name = make_patch_name(p.commit.get_log(), name_taken)
            out.info('Creating patch %s from commit %s' % (name, p.id))
            aname, amail, adate = name_email_date(p.commit.get_author())
            cname, cmail, cdate = name_email_date(p.commit.get_committer())
            parent, = p.parents
            crt_series.new_patch(
                name, can_edit = False, commit = False,
                top = p.id, bottom = parent.id, message = p.commit.get_log(),
                author_name = aname, author_email = amail, author_date = adate,
                committer_name = cname, committer_email = cmail)
            p.patch = name
            applied.append(p)
            names.add(name)
        out.done()

    # Figure out hidden
    orig_patches = orig_applied + orig_unapplied + orig_hidden
    orig_applied_name_set = set(orig_applied)
    orig_unapplied_name_set = set(orig_unapplied)
    orig_hidden_name_set = set(orig_hidden)
    orig_patches_name_set = set(orig_patches)
    hidden = [p for p in patches if p.patch in orig_hidden_name_set]

    # Write the applied/unapplied files.
    out.start('Checking patch appliedness')
    unapplied = patches - set(applied) - set(hidden)
    applied_name_set = set(p.patch for p in applied)
    unapplied_name_set = set(p.patch for p in unapplied)
    hidden_name_set = set(p.patch for p in hidden)
    patches_name_set = set(p.patch for p in patches)
    for name in orig_patches_name_set - patches_name_set:
        out.info('%s is gone' % name)
    for name in applied_name_set - orig_applied_name_set:
        out.info('%s is now applied' % name)
    for name in unapplied_name_set - orig_unapplied_name_set:
        out.info('%s is now unapplied' % name)
    for name in hidden_name_set - orig_hidden_name_set:
        out.info('%s is now hidden' % name)
    orig_order = dict(zip(orig_patches, range(len(orig_patches))))

    def patchname_key(p):
        i = orig_order.get(p, len(orig_order))
        return i, p

    crt_series.set_applied(p.patch for p in applied)
    crt_series.set_unapplied(sorted(unapplied_name_set, key=patchname_key))
    crt_series.set_hidden(sorted(hidden_name_set, key=patchname_key))
    out.done()
Esempio n. 44
0
File: sync.py Progetto: snits/stgit
def func(parser, options, args):
    """Synchronise a range of patches
    """
    if options.ref_branch:
        remote_series = stack.Series(options.ref_branch)
        if options.ref_branch == crt_series.get_name():
            raise CmdException('Cannot synchronise with the current branch')
        remote_patches = remote_series.get_applied()

        # the merge function merge_patch(patch, pname)
        merge_patch = lambda patch, pname: \
                      __branch_merge_patch(remote_series, pname)
    elif options.series:
        patchdir = os.path.dirname(options.series)

        remote_patches = []
        with open(options.series) as f:
            for line in f:
                p = re.sub('#.*$', '', line).strip()
                if not p:
                    continue
                remote_patches.append(p)

        # the merge function merge_patch(patch, pname)
        merge_patch = lambda patch, pname: \
                      __series_merge_patch(patch.get_bottom(), patchdir, pname)
    else:
        raise CmdException('No remote branch or series specified')

    applied = crt_series.get_applied()
    unapplied = crt_series.get_unapplied()
    
    if options.all:
        patches = applied
    elif len(args) != 0:
        patches = parse_patches(args, applied + unapplied, len(applied),
                                ordered = True)
    elif applied:
        patches = [crt_series.get_current()]
    else:
        parser.error('no patches applied')

    if not patches:
        raise CmdException('No patches to synchronise')

    __check_all()

    # only keep the patches to be synchronised
    sync_patches = [p for p in patches if p in remote_patches]
    if not sync_patches:
        raise CmdException('No common patches to be synchronised')

    # pop to the one before the first patch to be synchronised
    first_patch = sync_patches[0]
    if first_patch in applied:
        to_pop = applied[applied.index(first_patch) + 1:]
        if to_pop:
            pop_patches(crt_series, to_pop[::-1])
        pushed = [first_patch]
    else:
        to_pop = []
        pushed = []
    popped = to_pop + [p for p in patches if p in unapplied]

    for p in pushed + popped:
        if p in popped:
            # push this patch
            push_patches(crt_series, [p])
        if p not in sync_patches:
            # nothing to synchronise
            continue

        # the actual sync
        out.start('Synchronising "%s"' % p)

        patch = crt_series.get_patch(p)
        top = patch.get_top()

        # reset the patch backup information.
        patch.set_top(top, backup = True)

        # the actual merging (either from a branch or an external file)
        merge_patch(patch, p)

        if git.local_changes(verbose = False):
            # index (cache) already updated by the git merge. The
            # backup information was already reset above
            crt_series.refresh_patch(cache_update = False, backup = False,
                                     log = 'sync')
            out.done('updated')
        else:
            out.done()
Esempio n. 45
0
File: mail.py Progetto: snits/stgit
def func(parser, options, args):
    """Send the patches by e-mail using the patchmail.tmpl file as
    a template
    """
    applied = crt_series.get_applied()

    if options.all:
        patches = applied
    elif len(args) >= 1:
        unapplied = crt_series.get_unapplied()
        patches = parse_patches(args, applied + unapplied, len(applied))
    else:
        raise CmdException('Incorrect options. Unknown patches to send')

    # early test for sender identity
    __get_sender()

    out.start('Checking the validity of the patches')
    for p in patches:
        if crt_series.empty_patch(p):
            raise CmdException('Cannot send empty patch "%s"' % p)
    out.done()

    total_nr = len(patches)
    if total_nr == 0:
        raise CmdException('No patches to send')

    if options.in_reply_to:
        if options.no_thread or options.unrelated:
            raise CmdException('--in-reply-to option not allowed with '
                               '--no-thread or --unrelated')
        ref_id = options.in_reply_to
    else:
        ref_id = None

    # get username/password if sending by SMTP
    __set_smtp_credentials(options)

    # send the cover message (if any)
    if options.cover or options.edit_cover:
        if options.unrelated:
            raise CmdException('cover sending not allowed with --unrelated')

        # find the template file
        if options.cover:
            with open(options.cover) as f:
                tmpl = f.read()
        else:
            tmpl = templates.get_template('covermail.tmpl')
            if not tmpl:
                raise CmdException('No cover message template file found')

        msg_id = __send_message('cover', tmpl, options, patches)

        # subsequent e-mails are seen as replies to the first one
        if not options.no_thread:
            ref_id = msg_id

    # send the patches
    if options.template:
        with open(options.template) as f:
            tmpl = f.read()
    else:
        if options.attach:
            tmpl = templates.get_template('mailattch.tmpl')
        elif options.attach_inline:
            tmpl = templates.get_template('patchandattch.tmpl')
        else:
            tmpl = templates.get_template('patchmail.tmpl')
        if not tmpl:
            raise CmdException('No e-mail template file found')

    for (p, n) in zip(patches, range(1, total_nr + 1)):
        msg_id = __send_message('patch', tmpl, options, p, n, total_nr, ref_id)

        # subsequent e-mails are seen as replies to the first one
        if not options.no_thread and not options.unrelated and not ref_id:
            ref_id = msg_id
Esempio n. 46
0
def func(parser, options, args):
    """Repair inconsistencies in StGit metadata."""

    orig_applied = crt_series.get_applied()
    orig_unapplied = crt_series.get_unapplied()
    orig_hidden = crt_series.get_hidden()

    if crt_series.get_protected():
        raise CmdException(
            'This branch is protected. Modification is not permitted.')

    # Find commits that aren't patches, and applied patches.
    head = git.get_commit(git.get_head()).get_id_hash()
    commits, patches = read_commit_dag(crt_series.get_name())
    c = commits[head]
    patchify = []  # commits to definitely patchify
    maybe_patchify = []  # commits to patchify if we find a patch below them
    applied = []
    while len(c.parents) == 1:
        parent, = c.parents
        if c.patch:
            applied.append(c)
            patchify.extend(maybe_patchify)
            maybe_patchify = []
        else:
            maybe_patchify.append(c)
        c = parent
    applied.reverse()
    patchify.reverse()

    # Find patches hidden behind a merge.
    merge = c
    todo = set([c])
    seen = set()
    hidden = set()
    while todo:
        c = todo.pop()
        seen.add(c)
        todo |= c.parents - seen
        if c.patch:
            hidden.add(c)
    if hidden:
        out.warn(('%d patch%s are hidden below the merge commit' %
                  (len(hidden), ['es', ''][len(hidden) == 1])),
                 '%s,' % merge.id, 'and will be considered unapplied.')

    # Make patches of any linear sequence of commits on top of a patch.
    names = set(p.patch for p in patches)

    def name_taken(name):
        return name in names

    if applied and patchify:
        out.start('Creating %d new patch%s' %
                  (len(patchify), ['es', ''][len(patchify) == 1]))
        for p in patchify:
            name = make_patch_name(p.commit.get_log(), name_taken)
            out.info('Creating patch %s from commit %s' % (name, p.id))
            aname, amail, adate = name_email_date(p.commit.get_author())
            cname, cmail, cdate = name_email_date(p.commit.get_committer())
            parent, = p.parents
            crt_series.new_patch(
                name,
                can_edit=False,
                commit=False,
                top=p.id,
                bottom=parent.id,
                message=p.commit.get_log(),
                author_name=aname,
                author_email=amail,
                author_date=adate,
                committer_name=cname,
                committer_email=cmail,
            )
            p.patch = name
            applied.append(p)
            names.add(name)
        out.done()

    # Figure out hidden
    orig_patches = orig_applied + orig_unapplied + orig_hidden
    orig_applied_name_set = set(orig_applied)
    orig_unapplied_name_set = set(orig_unapplied)
    orig_hidden_name_set = set(orig_hidden)
    orig_patches_name_set = set(orig_patches)
    hidden = [p for p in patches if p.patch in orig_hidden_name_set]

    # Write the applied/unapplied files.
    out.start('Checking patch appliedness')
    unapplied = patches - set(applied) - set(hidden)
    applied_name_set = set(p.patch for p in applied)
    unapplied_name_set = set(p.patch for p in unapplied)
    hidden_name_set = set(p.patch for p in hidden)
    patches_name_set = set(p.patch for p in patches)
    for name in orig_patches_name_set - patches_name_set:
        out.info('%s is gone' % name)
    for name in applied_name_set - orig_applied_name_set:
        out.info('%s is now applied' % name)
    for name in unapplied_name_set - orig_unapplied_name_set:
        out.info('%s is now unapplied' % name)
    for name in hidden_name_set - orig_hidden_name_set:
        out.info('%s is now hidden' % name)
    orig_order = dict(zip(orig_patches, range(len(orig_patches))))

    def patchname_key(p):
        i = orig_order.get(p, len(orig_order))
        return i, p

    crt_series.set_applied(p.patch for p in applied)
    crt_series.set_unapplied(sorted(unapplied_name_set, key=patchname_key))
    crt_series.set_hidden(sorted(hidden_name_set, key=patchname_key))
    out.done()
Esempio n. 47
0
def func(parser, options, args):

    if options.create:

        if len(args) == 0 or len(args) > 2:
            parser.error('incorrect number of arguments')

        check_local_changes()
        check_conflicts()
        check_head_top_equal(crt_series)

        tree_id = None
        if len(args) >= 2:
            parentbranch = None
            try:
                branchpoint = git.rev_parse(args[1])

                # parent branch?
                head_re = re.compile('refs/(heads|remotes)/')
                ref_re = re.compile(args[1] + '$')
                for ref in git.all_refs():
                    if head_re.match(ref) and ref_re.search(ref):
                        # args[1] is a valid ref from the branchpoint
                        # setting above
                        parentbranch = args[1]
                        break
            except git.GitException:
                # should use a more specific exception to catch only
                # non-git refs ?
                out.info('Don\'t know how to determine parent branch'
                         ' from "%s"' % args[1])
                # exception in branch = rev_parse() leaves branchpoint unbound
                branchpoint = None

            tree_id = git_id(crt_series, branchpoint or args[1])

            if parentbranch:
                out.info('Recording "%s" as parent branch' % parentbranch)
            else:
                out.info('Don\'t know how to determine parent branch'
                         ' from "%s"' % args[1])                
        else:
            # branch stack off current branch
            parentbranch = git.get_head_file()

        if parentbranch:
            parentremote = git.identify_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.Series(args[0]).init(create_at = tree_id,
                                   parent_remote = parentremote,
                                   parent_branch = parentbranch)

        out.info('Branch "%s" created' % args[0])
        log.compat_log_entry('branch --create')
        return

    elif options.clone:

        if len(args) == 0:
            clone = crt_series.get_name() + \
                    time.strftime('-%C%y%m%d-%H%M%S')
        elif len(args) == 1:
            clone = args[0]
        else:
            parser.error('incorrect number of arguments')

        check_local_changes()
        check_conflicts()
        check_head_top_equal(crt_series)

        out.start('Cloning current branch to "%s"' % clone)
        crt_series.clone(clone)
        out.done()

        log.copy_log(log.default_repo(), crt_series.get_name(), clone,
                     'branch --clone')
        return

    elif options.delete:

        if len(args) != 1:
            parser.error('incorrect number of arguments')
        __delete_branch(args[0], options.force)
        log.delete_log(log.default_repo(), args[0])
        return

    elif options.cleanup:

        if not args:
            name = crt_series.get_name()
        elif len(args) == 1:
            name = args[0]
        else:
            parser.error('incorrect number of arguments')
        __cleanup_branch(name, options.force)
        log.delete_log(log.default_repo(), name)
        return

    elif options.list:

        if len(args) != 0:
            parser.error('incorrect number of arguments')

        branches = set(git.get_heads())
        for br in set(branches):
            m = re.match(r'^(.*)\.stgit$', br)
            if m and m.group(1) in branches:
                branches.remove(br)

        if branches:
            out.info('Available branches:')
            max_len = max([len(i) for i in branches])
            for i in sorted(branches):
                __print_branch(i, max_len)
        else:
            out.info('No branches')
        return

    elif options.protect:

        if len(args) == 0:
            branch_name = crt_series.get_name()
        elif len(args) == 1:
            branch_name = args[0]
        else:
            parser.error('incorrect number of arguments')
        branch = stack.Series(branch_name)

        if not branch.is_initialised():
            raise CmdException('Branch "%s" is not controlled by StGIT' %
                               branch_name)

        out.start('Protecting branch "%s"' % branch_name)
        branch.protect()
        out.done()

        return

    elif options.rename:

        if len(args) != 2:
            parser.error('incorrect number of arguments')

        if __is_current_branch(args[0]):
            raise CmdException('Renaming the current branch is not supported')

        stack.Series(args[0]).rename(args[1])

        out.info('Renamed branch "%s" to "%s"' % (args[0], args[1]))
        log.rename_log(log.default_repo(), args[0], args[1], 'branch --rename')
        return

    elif options.unprotect:

        if len(args) == 0:
            branch_name = crt_series.get_name()
        elif len(args) == 1:
            branch_name = args[0]
        else:
            parser.error('incorrect number of arguments')
        branch = stack.Series(branch_name)

        if not branch.is_initialised():
            raise CmdException('Branch "%s" is not controlled by StGIT' %
                               branch_name)

        out.info('Unprotecting branch "%s"' % branch_name)
        branch.unprotect()
        out.done()

        return

    elif options.description is not None:

        if len(args) == 0:
            branch_name = crt_series.get_name()
        elif len(args) == 1:
            branch_name = args[0]
        else:
            parser.error('incorrect number of arguments')
        branch = stack.Series(branch_name)

        if not branch.is_initialised():
            raise CmdException('Branch "%s" is not controlled by StGIT' %
                               branch_name)

        branch.set_description(options.description)

        return

    elif len(args) == 1:

        if __is_current_branch(args[0]):
            raise CmdException('Branch "%s" is already the current branch' %
                               args[0])

        if not options.merge:
            check_local_changes()
        check_conflicts()
        check_head_top_equal(crt_series)

        out.start('Switching to branch "%s"' % args[0])
        git.switch_branch(args[0])
        out.done()
        return

    # default action: print the current branch
    if len(args) != 0:
        parser.error('incorrect number of arguments')

    print(crt_series.get_name())
Esempio n. 48
0
def __pick_commit(stack, ref_stack, iw, commit, patchname, options):
    """Pick a commit."""
    repository = stack.repository

    if options.name:
        patchname = options.name
    elif patchname and options.revert:
        patchname = 'revert-' + patchname

    if patchname:
        patchname = find_patch_name(patchname, stack.patches.exists)
    else:
        patchname = make_patch_name(commit.data.message_str,
                                    stack.patches.exists)

    if options.parent:
        parent = git_commit(options.parent, repository, ref_stack.name)
    else:
        parent = commit.data.parent

    if not options.revert:
        bottom = parent
        top = commit
    else:
        bottom = commit
        top = parent

    if options.fold:
        out.start('Folding commit %s' % commit.sha1)

        diff = repository.diff_tree(bottom.data.tree,
                                    top.data.tree,
                                    pathlimits=options.file)

        if diff:
            try:
                # try a direct git apply first
                iw.apply(diff, quiet=True)
            except MergeException:
                if options.file:
                    out.done('conflict(s)')
                    out.error('%s does not apply cleanly' % patchname)
                    return STGIT_CONFLICT
                else:
                    try:
                        iw.merge(
                            bottom.data.tree,
                            stack.head.data.tree,
                            top.data.tree,
                        )
                    except MergeConflictException as e:
                        out.done('%s conflicts' % len(e.conflicts))
                        out.error('%s does not apply cleanly' % patchname,
                                  *e.conflicts)
                        return STGIT_CONFLICT
            out.done()
        else:
            out.done('no changes')
        return STGIT_SUCCESS
    elif options.update:
        files = [
            fn1 for _, _, _, _, _, fn1, fn2 in repository.diff_tree_files(
                stack.top.data.parent.data.tree, stack.top.data.tree)
        ]

        diff = repository.diff_tree(bottom.data.tree,
                                    top.data.tree,
                                    pathlimits=files)

        out.start('Updating with commit %s' % commit.sha1)

        try:
            iw.apply(diff, quiet=True)
        except MergeException:
            out.done('conflict(s)')
            out.error('%s does not apply cleanly' % patchname)
            return STGIT_CONFLICT
        else:
            out.done()
            return STGIT_SUCCESS
    else:
        author = commit.data.author
        message = commit.data.message_str

        if options.revert:
            author = Person.author()
            if message:
                lines = message.splitlines()
                subject = lines[0]
                body = '\n'.join(lines[2:])
            else:
                subject = commit.sha1
                body = ''
            message = 'Revert "%s"\n\nThis reverts commit %s.\n\n%s\n' % (
                subject,
                commit.sha1,
                body,
            )
        elif options.expose:
            fmt = config.get('stgit.pick.expose-format')
            message = Run('git', 'show', '--no-patch', '--pretty=' + fmt,
                          commit.sha1).raw_output()
            message = message.rstrip() + '\n'

        out.start('Importing commit %s' % commit.sha1)

        new_commit = repository.commit(
            CommitData(
                tree=top.data.tree,
                parents=[bottom],
                message=message,
                author=author,
            ))

        trans = StackTransaction(
            stack, 'pick %s from %s' % (patchname, ref_stack.name))
        trans.patches[patchname] = new_commit

        trans.unapplied.append(patchname)
        if not options.unapplied:
            try:
                trans.push_patch(patchname, iw, allow_interactive=True)
            except TransactionHalted:
                pass

        retval = trans.run(iw, print_current_patch=False)

        if retval == STGIT_CONFLICT:
            out.done('conflict(s)')
        elif stack.patches.get(patchname).is_empty():
            out.done('empty patch')
        else:
            out.done()

        return retval
Esempio n. 49
0
def func(parser, options, args):
    """Send the patches by e-mail using the patchmail.tmpl file as
    a template
    """
    stack = directory.repository.current_stack
    applied = stack.patchorder.applied

    if options.all:
        patches = applied
    elif len(args) >= 1:
        unapplied = stack.patchorder.unapplied
        patches = parse_patches(args, applied + unapplied, len(applied))
    else:
        raise CmdException('Incorrect options. Unknown patches to send')

    # early test for sender identity
    __get_sender()

    out.start('Checking the validity of the patches')
    for p in patches:
        if stack.patches.get(p).is_empty():
            raise CmdException('Cannot send empty patch "%s"' % p)
    out.done()

    total_nr = len(patches)
    if total_nr == 0:
        raise CmdException('No patches to send')

    if options.in_reply_to:
        if options.no_thread or options.unrelated:
            raise CmdException('--in-reply-to option not allowed with '
                               '--no-thread or --unrelated')
        ref_id = options.in_reply_to
    else:
        ref_id = None

    # get username/password if sending by SMTP
    __set_smtp_credentials(options)

    # send the cover message (if any)
    if options.cover or options.edit_cover:
        if options.unrelated:
            raise CmdException('cover sending not allowed with --unrelated')

        # find the template file
        if options.cover:
            with io.open(options.cover, 'r') as f:
                tmpl = f.read()
        else:
            tmpl = templates.get_template('covermail.tmpl')
            if not tmpl:
                raise CmdException('No cover message template file found')

        msg_id = __send_message('cover', tmpl, options, patches)

        # subsequent e-mails are seen as replies to the first one
        if not options.no_thread:
            ref_id = msg_id

    # send the patches
    if options.template:
        with io.open(options.template, 'r') as f:
            tmpl = f.read()
    else:
        if options.attach:
            tmpl = templates.get_template('mailattch.tmpl')
        elif options.attach_inline:
            tmpl = templates.get_template('patchandattch.tmpl')
        else:
            tmpl = templates.get_template('patchmail.tmpl')
        if not tmpl:
            raise CmdException('No e-mail template file found')

    for (p, n) in zip(patches, range(1, total_nr + 1)):
        msg_id = __send_message('patch', tmpl, options, p, n, total_nr, ref_id)

        # subsequent e-mails are seen as replies to the first one
        if not options.no_thread and not options.unrelated and not ref_id:
            ref_id = msg_id