Example #1
0
def __import_mail_path(mail_path, filename, options):
    with open(mail_path, 'rb') as f:
        mail = f.read()

    msg_path = mail_path + '-msg'
    patch_path = mail_path + '-patch'

    mailinfo_cmd = ['git', 'mailinfo']
    if options.message_id or config.getbool('stgit.import.message-id'):
        mailinfo_cmd.append('--message-id')
    mailinfo_cmd.extend([msg_path, patch_path])

    mailinfo_lines = (Run(*mailinfo_cmd).encoding(None).decoding(
        None).raw_input(mail).output_lines(b'\n'))

    mailinfo = dict(line.split(b': ', 1) for line in mailinfo_lines if line)

    with open(msg_path, 'rb') as f:
        msg_body = f.read()

    msg_bytes = mailinfo[b'Subject'] + b'\n\n' + msg_body

    with open(patch_path, 'rb') as f:
        diff = f.read()

    __create_patch(
        None if options.mbox else filename,
        decode_utf8_with_latin1(msg_bytes),
        None,
        mailinfo[b'Author'].decode('utf-8'),
        mailinfo[b'Email'].decode('utf-8'),
        mailinfo[b'Date'].decode('utf-8'),
        diff,
        options,
    )
Example #2
0
def func(parser, options, args):
    """Create a new patch."""
    stack = directory.repository.current_stack
    if stack.repository.default_index.conflicts():
        raise CmdException(
            'Cannot create a new patch -- resolve conflicts first')

    # Choose a name for the new patch -- or None, which means make one
    # up later when we've gotten hold of the commit message.
    if len(args) == 0:
        name = None
    elif len(args) == 1:
        name = args[0]
        if not stack.patches.is_name_valid(name):
            raise CmdException('Invalid patch name: "%s"' % name)
        elif name in stack.patches:
            raise CmdException('%s: patch already exists' % name)
    else:
        parser.error('incorrect number of arguments')

    if options.verbose:
        verbose = options.verbose
    else:
        verbose = config.getbool('stgit.new.verbose') or False

    cd = CommitData(
        tree=stack.head.data.tree,
        parents=[stack.head],
        message='',
        author=Person.author(),
        committer=Person.committer(),
    )
    cd = update_commit_data(
        stack.repository,
        cd,
        message=options.message,
        author=options.author(cd.author),
        trailers=options.trailers,
        edit=(not options.save_template and options.message is None),
        verbose=verbose,
    )

    if options.save_template:
        options.save_template(cd.message)
        return utils.STGIT_SUCCESS

    if not options.no_verify:
        cd = run_commit_msg_hook(stack.repository, cd)

    if name is None:
        name = stack.patches.make_name(cd.message_str)

    # Write the new patch.
    check_head_top_equal(stack)
    trans = StackTransaction(stack)
    trans.patches[name] = stack.repository.commit(cd)
    trans.applied.append(name)
    return trans.execute('new: %s' % name)
Example #3
0
def func(parser, options, args):
    """Generate a new commit for the current or given patch."""

    if options.spill:
        if (
            len(args) > 0
            or options.index
            or options.edit
            or options.update
            or options.patch
            or options.force
            or options.no_verify
            or options.sign_str
        ):
            raise CmdException('--spill option does not take any arguments or options')
        return __refresh_spill(annotate=options.annotate)
    else:
        # Catch illegal argument combinations.
        is_path_limiting = bool(args or options.update)
        if options.index and is_path_limiting:
            raise CmdException('Only full refresh is available with the --index option')

        if options.index and options.force:
            raise CmdException(
                'You cannot --force a full refresh when using --index mode'
            )

        if options.update and options.submodules:
            raise CmdException(
                '--submodules is meaningless when only updating modified files'
            )

        if options.index and options.submodules:
            raise CmdException(
                '--submodules is meaningless when keeping the current index'
            )

        # If submodules was not specified on the command line, infer a default
        # from configuration.
        if options.submodules is None:
            options.submodules = config.getbool('stgit.refreshsubmodules')

        return __refresh(
            args,
            force=options.force,
            target_patch=options.patch,
            message=options.message,
            author=options.author,
            sign_str=options.sign_str,
            annotate=options.annotate,
            use_temp_index=is_path_limiting,
            refresh_from_index=options.index,
            only_update_patchfiles=options.update,
            include_submodules=options.submodules,
            no_verify=options.no_verify,
            invoke_editor=options.edit,
        )
