Exemplo n.º 1
0
 def __assert_head_top_equal(self):
     if not self.__stack.head_top_equal():
         out.error(
             'HEAD and top are not the same.',
             'This can happen if you modify a branch with git.',
             '"stg repair --help" explains more about what to do next.')
         self.__abort()
    def execute(
        self,
        msg,
        iw=None,
        set_head=True,
        allow_bad_head=False,
        print_current_patch=True,
    ):
        """Execute the transaction.

        Will either succeed, or fail (with an exception) and do nothing.

        """
        self._check_consistency()
        log_external_mods(self.stack)

        # Set branch head.
        if set_head:
            if iw:
                try:
                    self._checkout(self.head.data.tree, iw, allow_bad_head)
                except CheckoutException:
                    # We have to abort the transaction.
                    # The only state we need to restore is index+worktree.
                    self._checkout(self.stack.head.data.tree,
                                   iw,
                                   allow_bad_head=True)
                    raise TransactionAborted()
            self.stack.set_head(self.head, msg)

        if self._error:
            if self._conflicts:
                out.error(*([self._error] + self._conflicts))
            else:
                out.error(self._error)

        old_applied = self.stack.patchorder.applied
        if self._conflicts:
            msg = '%s (CONFLICT)' % (msg, )

        # Write patches.
        for pn, commit in self.patches.items():
            if pn in self.stack.patches:
                if commit is None:
                    self.stack.patches.delete(pn)
                else:
                    self.stack.patches.update(pn, commit, msg)
            else:
                self.stack.patches.new(pn, commit, msg)
        self.stack.patchorder.set_order(self._applied, self._unapplied,
                                        self._hidden)
        log_stack_state(self.stack, msg)

        if print_current_patch:
            _print_current_patch(old_applied, self._applied)

        if self._error:
            return utils.STGIT_CONFLICT
        else:
            return utils.STGIT_SUCCESS
Exemplo n.º 3
0
def __refresh_spill(annotate):
    stack = directory.repository.current_stack

    # Fetch the topmost patch.
    patchname = get_patch(stack, None)

    cd = stack.patches[patchname].data

    # Set the tree of the patch to the parent.
    cd = cd.set_tree(cd.parent.data.tree)

    log_msg = 'refresh (spill)'
    if annotate:
        log_msg += '\n\n' + annotate

    trans = StackTransaction(stack, log_msg, allow_conflicts=True)
    trans.patches[patchname] = stack.repository.commit(cd)
    try:
        # Either a complete success, or a conflict during push. But in
        # either case, we've successfully effected the edits the user
        # asked us for.
        return trans.run()
    except TransactionException:
        # Transaction aborted -- we couldn't check out files due to
        # dirty index/worktree. The edits were not carried out.
        out.error('Unable to spill the topmost patch')
        return utils.STGIT_COMMAND_ERROR
Exemplo n.º 4
0
def note_patch_application_failure(patch_desc,
                                   reason='Edited patch did not apply.'):
    """Call when edit fails. Logs to stderr and saves a patch to filesystem."""
    fn = '.stgit-failed.patch'
    with open(fn, 'wb') as f:
        f.write(patch_desc)
    out.error(reason, 'The patch has been saved to "%s".' % fn)
Exemplo n.º 5
0
 def failed(reason='Edited patch did not apply.'):
     fn = '.stgit-failed.patch'
     with io.open(fn, 'wb') as f:
         f.write(
             edit.patch_desc(stack.repository, cd, options.diff,
                             options.diff_flags, failed_diff))
     out.error(reason, 'The patch has been saved to "%s".' % fn)
     return utils.STGIT_COMMAND_ERROR
Exemplo n.º 6
0
Arquivo: edit.py Projeto: snits/stgit
 def failed(reason='Edited patch did not apply.'):
     fn = '.stgit-failed.patch'
     with open(fn, 'w') as f:
         f.write(edit.patch_desc(stack.repository, cd,
                                 options.diff, options.diff_flags,
                                 failed_diff))
     out.error(reason,
               'The patch has been saved to "%s".' % fn)
     return utils.STGIT_COMMAND_ERROR