Example #4
0
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.getbool('stgit.autoimerge'):
            mergetool()
        else:
            conflicts = [l for l in output if l.startswith('CONFLICT')]
            out.info(*conflicts)
            raise GitException("%d conflict(s)" % len(conflicts))
Example #5
0
    def commit(self, repository):
        """Commit the commit.

        :returns: the committed commit
        :rtype: :class:`Commit`

        """
        c = ['git', 'commit-tree', self.tree.sha1]
        for p in self.parents:
            c.append('-p')
            c.append(p.sha1)

        if config.getbool("commit.gpgsign"):
            c.append('-S')

        sha1 = (
            repository.run(c, env=self.env)
            .encoding(None)
            .raw_input(self.message)
            .output_one_line()
        )
        return repository.get_commit(sha1)
Example #6
0
def func(parser, options, args):
    """Generate a new commit for the current or given patch."""

    # Catch illegal argument combinations.
    path_limiting = bool(args or options.update)
    if options.index and path_limiting:
        raise common.CmdException(
            'Only full refresh is available with the --index option')

    if options.index and options.force:
        raise common.CmdException(
            'You cannot --force a full refresh when using --index mode')

    if options.update and options.submodules:
        raise common.CmdException(
            '--submodules is meaningless when only updating modified files')

    if options.index and options.submodules:
        raise common.CmdException(
            '--submodules is meaningless when keeping the current index')

    # If submodules was not specified on the command line, infer a default
    # from configuration.
    if options.submodules is None:
        options.submodules = (config.getbool('stgit.refreshsubmodules'))

    stack = directory.repository.current_stack
    patch_name = get_patch(stack, options.patch)
    paths = list_files(
        stack,
        patch_name,
        args,
        options.index,
        options.update,
        options.submodules,
    )

    # Make sure there are no conflicts in the files we want to
    # refresh.
    if stack.repository.default_index.conflicts() & paths:
        raise common.CmdException('Cannot refresh -- resolve conflicts first')

    # Make sure the index is clean before performing a full refresh
    if not options.index and not options.force:
        if not (stack.repository.default_index.is_clean(stack.head)
                or stack.repository.default_iw.worktree_clean()):
            raise common.CmdException(
                'The index is dirty. Did you mean --index? '
                'To force a full refresh use --force.')

    # Commit index to temp patch, and absorb it into the target patch.
    retval, temp_name = make_temp_patch(stack,
                                        patch_name,
                                        paths,
                                        temp_index=path_limiting)
    if retval != utils.STGIT_SUCCESS:
        return retval

    def edit_fun(cd):
        orig_msg = cd.message
        cd, failed_diff = edit.auto_edit_patch(
            stack.repository,
            cd,
            msg=(None if options.message is None else
                 options.message.encode('utf-8')),
            contains_diff=False,
            author=options.author,
            committer=lambda p: p,
            sign_str=options.sign_str)
        assert not failed_diff
        if options.edit:
            cd, failed_diff = edit.interactive_edit_patch(
                stack.repository,
                cd,
                edit_diff=False,
                diff_flags=[],
                replacement_diff=None,
            )
            assert not failed_diff
        if not options.no_verify and (options.edit or cd.message != orig_msg):
            cd = common.run_commit_msg_hook(stack.repository, cd, options.edit)
        return cd

    return absorb(stack,
                  patch_name,
                  temp_name,
                  edit_fun,
                  annotate=options.annotate)
Example #7
0
 def protected(self):
     return config.getbool('branch.%s.stgit.protect' % self.name)
Example #8
0
def func(parser, options, args):
    """Pull the changes from a remote repository"""
    repository = directory.repository
    iw = repository.default_iw
    stack = repository.get_stack()
    policy = config.get('branch.%s.stgit.pull-policy' %
                        stack.name) or config.get('stgit.pull-policy')

    if policy not in ['pull', 'fetch-rebase', 'rebase']:
        raise GitConfigException('Unsupported pull-policy "%s"' % policy)

    remote_name = None
    if policy == 'rebase':
        # parent is local
        if len(args) == 1:
            parser.error(
                'specifying a repository is meaningless for policy="%s"' %
                (policy, ))
        elif len(args) > 0:
            parser.error('incorrect number of arguments')
    else:
        # parent is remote
        if len(args) > 1:
            parser.error('incorrect number of arguments')

        if len(args) >= 1:
            remote_name = args[0]
        else:
            remote_name = stack.parent_remote

    if policy in ['pull', 'fetch-rebase'] and remote_name is None:
        parser.error(
            'There is no tracking information for the current branch.\n'
            'Please specify the remote repository to pull from.')

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

    applied = stack.patchorder.applied

    retval = prepare_rebase(stack, 'pull')
    if retval:
        return retval

    # pull the remote changes
    if policy == 'pull':
        out.info('Pulling from "%s"' % remote_name)
        pull(repository, remote_name)
    elif policy == 'fetch-rebase':
        out.info('Fetching from "%s"' % remote_name)
        fetch(repository, remote_name)
        try:
            target = repository.rev_parse('FETCH_HEAD')
        except RepositoryException:
            out.error('Could not find the remote head to rebase onto - '
                      'fix branch.%s.merge in .git/config' % stack.name)
            out.error('Pushing any patches back...')
            post_rebase(stack, applied, 'pull', check_merged=False)
            raise

        rebase(stack, iw, target)
    elif policy == 'rebase':
        value = config.get('branch.%s.stgit.parentbranch' % stack.name)
        if value:
            parent_commit = git_commit(value, repository)
        else:
            try:
                parent_commit = repository.rev_parse('heads/origin')
            except RepositoryException:
                raise CmdException('Cannot find a parent branch for "%s"' %
                                   stack.name)
            else:
                out.warn(
                    'No parent branch declared for stack "%s", defaulting to'
                    '"heads/origin".' % stack.name,
                    'Consider setting "branch.%s.stgit.parentbranch" with '
                    '"git config".' % stack.name,
                )
        rebase(stack, iw, parent_commit)

    if not options.nopush:
        post_rebase(stack, applied, 'pull', check_merged=options.merged)

    # maybe tidy up
    if config.getbool('stgit.keepoptimized'):
        repository.repack()
Example #9
0
def func(parser, options, args):
    """Pull the changes from a remote repository
    """
    policy = config.get('branch.%s.stgit.pull-policy' % crt_series.get_name()) or \
             config.get('stgit.pull-policy')

    if policy == 'rebase':
        # parent is local
        if len(args) == 1:
            parser.error(
                'specifying a repository is meaningless for policy="%s"' %
                policy)
        if len(args) > 0:
            parser.error('incorrect number of arguments')

    else:
        # parent is remote
        if len(args) > 1:
            parser.error('incorrect number of arguments')

        if len(args) >= 1:
            repository = args[0]
        else:
            repository = crt_series.get_parent_remote()

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

    check_local_changes()
    check_conflicts()
    check_head_top_equal(crt_series)

    if policy not in ['pull', 'fetch-rebase', 'rebase']:
        raise GitConfigException('Unsupported pull-policy "%s"' % policy)

    applied = prepare_rebase(crt_series)

    # pull the remote changes
    if policy == 'pull':
        out.info('Pulling from "%s"' % repository)
        git.pull(repository)
    elif policy == 'fetch-rebase':
        out.info('Fetching from "%s"' % repository)
        git.fetch(repository)
        try:
            target = git.fetch_head()
        except git.GitException:
            out.error(
                'Could not find the remote head to rebase onto - fix branch.%s.merge in .git/config'
                % crt_series.get_name())
            out.error('Pushing any patches back...')
            post_rebase(crt_series, applied, False, False)
            raise

        rebase(crt_series, target)
    elif policy == 'rebase':
        rebase(crt_series, crt_series.get_parent_branch())

    post_rebase(crt_series, applied, options.nopush, options.merged)

    # maybe tidy up
    if config.getbool('stgit.keepoptimized'):
        git.repack()

    print_crt_patch(crt_series)