Exemplo n.º 7
0
    def run(self,
            iw=None,
            set_head=True,
            allow_bad_head=False,
            print_current_patch=True):
        """Execute the transaction. Will either succeed, or fail (with an
        exception) and do nothing."""
        self.__check_consistency()
        log.log_external_mods(self.__stack)
        new_head = self.head

        # Set branch head.
        if set_head:
            if iw:
                try:
                    self.__checkout(new_head.data.tree, iw, allow_bad_head)
                except git.CheckoutException:
                    # We have to abort the transaction.
                    self.abort(iw)
                    self.__abort()
            self.__stack.set_head(new_head, self.__msg)

        if self.__error:
            if self.__conflicts:
                out.error(*([self.__error] + self.__conflicts))
            else:
                out.error(self.__error)

        # Write patches.
        def write(msg):
            for pn, commit in self.__patches.items():
                if self.__stack.patches.exists(pn):
                    p = self.__stack.patches.get(pn)
                    if commit is None:
                        p.delete()
                    else:
                        p.set_commit(commit, msg)
                else:
                    self.__stack.patches.new(pn, commit, msg)
            self.__stack.patchorder.applied = self.__applied
            self.__stack.patchorder.unapplied = self.__unapplied
            self.__stack.patchorder.hidden = self.__hidden
            log.log_entry(self.__stack, msg)

        old_applied = self.__stack.patchorder.applied
        if not self.__conflicts:
            write(self.__msg)
        else:
            write(self.__msg + ' (CONFLICT)')
        if print_current_patch:
            _print_current_patch(old_applied, self.__applied)

        if self.__error:
            return utils.STGIT_CONFLICT
        else:
            return utils.STGIT_SUCCESS
Exemplo n.º 8
0
    def run(self,
            iw=None,
            set_head=True,
            allow_bad_head=False,
            print_current_patch=True):
        """Execute the transaction.

        Will either succeed, or fail (with an exception) and do nothing.

        """
        self._check_consistency()
        log_external_mods(self.stack)
        new_head = self.head

        # Set branch head.
        if set_head:
            if iw:
                try:
                    self._checkout(new_head.data.tree, iw, allow_bad_head)
                except CheckoutException:
                    # We have to abort the transaction.
                    self.abort(iw)
                    self._abort()
            self.stack.set_head(new_head, self._msg)

        if self._error:
            if self._conflicts:
                out.error(*([self._error] + self._conflicts))
            else:
                out.error(self._error)

        old_applied = self.stack.patchorder.applied
        msg = self._msg + (' (CONFLICT)' if self._conflicts else '')

        # Write patches.
        for pn, commit in self.patches.items():
            if pn in self.stack.patches:
                if commit is None:
                    self.stack.patches.delete(pn)
                else:
                    self.stack.patches.update(pn, commit, msg)
            else:
                self.stack.patches.new(pn, commit, msg)
        self.stack.patchorder.set_order(self._applied, self._unapplied,
                                        self._hidden)
        log_stack_state(self.stack, msg)

        if print_current_patch:
            _print_current_patch(old_applied, self._applied)

        if self._error:
            return utils.STGIT_CONFLICT
        else:
            return utils.STGIT_SUCCESS
Exemplo n.º 9
0
    def canonical_cmd(self, key):
        """Return the canonical name for a possibly-shortenned
        command name.
        """
        candidates = [cmd for cmd in self if cmd.startswith(key)]

        if not candidates:
            out.error('Unknown command: %s' % key,
                      'Try "%s help" for a list of supported commands' % prog)
            sys.exit(utils.STGIT_GENERAL_ERROR)
        elif len(candidates) > 1:
            out.error('Ambiguous command: %s' % key,
                      'Candidates are: %s' % ', '.join(candidates))
            sys.exit(utils.STGIT_GENERAL_ERROR)

        return candidates[0]
Exemplo n.º 10
0
Arquivo: main.py Projeto: snits/stgit
    def canonical_cmd(self, key):
        """Return the canonical name for a possibly-shortenned
        command name.
        """
        candidates = [cmd for cmd in self if cmd.startswith(key)]

        if not candidates:
            out.error('Unknown command: %s' % key,
                      'Try "%s help" for a list of supported commands' % prog)
            sys.exit(utils.STGIT_GENERAL_ERROR)
        elif len(candidates) > 1:
            out.error('Ambiguous command: %s' % key,
                      'Candidates are: %s' % ', '.join(candidates))
            sys.exit(utils.STGIT_GENERAL_ERROR)

        return candidates[0]