Example #10
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 is None:
                self._halt('%s does not apply cleanly' % pn)
            try:
                self._checkout(ours, iw, allow_bad_head=False)
            except CheckoutException:
                self._halt('Index/worktree dirty')
            try:
                interactive = allow_interactive and config.getbool(
                    'stgit.autoimerge')
                iw.merge(base, ours, theirs, interactive=interactive)
                tree = iw.index.write_tree()
                self._current_tree = tree
                s = 'modified'
            except MergeConflictException as e:
                tree = ours
                merge_conflict = True
                self._conflicts = e.conflicts
                s = 'conflict'
            except MergeException as e:
                self._halt(str(e))
        cd = cd.set_tree(tree)
        if any(
                getattr(cd, a) != getattr(orig_cd, a)
                for a in ['parent', 'tree', 'author', 'message']):
            comm = self.stack.repository.commit(cd)
            if merge_conflict:
                # When we produce a conflict, we'll run the update()
                # function defined below _after_ having done the
                # checkout in run(). To make sure that we check out
                # the real stack top (as it will look after update()
                # has been run), set it hard here.
                self.head = comm
        else:
            comm = None
            s = 'unmodified'
        if already_merged:
            s = 'merged'
        elif not merge_conflict and cd.is_nochange():
            s = 'empty'
        out.done(s)

        if merge_conflict:
            # We've just caused conflicts, so we must allow them in
            # the final checkout.
            self._allow_conflicts = lambda trans: True

        # Update the stack state
        if comm:
            self.patches[pn] = comm
        if pn in self.hidden:
            x = self.hidden
        else:
            x = self.unapplied
        del x[x.index(pn)]
        self.applied.append(pn)

        if merge_conflict:
            self._halt("%d merge conflict(s)" % len(self._conflicts))
Example #11
0
def func(parser, options, args):
    """Generate a new commit for the current or given patch."""

    # Catch illegal argument combinations.
    path_limiting = bool(args or options.update)
    if options.index and path_limiting:
        raise CmdException(
            'Only full refresh is available with the --index option')

    if options.index and options.force:
        raise CmdException(
            'You cannot --force a full refresh when using --index mode')

    if options.update and options.submodules:
        raise CmdException(
            '--submodules is meaningless when only updating modified files')

    if options.index and options.submodules:
        raise CmdException(
            '--submodules is meaningless when keeping the current index')

    # If submodules was not specified on the command line, infer a default
    # from configuration.
    if options.submodules is None:
        options.submodules = config.getbool('stgit.refreshsubmodules')

    stack = directory.repository.current_stack
    patch_name = get_patch(stack, options.patch)
    paths = list_files(
        stack,
        patch_name,
        args,
        options.index,
        options.update,
        options.submodules,
    )

    # Make sure there are no conflicts in the files we want to
    # refresh.
    if stack.repository.default_index.conflicts() & paths:
        raise CmdException('Cannot refresh -- resolve conflicts first')

    # Make sure the index is clean before performing a full refresh
    if not options.index and not options.force:
        if not (stack.repository.default_index.is_clean(stack.head)
                or stack.repository.default_iw.worktree_clean()):
            raise CmdException('The index is dirty. Did you mean --index? '
                               'To force a full refresh use --force.')

    # Update index and write tree
    tree = write_tree(stack, paths, temp_index=path_limiting)

    # Run pre-commit hook, if fails, abort refresh
    if not options.no_verify:
        pre_commit_hook = get_hook(
            stack.repository,
            'pre-commit',
            extra_env={} if options.edit else {'GIT_EDITOR': ':'},
        )
        if pre_commit_hook:
            try:
                pre_commit_hook()
            except RunException:
                raise CmdException(
                    'pre-commit hook failed, review the changes using `stg diff`, '
                    'run `stg add` to add them to index and run `stg refresh` again'
                )
            else:
                # Update index and rewrite tree if hook updated files in index
                if not stack.repository.default_index.is_clean(tree):
                    tree = write_tree(stack, paths, temp_index=path_limiting)

    # Commit tree to temp patch, and absorb it into the target patch.
    retval, temp_name = make_temp_patch(stack, patch_name, tree)

    if retval != utils.STGIT_SUCCESS:
        return retval

    def edit_fun(cd):
        orig_msg = cd.message
        cd = auto_edit_patch(
            stack.repository,
            cd,
            msg=(None if options.message is None else options.message.encode(
                config.get('i18n.commitencoding'))),
            author=options.author,
            sign_str=options.sign_str,
        )
        if options.edit:
            cd, failed_diff = interactive_edit_patch(stack.repository,
                                                     cd,
                                                     edit_diff=False,
                                                     diff_flags=[])
            assert not failed_diff
        if not options.no_verify and (options.edit or cd.message != orig_msg):
            cd = run_commit_msg_hook(stack.repository, cd, options.edit)
        # Refresh the committer information
        return cd.set_committer(None)

    return absorb(stack,
                  patch_name,
                  temp_name,
                  edit_fun,
                  annotate=options.annotate)