Exemplo n.º 11
0
 def _checkout(self, tree, iw, allow_bad_head):
     if not allow_bad_head:
         self._assert_head_top_equal()
     if self._current_tree == tree and not self._discard_changes:
         # No tree change, but we still want to make sure that
         # there are no unresolved conflicts. Conflicts
         # conceptually "belong" to the topmost patch, and just
         # carrying them along to another patch is confusing.
         if self._allow_conflicts(self) or iw is None or not iw.index.conflicts():
             return
         out.error('Need to resolve conflicts first')
         self._abort()
     assert iw is not None
     if self._discard_changes:
         iw.checkout_hard(tree)
     else:
         iw.checkout(self._current_tree, tree)
     self._current_tree = tree
Exemplo n.º 12
0
    def canonical_cmd(self, key):
        """Return the canonical name for a possibly-shortened command name."""
        candidates = [cmd for cmd in self if cmd.startswith(key)]

        if not candidates:
            out.error(
                'Unknown command: %s' % key,
                'Try "stg help" for a list of supported commands',
            )
            raise KeyError(key)
        elif len(candidates) == 1:
            return candidates[0]
        elif key in candidates:
            return key
        else:
            out.error(
                'Ambiguous command: %s' % key,
                'Candidates are: %s' % ', '.join(candidates),
            )
            raise KeyError(key)
 def _checkout(self, tree, iw, allow_bad_head):
     assert iw is not None
     if not allow_bad_head and self.stack.head != self.stack.top:
         out.error(
             'HEAD and top are not the same.',
             'This can happen if you modify a branch with git.',
             '"stg repair --help" explains more about what to do next.',
         )
         raise TransactionAborted()
     if self._current_tree == tree and not self._discard_changes:
         # No tree change, but we still want to make sure that
         # there are no unresolved conflicts. Conflicts
         # conceptually "belong" to the topmost patch, and just
         # carrying them along to another patch is confusing.
         if self._allow_conflicts(self) or not iw.index.conflicts():
             return
         out.error('Need to resolve conflicts first')
         raise TransactionAborted()
     if self._discard_changes:
         iw.checkout_hard(tree)
     else:
         iw.checkout(self._current_tree, tree)
     self._current_tree = tree
Exemplo n.º 14
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)
Exemplo n.º 15
0
def _main():
    """The main function
    """
    global prog

    sys.argv = list(map(fsdecode_utf8, sys.argv))

    prog = os.path.basename(sys.argv[0])

    if len(sys.argv) < 2:
        print('usage: %s <command>' % prog, file=sys.stderr)
        print('  Try "%s --help" for a list of supported commands' % prog,
              file=sys.stderr)
        sys.exit(utils.STGIT_GENERAL_ERROR)

    cmd = sys.argv[1]

    if cmd in ['-h', '--help']:
        if len(sys.argv) >= 3:
            cmd = commands.canonical_cmd(sys.argv[2])
            sys.argv[2] = '--help'
        else:
            print_help()
            sys.exit(utils.STGIT_SUCCESS)
    if cmd == 'help':
        if len(sys.argv) == 3 and not sys.argv[2] in ['-h', '--help']:
            cmd = commands.canonical_cmd(sys.argv[2])
            sys.argv[0] += ' %s' % cmd
            command = commands[cmd]
            parser = argparse.make_option_parser(command)
            if is_cmd_alias(command):
                parser.remove_option('-h')
            pager(parser.format_help().encode())
        else:
            print_help()
        sys.exit(utils.STGIT_SUCCESS)
    if cmd in ['-v', '--version', 'version']:
        from stgit.version import get_version

        print('Stacked GIT %s' % get_version())
        os.system('git --version')
        print('Python version %s' % sys.version)
        sys.exit(utils.STGIT_SUCCESS)
    if cmd in ['copyright']:
        print(__copyright__)
        sys.exit(utils.STGIT_SUCCESS)

    # re-build the command line arguments
    cmd = commands.canonical_cmd(cmd)
    sys.argv[0] += ' %s' % cmd
    del sys.argv[1]

    command = commands[cmd]
    if is_cmd_alias(command):
        sys.exit(command.func(sys.argv[1:]))

    parser = argparse.make_option_parser(command)
    directory = command.directory

    # These modules are only used from this point onwards and do not
    # need to be imported earlier
    try:
        from configparser import ParsingError, NoSectionError
    except ImportError:
        from ConfigParser import ParsingError, NoSectionError
    from stgit.exception import StgException
    from stgit.config import config_setup
    from stgit.stack import Series

    try:
        debug_level = int(environ_get('STGIT_DEBUG_LEVEL', 0))
    except ValueError:
        out.error('Invalid STGIT_DEBUG_LEVEL environment variable')
        sys.exit(utils.STGIT_GENERAL_ERROR)

    try:
        (options, args) = parser.parse_args()
        directory.setup()
        config_setup()

        # Some commands don't (always) need an initialized series.
        if directory.needs_current_series:
            if hasattr(options, 'branch') and options.branch:
                command.crt_series = Series(options.branch)
            else:
                command.crt_series = Series()

        ret = command.func(parser, options, args)
    except (StgException, IOError, ParsingError, NoSectionError) as err:
        directory.write_log(cmd)
        if debug_level > 0:
            traceback.print_exc(file=sys.stderr)
        out.error(str(err), title='%s %s' % (prog, cmd))
        sys.exit(utils.STGIT_COMMAND_ERROR)
    except SystemExit:
        # Triggered by the option parser when it finds bad commandline
        # parameters.
        sys.exit(utils.STGIT_COMMAND_ERROR)
    except KeyboardInterrupt:
        sys.exit(utils.STGIT_GENERAL_ERROR)
    except BaseException:
        out.error('Unhandled exception:')
        traceback.print_exc(file=sys.stderr)
        sys.exit(utils.STGIT_BUG_ERROR)

    directory.write_log(cmd)
    sys.exit(ret or utils.STGIT_SUCCESS)
Exemplo n.º 16
0
Arquivo: stack.py Projeto: snits/stgit
    def push_patch(self, name):
        """Pushes a patch on the stack
        """
        unapplied = self.get_unapplied()
        assert(name in unapplied)

        patch = self.get_patch(name)

        head = git.get_head()
        bottom = patch.get_bottom()
        top = patch.get_top()
        # top != bottom always since we have a commit for each patch

        if head == bottom:
            # A fast-forward push. Just reset the backup
            # information. No need for logging
            patch.set_top(top, backup = True)

            git.switch(top)
            append_string(self.__applied_file, name)

            unapplied.remove(name)
            write_strings(self.__unapplied_file, unapplied)
            return False

        # Need to create a new commit an merge in the old patch
        ex = None
        modified = False

        # Try the fast applying first. If this fails, fall back to the
        # three-way merge
        if not git.apply_diff(bottom, top):
            # if git.apply_diff() fails, the patch requires a diff3
            # merge and can be reported as modified
            modified = True

            # merge can fail but the patch needs to be pushed
            try:
                git.merge_recursive(bottom, head, top)
            except git.GitException:
                out.error('The merge failed during "push".',
                          'Revert the operation with "stg undo".')

        append_string(self.__applied_file, name)

        unapplied.remove(name)
        write_strings(self.__unapplied_file, unapplied)

        if not ex:
            # if the merge was OK and no conflicts, just refresh the patch
            # The GIT cache was already updated by the merge operation
            if modified:
                log = 'push(m)'
            else:
                log = 'push'
            self.refresh_patch(bottom = head, cache_update = False, log = log)
        else:
            # we make the patch empty, with the merged state in the
            # working tree.
            self.refresh_patch(bottom = head, cache_update = False,
                               empty = True, log = 'push(c)')
            raise StackException(str(ex))

        return modified
Exemplo n.º 17
0
    def push_patch(self, name):
        """Pushes a patch on the stack
        """
        unapplied = self.get_unapplied()
        assert(name in unapplied)

        patch = self.get_patch(name)

        head = git.get_head()
        bottom = patch.get_bottom()
        top = patch.get_top()
        # top != bottom always since we have a commit for each patch

        if head == bottom:
            # A fast-forward push. Just reset the backup
            # information. No need for logging
            patch.set_top(top, backup=True)

            git.switch(top)
            append_string(self.__applied_file, name)

            unapplied.remove(name)
            write_strings(self.__unapplied_file, unapplied)
            return False

        # Need to create a new commit an merge in the old patch
        ex = None
        modified = False

        # Try the fast applying first. If this fails, fall back to the
        # three-way merge
        if not git.apply_diff(bottom, top):
            # if git.apply_diff() fails, the patch requires a diff3
            # merge and can be reported as modified
            modified = True

            # merge can fail but the patch needs to be pushed
            try:
                git.merge_recursive(bottom, head, top)
            except git.GitException:
                out.error('The merge failed during "push".',
                          'Revert the operation with "stg undo".')

        append_string(self.__applied_file, name)

        unapplied.remove(name)
        write_strings(self.__unapplied_file, unapplied)

        if not ex:
            # if the merge was OK and no conflicts, just refresh the patch
            # The GIT cache was already updated by the merge operation
            if modified:
                log = 'push(m)'
            else:
                log = 'push'
            self.refresh_patch(bottom=head, cache_update=False, log=log)
        else:
            # we make the patch empty, with the merged state in the
            # working tree.
            self.refresh_patch(
                bottom=head,
                cache_update=False,
                empty=True,
                log='push(c)',
            )
            raise StackException(str(ex))

        return modified
Exemplo n.º 18
0
def _main(argv):
    prog = argv[0] = 'stg'

    if len(argv) < 2:
        print('usage: %s <command>' % prog, file=sys.stderr)
        print('  Try "%s --help" for a list of supported commands' % prog,
              file=sys.stderr)
        return utils.STGIT_GENERAL_ERROR

    cmd = argv[1]

    if cmd in ['-h', '--help']:
        if len(argv) >= 3:
            try:
                cmd = commands.canonical_cmd(argv[2])
            except KeyError:
                return utils.STGIT_GENERAL_ERROR

            argv[2] = '--help'
        else:
            print_help(prog)
            return utils.STGIT_SUCCESS
    if cmd == 'help':
        if len(argv) == 3 and not argv[2] in ['-h', '--help']:
            try:
                cmd = commands.canonical_cmd(argv[2])
            except KeyError:
                return utils.STGIT_GENERAL_ERROR
            argv[0] += ' %s' % cmd
            command = commands[cmd]
            parser = argparse.make_option_parser(command)
            if is_cmd_alias(command):
                parser.remove_option('-h')
            pager(parser.format_help().encode())
        else:
            print_help(prog)
        return utils.STGIT_SUCCESS
    if cmd in ['-v', '--version', 'version']:
        print('Stacked Git %s' % get_version())
        os.system('git --version')
        os.system('%s --version' % sys.executable)
        return utils.STGIT_SUCCESS
    if cmd in ['copyright']:
        print(__copyright__)
        return utils.STGIT_SUCCESS

    # re-build the command line arguments
    try:
        cmd = commands.canonical_cmd(cmd)
    except KeyError:
        return utils.STGIT_GENERAL_ERROR
    argv[0] += ' %s' % cmd
    del argv[1]

    command = commands[cmd]
    if is_cmd_alias(command):
        return command.func(argv[1:])

    parser = argparse.make_option_parser(command)

    # These modules are only used from this point onwards and do not
    # need to be imported earlier
    from configparser import NoSectionError, ParsingError

    from stgit.config import config_setup
    from stgit.exception import StgException
    from stgit.lib.git import MergeConflictException

    try:
        debug_level = int(environ_get('STGIT_DEBUG_LEVEL', 0))
    except ValueError:
        out.error('Invalid STGIT_DEBUG_LEVEL environment variable')
        return utils.STGIT_GENERAL_ERROR

    try:
        (options, args) = parser.parse_args(argv[1:])
        command.directory.setup()
        config_setup()
        return command.func(parser, options, args)
    except MergeConflictException as err:
        if debug_level > 1:
            traceback.print_exc(file=sys.stderr)
        for conflict in err.conflicts:
            out.err(conflict)
        return utils.STGIT_CONFLICT
    except (StgException, IOError, ParsingError, NoSectionError) as err:
        if debug_level > 0:
            traceback.print_exc(file=sys.stderr)
        out.error(str(err), title='%s %s' % (prog, cmd))
        return utils.STGIT_COMMAND_ERROR
    except SystemExit:
        # Triggered by the option parser when it finds bad commandline
        # parameters.
        return utils.STGIT_COMMAND_ERROR
    except KeyboardInterrupt:
        return utils.STGIT_GENERAL_ERROR
    except BaseException:
        out.error('Unhandled exception:')
        traceback.print_exc(file=sys.stderr)
        return utils.STGIT_BUG_ERROR
Exemplo n.º 19
0
Arquivo: pull.py Projeto: snits/stgit
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.get('stgit.keepoptimized') == 'yes':
        git.repack()

    print_crt_patch(crt_series)
Exemplo n.º 20
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()
Exemplo n.º 21
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
Exemplo n.º 22
0
Arquivo: main.py Projeto: snits/stgit
def _main():
    """The main function
    """
    global prog

    sys.argv = list(map(fsdecode_utf8, sys.argv))

    prog = os.path.basename(sys.argv[0])

    if len(sys.argv) < 2:
        print('usage: %s <command>' % prog, file=sys.stderr)
        print('  Try "%s --help" for a list of supported commands' % prog, file=sys.stderr)
        sys.exit(utils.STGIT_GENERAL_ERROR)

    cmd = sys.argv[1]

    if cmd in ['-h', '--help']:
        if len(sys.argv) >= 3:
            cmd = commands.canonical_cmd(sys.argv[2])
            sys.argv[2] = '--help'
        else:
            print_help()
            sys.exit(utils.STGIT_SUCCESS)
    if cmd == 'help':
        if len(sys.argv) == 3 and not sys.argv[2] in ['-h', '--help']:
            cmd = commands.canonical_cmd(sys.argv[2])
            if cmd not in commands:
                out.error('%s help: "%s" command unknown' % (prog, cmd))
                sys.exit(utils.STGIT_GENERAL_ERROR)

            sys.argv[0] += ' %s' % cmd
            command = commands[cmd]
            parser = argparse.make_option_parser(command)
            if is_cmd_alias(command):
                parser.remove_option('-h')
            from pydoc import pager
            pager(parser.format_help())
        else:
            print_help()
        sys.exit(utils.STGIT_SUCCESS)
    if cmd in ['-v', '--version', 'version']:
        from stgit.version import version
        print('Stacked GIT %s' % version)
        os.system('git --version')
        print('Python version %s' % sys.version)
        sys.exit(utils.STGIT_SUCCESS)
    if cmd in ['copyright']:
        print(__copyright__)
        sys.exit(utils.STGIT_SUCCESS)

    # re-build the command line arguments
    cmd = commands.canonical_cmd(cmd)
    sys.argv[0] += ' %s' % cmd
    del(sys.argv[1])

    command = commands[cmd]
    if is_cmd_alias(command):
        sys.exit(command.func(sys.argv[1:]))

    parser = argparse.make_option_parser(command)
    directory = command.directory

    # These modules are only used from this point onwards and do not
    # need to be imported earlier
    try:
        from configparser import ParsingError, NoSectionError
    except ImportError:
        from ConfigParser import ParsingError, NoSectionError
    from stgit.exception import StgException
    from stgit.config import config_setup
    from stgit.stack import Series

    try:
        debug_level = int(os.environ.get('STGIT_DEBUG_LEVEL', 0))
    except ValueError:
        out.error('Invalid STGIT_DEBUG_LEVEL environment variable')
        sys.exit(utils.STGIT_GENERAL_ERROR)

    try:
        (options, args) = parser.parse_args()
        directory.setup()
        config_setup()

        # Some commands don't (always) need an initialized series.
        if directory.needs_current_series:
            if hasattr(options, 'branch') and options.branch:
                command.crt_series = Series(options.branch)
            else:
                command.crt_series = Series()

        ret = command.func(parser, options, args)
    except (StgException, IOError, ParsingError, NoSectionError) as err:
        directory.write_log(cmd)
        if debug_level > 0:
            traceback.print_exc(file=sys.stderr)
        out.error(str(err), title = '%s %s' % (prog, cmd))
        sys.exit(utils.STGIT_COMMAND_ERROR)
    except SystemExit:
        # Triggered by the option parser when it finds bad commandline
        # parameters.
        sys.exit(utils.STGIT_COMMAND_ERROR)
    except KeyboardInterrupt:
        sys.exit(utils.STGIT_GENERAL_ERROR)
    except:
        out.error('Unhandled exception:')
        traceback.print_exc(file=sys.stderr)
        sys.exit(utils.STGIT_BUG_ERROR)

    directory.write_log(cmd)
    sys.exit(ret or utils.STGIT_SUCCESS)