Example #12
0
def func(parser, options, args):
    """Generate a new commit for the current or given patch."""

    if options.spill:
        if len(args) > 0:
            # TODO: would be nice if path limiting could be used with spill.
            raise CmdException('Cannot use path limiting with --spill')
        for opt_name, opt_value in [
            ('--index', options.index),
            ('--edit', options.edit),
            ('--update', options.update),
            ('--patch', options.patch),
            ('--force', options.force),
            ('--no-verify', options.no_verify),
            ('--sign', options.trailers),
            ('--ack', options.trailers),
            ('--review', options.trailers),
        ]:
            if opt_value:
                raise CmdException('Cannot combine --spill with %s' % opt_name)
        return __refresh_spill(annotate=options.annotate)
    else:
        # Catch illegal argument combinations.
        is_path_limiting = bool(args or options.update)
        if options.index and is_path_limiting:
            raise CmdException('Only full refresh is available with the --index option')

        if options.index and options.force:
            raise CmdException(
                'You cannot --force a full refresh when using --index mode'
            )

        if options.update and options.submodules:
            raise CmdException(
                '--submodules is meaningless when only updating modified files'
            )

        if options.index and options.submodules:
            raise CmdException(
                '--submodules is meaningless when keeping the current index'
            )

        # If submodules was not specified on the command line, infer a default
        # from configuration.
        if options.submodules is None:
            options.submodules = config.getbool('stgit.refreshsubmodules')

        return __refresh(
            args,
            force=options.force,
            target_patch=options.patch,
            message=options.message,
            author=options.author,
            trailers=options.trailers,
            annotate=options.annotate,
            use_temp_index=is_path_limiting,
            refresh_from_index=options.index,
            only_update_patchfiles=options.update,
            include_submodules=options.submodules,
            no_verify=options.no_verify,
            invoke_editor=options.edit,
            edit_diff=options.diff,
            diff_flags=options.diff_flags,
        )
Example #13
0
 def get_protected(self):
     return config.getbool(self.__branch_protect())
Example #14
0
def func(parser, options, args):
    """Show the patch series"""
    if options.all and options.short:
        raise 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)

    if options.description is None:
        options.description = config.getbool('stgit.series.description')

    # current series patches
    applied = unapplied = hidden = ()
    if options.applied or options.unapplied or options.hidden:
        if options.all:
            raise 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 = len(branch_str) + max(len(p) for p 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"),
        )
        '-n',
        '--nopush',
        action='store_true',
        short='Do not push the patches back after rebasing',
    ),
    opt(
        '-m',
        '--merged',
        action='store_true',
        short='Check for patches merged upstream',
    ),
    opt(
        '--autostash',
        action='store_true',
        short='Stash changes before the rebase and apply them after',
        default=config.getbool('stgit.autostash'),
        long='''
    Automatically create a temporary stash before the operation begins, and
    apply it after the operation ends. This means that you can run rebase on a
    dirty work-tree. However, use with care: the final stash application after a
    successful rebase might result in non-trivial conflicts.
    ''',
    ),
]

directory = DirectoryGotoTopLevel()

INTERACTIVE_APPLY_LINE = '# --- APPLY_LINE ---'

INTERACTIVE_HELP_INSTRUCTIONS = """
# Commands: