예제 #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()
예제 #2
0
def func(parser, options, args):
    """Import a GNU diff file as a new patch
    """
    if len(args) > 1:
        parser.error('incorrect number of arguments')

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

    if not options.url and filename:
        filename = os.path.abspath(filename)
    directory.cd_to_topdir()

    repository = directory.repository
    stack = repository.current_stack
    check_local_changes(repository)
    check_conflicts(repository.default_iw)
    check_head_top_equal(stack)

    if options.series:
        __import_series(filename, options)
    elif options.mail or options.mbox:
        __import_mail(filename, options)
    elif options.url:
        __import_url(filename, options)
    else:
        __import_file(filename, options)
예제 #3
0
def func(parser, options, args):
    """Import a GNU diff file as a new 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

    if not options.url and filename:
        filename = os.path.abspath(filename)
    directory.cd_to_topdir()

    if options.series:
        __import_series(filename, options)
    elif options.mbox:
        __import_mbox(filename, options)
    elif options.url:
        __import_url(filename, options)
    else:
        __import_file(filename, options)

    print_crt_patch(crt_series)
예제 #4
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

    check_head_top_equal(stack)
    trans = StackTransaction(stack, 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.execute(log_msg)
    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
예제 #5
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)
예제 #6
0
def func(parser, options, args):
    """Import a commit object as a new patch
    """
    if not args:
        parser.error('incorrect number of arguments')

    if options.file and not options.fold:
        parser.error('--file can only be specified with --fold')

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

    if options.ref_branch:
        remote_series = Series(options.ref_branch)
    else:
        remote_series = crt_series

    applied = remote_series.get_applied()
    unapplied = remote_series.get_unapplied()
    try:
        patches = parse_patches(args, applied + unapplied, len(applied))
        commit_id = None
    except CmdException:
        if len(args) > 1:
            raise
        # no patches found, try a commit id
        commit_id = git_id(remote_series, args[0])

    if not commit_id and len(patches) > 1:
        if options.name:
            raise CmdException('--name can only be specified with one patch')
        if options.parent:
            raise CmdException('--parent can only be specified with one patch')

    if options.update and not crt_series.get_current():
        raise CmdException('No patches applied')

    if commit_id:
        # Try to guess a patch name if the argument was <branch>:<patch>
        try:
            patchname = args[0].split(':')[1]
        except IndexError:
            patchname = None
        __pick_commit(commit_id, patchname, options)
    else:
        if options.unapplied:
            patches.reverse()
        for patch in patches:
            __pick_commit(git_id(remote_series, patch), patch, options)

    print_crt_patch(crt_series)
예제 #7
0
파일: pick.py 프로젝트: snits/stgit
def func(parser, options, args):
    """Import a commit object as a new patch
    """
    if not args:
        parser.error('incorrect number of arguments')

    if options.file and not options.fold:
        parser.error('--file can only be specified with --fold')

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

    if options.ref_branch:
        remote_series = Series(options.ref_branch)
    else:
        remote_series = crt_series

    applied = remote_series.get_applied()
    unapplied = remote_series.get_unapplied()
    try:
        patches = parse_patches(args, applied + unapplied, len(applied))
        commit_id = None
    except CmdException:
        if len(args) > 1:
            raise
        # no patches found, try a commit id
        commit_id = git_id(remote_series, args[0])

    if not commit_id and len(patches) > 1:
        if options.name:
            raise CmdException('--name can only be specified with one patch')
        if options.parent:
            raise CmdException('--parent can only be specified with one patch')

    if options.update and not crt_series.get_current():
        raise CmdException('No patches applied')

    if commit_id:
        # Try to guess a patch name if the argument was <branch>:<patch>
        try:
            patchname = args[0].split(':')[1]
        except IndexError:
            patchname = None
        __pick_commit(commit_id, patchname, options)
    else:
        if options.unapplied:
            patches.reverse()
        for patch in patches:
            __pick_commit(git_id(remote_series, patch), patch, options)

    print_crt_patch(crt_series)
예제 #8
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()
예제 #9
0
def func(parser, options, args):
    if len(args) != 1:
        parser.error('incorrect number of arguments')
    name = args[0]

    stack = directory.repository.current_stack
    iw = stack.repository.default_iw

    check_head_top_equal(stack)
    if not options.keep:
        check_index_and_worktree_clean(stack)

    trans = transaction.StackTransaction(stack)

    if name not in trans.all_patches:
        candidates = [pn for pn in trans.all_patches if name in pn]
        if len(candidates) == 1:
            name = candidates[0]
        elif len(candidates) > 1:
            out.info('Possible patches:\n  %s' % '\n  '.join(candidates))
            raise CmdException('Ambiguous patch name "%s"' % name)
        elif re.match('[0-9A-Fa-f]{4,40}$', name):
            sha1 = name
            name = stack.patches.name_from_sha1(sha1)
            if not name:
                raise CmdException('No patch associated with %s' % sha1)
        else:
            raise CmdException('Patch "%s" does not exist' % name)

    if name in trans.applied:
        to_pop = set(trans.applied[trans.applied.index(name) + 1:])
        popped_extra = trans.pop_patches(lambda pn: pn in to_pop)
        assert not popped_extra
    elif name in trans.unapplied:
        try:
            to_push = trans.unapplied[:trans.unapplied.index(name) + 1]
            if options.merged:
                merged = set(trans.check_merged(to_push))
            else:
                merged = set()
            for pn in to_push:
                trans.push_patch(pn,
                                 iw,
                                 allow_interactive=True,
                                 already_merged=pn in merged)
        except transaction.TransactionHalted:
            pass
    else:
        raise CmdException('Cannot goto a hidden patch')
    return trans.execute('goto', iw)
예제 #10
0
def func(parser, options, args):
    """Reorder patches to make the named patch the topmost one."""
    if options.series and args:
        parser.error('<patches> cannot be used with --series')
    elif not options.series and not args:
        parser.error('incorrect number of arguments')

    stack = directory.repository.current_stack

    if options.series:
        if options.series == '-':
            f = io.open(sys.stdin.fileno())
        else:
            f = io.open(options.series)

        patches = []
        for line in f:
            patch = re.sub('#.*$', '', line).strip()
            if patch:
                patches.append(patch)
        patches = parse_patches(patches, stack.patchorder.all)
    else:
        patches = parse_patches(args, stack.patchorder.all)

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

    iw = stack.repository.default_iw
    check_head_top_equal(stack)

    if not options.keep and (not options.noapply
                             or any(pn in stack.patchorder.applied
                                    for pn in patches)):
        check_index_and_worktree_clean(stack)

    trans = transaction.StackTransaction(stack)

    if options.noapply:
        applied = [p for p in trans.applied if p not in patches]
        unapplied = patches + [p for p in trans.unapplied if p not in patches]
    else:
        applied = [p for p in trans.applied if p not in patches] + patches
        unapplied = [p for p in trans.unapplied if p not in patches]

    try:
        trans.reorder_patches(applied, unapplied, iw=iw)
    except transaction.TransactionHalted:
        pass
    return trans.execute('float', iw)
예제 #11
0
def func(parser, options, args):
    """Sink patches down the stack."""
    stack = directory.repository.current_stack

    if options.to and options.to not in stack.patchorder.applied:
        raise CmdException('Cannot sink below %s since it is not applied' %
                           options.to)

    if len(args) > 0:
        patches = parse_patches(args, stack.patchorder.all)
    else:
        # current patch
        patches = list(stack.patchorder.applied[-1:])

    if not patches:
        raise CmdException('No patches to sink')
    if options.to and options.to in patches:
        raise CmdException('Cannot have a sinked patch as target')

    unapplied_rem = [p for p in stack.patchorder.unapplied if p not in patches]
    applied_rem = [p for p in stack.patchorder.applied if p not in patches]
    insert_idx = applied_rem.index(options.to) if options.to else 0
    stay_applied, re_applied = applied_rem[:insert_idx], applied_rem[
        insert_idx:]

    if options.nopush:
        applied = stay_applied + patches
        unapplied = re_applied + unapplied_rem
    else:
        applied = stay_applied + patches + re_applied
        unapplied = unapplied_rem

    iw = stack.repository.default_iw

    check_head_top_equal(stack)
    if not options.keep:
        check_index_and_worktree_clean(stack)

    trans = transaction.StackTransaction(stack)

    try:
        trans.reorder_patches(applied,
                              unapplied,
                              iw=iw,
                              allow_interactive=True)
    except transaction.TransactionHalted:
        pass
    return trans.execute('sink', iw)
예제 #12
0
def func(parser, options, args):
    """Pop the given patches or the topmost one from the stack."""
    stack = directory.repository.current_stack
    iw = stack.repository.default_iw
    check_head_top_equal(stack)
    if not options.keep and not options.spill:
        check_index_and_worktree_clean(stack)
    trans = transaction.StackTransaction(stack)

    if options.number == 0:
        # explicitly allow this without any warning/error message
        return

    if not trans.applied:
        raise CmdException('No patches applied')

    if options.all:
        patches = trans.applied
    elif options.number is not None:
        # reverse it twice to also work with negative or bigger than
        # the length numbers
        patches = trans.applied[::-1][:options.number][::-1]
    elif not args:
        patches = [trans.applied[-1]]
    else:
        patches = parse_patches(args, trans.applied, ordered=True)

    if not patches:
        # FIXME: Why is this an error, and not just a noop ?
        raise CmdException('No patches to pop')

    if options.spill:
        if set(stack.patchorder.applied[-len(patches):]) != set(patches):
            parser.error('Can only spill topmost applied patches')
        iw = None  # don't touch index+worktree

    try:
        trans.reorder_patches(
            applied=[p for p in trans.applied if p not in patches],
            unapplied=patches + trans.unapplied,
            iw=iw,
            allow_interactive=True,
        )
    except transaction.TransactionException:
        pass
    return trans.execute('pop', iw)
예제 #13
0
def _clean(stack, clean_applied, clean_unapplied):
    check_head_top_equal(stack)
    trans = transaction.StackTransaction(stack, allow_conflicts=True)

    def del_patch(pn):
        if pn in stack.patchorder.applied:
            if pn == stack.patchorder.applied[-1]:
                # We're about to clean away the topmost patch. Don't
                # do that if we have conflicts, since that means the
                # patch is only empty because the conflicts have made
                # us dump its contents into the index and worktree.
                if stack.repository.default_index.conflicts():
                    return False
            return clean_applied and trans.patches[pn].data.is_nochange()
        elif pn in stack.patchorder.unapplied:
            return clean_unapplied and trans.patches[pn].data.is_nochange()

    for pn in trans.delete_patches(del_patch):
        trans.push_patch(pn)
    trans.execute('clean')
예제 #14
0
def func(parser, options, args):
    """Unhide a range of patch in the series."""
    stack = directory.repository.current_stack
    check_head_top_equal(stack)
    trans = transaction.StackTransaction(stack)

    if not args:
        parser.error('No patches specified')

    patches = parse_patches(args, trans.all_patches)
    for p in patches:
        if p not in trans.hidden:
            raise CmdException('Patch "%s" not hidden' % p)

    applied = list(trans.applied)
    unapplied = trans.unapplied + patches
    hidden = [p for p in trans.hidden if p not in set(patches)]

    trans.reorder_patches(applied, unapplied, hidden)
    return trans.execute('unhide')
예제 #15
0
파일: fold.py 프로젝트: 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()
예제 #16
0
def func(parser, options, args):
    """Hide a range of patch in the series."""
    stack = directory.repository.current_stack
    check_head_top_equal(stack)
    trans = transaction.StackTransaction(stack)

    if not args:
        parser.error('No patches specified')

    patches = parse_patches(args, trans.all_patches)
    for p in patches:
        if p in trans.hidden:
            out.warn('Patch "%s" already hidden' % p)
    patches = [p for p in patches if p not in set(trans.hidden)]

    applied = [p for p in trans.applied if p not in set(patches)]
    unapplied = [p for p in trans.unapplied if p not in set(patches)]
    hidden = patches + trans.hidden

    trans.reorder_patches(applied, unapplied, hidden)
    return trans.execute('hide')
예제 #17
0
def func(parser, options, args):
    """Rebase the current stack
    """
    if len(args) != 1:
        parser.error('incorrect number of arguments')

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

    check_local_changes()
    check_conflicts()
    check_head_top_equal(crt_series)

    # ensure an exception is raised before popping on non-existent target
    if git_id(crt_series, args[0]) is None:
        raise GitException('Unknown revision: %s' % args[0])

    applied = prepare_rebase(crt_series)
    rebase(crt_series, args[0])
    post_rebase(crt_series, applied, options.nopush, options.merged)

    print_crt_patch(crt_series)
예제 #18
0
파일: rebase.py 프로젝트: snits/stgit
def func(parser, options, args):
    """Rebase the current stack
    """
    if len(args) != 1:
        parser.error('incorrect number of arguments')

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

    check_local_changes()
    check_conflicts()
    check_head_top_equal(crt_series)

    # ensure an exception is raised before popping on non-existent target
    if git_id(crt_series, args[0]) is None:
        raise GitException('Unknown revision: %s' % args[0])
        
    applied = prepare_rebase(crt_series)
    rebase(crt_series, args[0])
    post_rebase(crt_series, applied, options.nopush, options.merged)

    print_crt_patch(crt_series)
예제 #19
0
def absorb(stack, patch_name, temp_name, edit_fun, annotate=None):
    """Absorb the temp patch into the target patch."""
    log_msg = 'refresh'
    if annotate:
        log_msg += '\n\n' + annotate

    check_head_top_equal(stack)
    trans = StackTransaction(stack)
    iw = stack.repository.default_iw
    if patch_name in trans.applied:
        absorbed = absorb_applied(trans, iw, patch_name, temp_name, edit_fun)
    else:
        absorbed = absorb_unapplied(trans, iw, patch_name, temp_name, edit_fun)

    r = trans.execute(log_msg, iw)

    if not absorbed:
        out.warn(
            'The new changes did not apply cleanly to %s.' % patch_name,
            'They were saved in %s.' % temp_name,
        )

    return r
예제 #20
0
def __refresh(
    args,
    force=False,
    target_patch=None,
    message=None,
    author=None,
    trailers=None,
    annotate=None,
    use_temp_index=False,
    refresh_from_index=False,
    only_update_patchfiles=False,
    include_submodules=False,
    no_verify=False,
    invoke_editor=False,
    edit_diff=False,
    diff_flags=(),
):
    stack = directory.repository.current_stack

    patch_name = get_patch(stack, target_patch)

    if refresh_from_index:
        paths = set()
    else:
        paths = list_files(
            stack,
            patch_name,
            args,
            only_update_patchfiles,
            include_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 refresh_from_index and not 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.'
            )

    check_head_top_equal(stack)

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

    # Run pre-commit hook, if fails, abort refresh
    if not no_verify:
        pre_commit_hook = get_hook(
            stack.repository,
            'pre-commit',
            extra_env={} if invoke_editor 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, use_temp_index=use_temp_index)

    # 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
        new_msg = None
        if message is not None:
            new_msg = message.encode(config.get('i18n.commitencoding'))
        cd = auto_edit_patch(
            stack.repository,
            cd,
            msg=new_msg,
            author=author,
            trailers=trailers,
        )
        new_patch_name = None
        if invoke_editor:
            cd, new_patch_name, failed_diff = interactive_edit_patch(
                stack.repository, cd, patch_name, edit_diff, diff_flags
            )
            assert not failed_diff
        if not no_verify and (invoke_editor or cd.message != orig_msg):
            cd = run_commit_msg_hook(stack.repository, cd, invoke_editor)
        # Refresh the committer information
        return cd.set_committer(None), new_patch_name

    return absorb(stack, patch_name, temp_name, edit_fun, annotate=annotate)
예제 #21
0
def func(parser, options, args):
    """Pushes the given patches or the first unapplied onto the stack."""
    stack = directory.repository.current_stack
    iw = stack.repository.default_iw

    if options.number == 0:
        # explicitly allow this without any warning/error message
        return

    if not stack.patchorder.unapplied:
        raise CmdException('No patches to push')

    if options.all:
        if options.noapply:
            raise CmdException('Cannot use --noapply with --all')
        patches = list(stack.patchorder.unapplied)
    elif options.number is not None:
        if options.noapply:
            raise CmdException('Cannot use --noapply with --number')
        patches = list(stack.patchorder.unapplied[: options.number])
    elif not args:
        if options.noapply:
            raise CmdException('Must supply patch names with --noapply')
        patches = [stack.patchorder.unapplied[0]]
    else:
        try:
            patches = parse_patches(args, stack.patchorder.unapplied)
        except CmdException as e:
            try:
                patches = parse_patches(args, stack.patchorder.applied)
            except CmdException:
                raise e
            else:
                raise CmdException(
                    'Patch%s already applied: %s'
                    % ('es' if len(patches) > 1 else '', ', '.join(patches))
                )

    assert patches

    check_head_top_equal(stack)
    if not options.keep and not options.noapply:
        check_index_and_worktree_clean(stack)

    trans = transaction.StackTransaction(stack)

    if options.reverse:
        patches.reverse()

    if options.set_tree:
        if options.noapply:
            raise CmdException('Cannot use --noapply with --set-tree')
        for pn in patches:
            trans.push_tree(pn)
    elif options.noapply:
        if options.merged:
            raise CmdException('Cannot use --noapply with --merged')
        unapplied = patches + [pn for pn in trans.unapplied if pn not in patches]
        trans.reorder_patches(trans.applied, unapplied)
    else:
        try:
            if options.merged:
                merged = set(trans.check_merged(patches))
            else:
                merged = set()
            for pn in patches:
                trans.push_patch(
                    pn, iw, allow_interactive=True, already_merged=pn in merged
                )
        except transaction.TransactionHalted:
            pass
    return trans.execute('push', iw)
예제 #22
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())
예제 #23
0
파일: sync.py 프로젝트: snits/stgit
def __check_all():
    check_local_changes()
    check_conflicts()
    check_head_top_equal(crt_series)
예제 #24
0
def func(parser, options, args):
    """Commit a number of patches."""
    stack = directory.repository.current_stack
    args = parse_patches(args, list(stack.patchorder.all_visible))
    exclusive = [args, options.number is not None, options.all]
    if sum(map(bool, exclusive)) > 1:
        parser.error('too many options')
    if args:
        patches = [pn for pn in stack.patchorder.all_visible if pn in args]
        bad = set(args) - set(patches)
        if bad:
            raise CmdException('Nonexistent or hidden patch names: %s' %
                               (', '.join(sorted(bad)), ))
    elif options.number is not None:
        if options.number <= len(stack.patchorder.applied):
            patches = stack.patchorder.applied[:options.number]
        else:
            raise CmdException('There are not that many applied patches')
    elif options.all:
        patches = stack.patchorder.applied
    else:
        patches = stack.patchorder.applied[:1]
    if not patches:
        raise CmdException('No patches to commit')

    if not options.allow_empty:
        empty_patches = [
            pn for pn in patches if stack.patches[pn].data.tree ==
            stack.patches[pn].data.parent.data.tree
        ]
        if empty_patches:
            raise CmdException(
                'Empty patch%s (override with --allow-empty): %s' %
                ('' if len(empty_patches) == 1 else 'es',
                 ', '.join(empty_patches)))

    iw = stack.repository.default_iw

    def allow_conflicts(trans):
        # As long as the topmost patch stays where it is, it's OK to
        # run "stg commit" with conflicts in the index.
        return len(trans.applied) >= 1

    check_head_top_equal(stack)
    trans = transaction.StackTransaction(stack,
                                         allow_conflicts=allow_conflicts)
    try:
        common_prefix = 0
        for i in range(min(len(stack.patchorder.applied), len(patches))):
            if stack.patchorder.applied[i] == patches[i]:
                common_prefix += 1
            else:
                break
        if common_prefix < len(patches):
            to_push = [
                pn for pn in stack.patchorder.applied[common_prefix:]
                if pn not in patches[common_prefix:]
            ]
            # this pops all the applied patches from common_prefix
            trans.pop_patches(lambda pn: pn in to_push)
            for pn in patches[common_prefix:]:
                trans.push_patch(pn, iw)
        else:
            to_push = []
        new_base = trans.patches[patches[-1]]
        for pn in patches:
            trans.patches[pn] = None
        trans.applied = [pn for pn in trans.applied if pn not in patches]
        trans.base = new_base
        out.info('Committed %d patch%s' %
                 (len(patches), ['es', ''][len(patches) == 1]))
        for pn in to_push:
            trans.push_patch(pn, iw)
    except transaction.TransactionHalted:
        pass
    return trans.execute('commit', iw)
예제 #25
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:
        if patchname in stack.patches:
            patchname = stack.patches.make_name(patchname, lower=False)
    else:
        patchname = stack.patches.make_name(commit.data.message_str)

    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,
            ))

        if not options.noapply:
            check_head_top_equal(stack)

        trans = StackTransaction(stack)
        trans.patches[patchname] = new_commit

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

        retval = trans.execute(
            'pick %s from %s' % (patchname, ref_stack.name),
            iw,
            print_current_patch=False,
        )

        if retval == STGIT_CONFLICT:
            out.done('conflict(s)')
        elif stack.patches[patchname].data.is_nochange():
            out.done('empty patch')
        else:
            out.done()

        return retval
예제 #26
0
def func(parser, options, args):
    repository = directory.repository

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

        branch_name = args[0]
        committish = None if len(args) < 2 else args[1]

        if committish:
            check_local_changes(repository)
        check_conflicts(repository.default_iw)
        try:
            stack = repository.get_stack()
        except (DetachedHeadException, StackException):
            pass
        else:
            check_head_top_equal(stack)

        stack = __create_branch(branch_name, committish)

        out.info('Branch "%s" created' % branch_name)
        log.log_entry(stack, 'branch --create %s' % stack.name)
        return

    elif options.clone:

        cur_branch = Branch(repository, repository.current_branch_name)
        if len(args) == 0:
            clone_name = cur_branch.name + time.strftime('-%C%y%m%d-%H%M%S')
        elif len(args) == 1:
            clone_name = args[0]
        else:
            parser.error('incorrect number of arguments')

        check_local_changes(repository)
        check_conflicts(repository.default_iw)
        try:
            stack = repository.current_stack
        except StackException:
            stack = None
            base = repository.refs.get(repository.head_ref)
        else:
            check_head_top_equal(stack)
            base = stack.base

        out.start('Cloning current branch to "%s"' % clone_name)
        clone = Stack.create(
            repository,
            name=clone_name,
            create_at=base,
            parent_remote=cur_branch.parent_remote,
            parent_branch=cur_branch.name,
        )
        if stack:
            for pn in stack.patchorder.all_visible:
                patch = stack.patches.get(pn)
                clone.patches.new(pn, patch.commit, 'clone %s' % stack.name)
            clone.patchorder.set_order(applied=[],
                                       unapplied=stack.patchorder.all_visible,
                                       hidden=[])
            trans = StackTransaction(clone, 'clone')
            try:
                for pn in stack.patchorder.applied:
                    trans.push_patch(pn)
            except TransactionHalted:
                pass
            trans.run()
        prefix = 'branch.%s.' % cur_branch.name
        new_prefix = 'branch.%s.' % clone.name
        for n, v in list(config.getstartswith(prefix)):
            config.set(n.replace(prefix, new_prefix, 1), v)
        clone.set_description('clone of "%s"' % cur_branch.name)
        clone.switch_to()
        out.done()

        log.copy_log(log.default_repo(), cur_branch.name, clone.name,
                     '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 = repository.current_branch_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')

        branch_names = sorted(
            ref.replace('refs/heads/', '', 1) for ref in repository.refs
            if ref.startswith('refs/heads/') and not ref.endswith('.stgit'))

        if branch_names:
            out.info('Available branches:')
            max_len = max(len(name) for name in branch_names)
            for branch_name in branch_names:
                __print_branch(branch_name, max_len)
        else:
            out.info('No branches')
        return

    elif options.protect:

        if len(args) == 0:
            branch_name = repository.current_branch_name
        elif len(args) == 1:
            branch_name = args[0]
        else:
            parser.error('incorrect number of arguments')

        try:
            stack = repository.get_stack(branch_name)
        except StackException:
            raise CmdException('Branch "%s" is not controlled by StGIT' %
                               branch_name)

        out.start('Protecting branch "%s"' % branch_name)
        stack.protected = True
        out.done()

        return

    elif options.rename:

        if len(args) == 1:
            stack = repository.current_stack
            new_name = args[0]
        elif len(args) == 2:
            stack = repository.get_stack(args[0])
            new_name = args[1]
        else:
            parser.error('incorrect number of arguments')

        old_name = stack.name
        stack.rename(new_name)

        out.info('Renamed branch "%s" to "%s"' % (old_name, new_name))
        log.rename_log(repository, old_name, new_name, 'branch --rename')
        return

    elif options.unprotect:

        if len(args) == 0:
            branch_name = repository.current_branch_name
        elif len(args) == 1:
            branch_name = args[0]
        else:
            parser.error('incorrect number of arguments')

        try:
            stack = repository.get_stack(branch_name)
        except StackException:
            raise CmdException('Branch "%s" is not controlled by StGIT' %
                               branch_name)

        out.info('Unprotecting branch "%s"' % branch_name)
        stack.protected = False
        out.done()

        return

    elif options.description is not None:

        if len(args) == 0:
            branch_name = repository.current_branch_name
        elif len(args) == 1:
            branch_name = args[0]
        else:
            parser.error('incorrect number of arguments')

        Branch(repository, branch_name).set_description(options.description)
        return

    elif len(args) == 1:
        branch_name = args[0]
        if branch_name == repository.current_branch_name:
            raise CmdException('Branch "%s" is already the current branch' %
                               branch_name)

        if not options.merge:
            check_local_changes(repository)
        check_conflicts(repository.default_iw)
        try:
            stack = repository.get_stack()
        except StackException:
            pass
        else:
            check_head_top_equal(stack)

        out.start('Switching to branch "%s"' % branch_name)
        Branch(repository, branch_name).switch_to()
        out.done()
        return

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

    out.stdout(directory.repository.current_branch_name)
예제 #27
0
def func(parser, options, args):
    """Synchronise a range of patches"""
    repository = directory.repository
    stack = repository.get_stack()

    if options.ref_branch:
        remote_stack = repository.get_stack(options.ref_branch)
        if remote_stack.name == stack.name:
            raise CmdException('Cannot synchronise with the current branch')
        remote_patches = remote_stack.patchorder.applied

        def merge_patch(commit, pname):
            return __branch_merge_patch(remote_stack, stack, commit, pname)

    elif options.series:
        patchdir = os.path.dirname(options.series)

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

        def merge_patch(commit, pname):
            return __series_merge_patch(patchdir, stack, commit, pname)

    else:
        raise CmdException('No remote branch or series specified')

    applied = list(stack.patchorder.applied)
    unapplied = list(stack.patchorder.unapplied)

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

    assert patches

    # 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')

    iw = repository.default_iw
    check_head_top_equal(stack)
    check_index_and_worktree_clean(stack)

    # 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:
            trans = StackTransaction(stack)
            popped_extra = trans.pop_patches(lambda pn: pn in to_pop)
            assert not popped_extra
            retval = trans.execute('sync (pop)', iw)
            assert not retval
        pushed = [first_patch]
    else:
        to_pop = []
        pushed = []
    popped = to_pop + [p for p in patches if p in unapplied]

    trans = StackTransaction(stack)
    try:
        for p in pushed + popped:
            if p in popped:
                trans.push_patch(p, iw=iw)

            if p not in sync_patches:
                # nothing to synchronise
                continue

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

            commit = trans.patches[p]

            # the actual merging (either from a branch or an external file)
            tree = merge_patch(commit, p)
            if tree:
                trans.patches[p] = commit.data.set_tree(tree).commit(
                    repository)
                out.done('updated')
            else:
                out.done()
    except TransactionHalted:
        pass
    return trans.execute('sync', iw)
예제 #28
0
def __check_all():
    check_local_changes()
    check_conflicts()
    check_head_top_equal(crt_series)
예제 #29
0
def func(parser, options, args):
    """Import a commit object as a new patch
    """
    if not args:
        parser.error('incorrect number of arguments')

    if options.file and not options.fold:
        parser.error('--file can only be specified with --fold')

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

    if not options.unapplied:
        check_local_changes(repository)
        check_conflicts(iw)
        check_head_top_equal(stack)

    if options.ref_branch:
        ref_stack = repository.get_stack(options.ref_branch)
    else:
        ref_stack = stack

    try:
        patches = parse_patches(
            args,
            ref_stack.patchorder.all_visible,
            len(ref_stack.patchorder.applied),
        )
        commit = None
    except CmdException:
        if len(args) > 1:
            raise

        branch, patch = parse_rev(args[0])

        if not branch:
            commit = git_commit(patch, repository, options.ref_branch)
            patches = []
        else:
            ref_stack = repository.get_stack(branch)
            patches = parse_patches(
                [patch],
                ref_stack.patchorder.all_visible,
                len(ref_stack.patchorder.applied),
            )
            commit = None

    if not commit and len(patches) > 1:
        if options.name:
            raise CmdException('--name can only be specified with one patch')
        if options.parent:
            raise CmdException('--parent can only be specified with one patch')

    if options.update and not stack.patchorder.applied:
        raise CmdException('No patches applied')

    if commit:
        patchname = None
        retval = __pick_commit(stack, ref_stack, iw, commit, patchname,
                               options)
    else:
        if options.unapplied:
            patches.reverse()
        for patchname in patches:
            commit = git_commit(patchname, repository, ref_stack.name)
            retval = __pick_commit(stack, ref_stack, iw, commit, patchname,
                                   options)
            if retval != STGIT_SUCCESS:
                break

    if retval == STGIT_SUCCESS:
        print_current_patch(stack)

    return retval
예제 #30
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)
예제 #31
0
파일: branch.py 프로젝트: snits/stgit
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())
예제 #32
0
파일: pull.py 프로젝트: 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)
예제 #33
0
def perform_edit(
    stack,
    cd,
    orig_patchname,
    new_patchname,
    edit_diff,
    diff_flags,
    set_tree=None,
):
    """Given instructions, performs required the edit.

    :returns: 2-tuple:
        - the result of the transaction
        - the new patch name, whether changed or not.
    """
    # Refresh the committer information
    cd = cd.set_committer(None)

    # Rewrite the StGit patch with the given diff (and any patches on top of
    # it).
    iw = stack.repository.default_iw
    common.check_head_top_equal(stack)
    trans = transaction.StackTransaction(stack, allow_conflicts=True)
    if orig_patchname in trans.applied:
        popped = trans.applied[trans.applied.index(orig_patchname) + 1:]
        popped_extra = trans.pop_patches(lambda pn: pn in popped)
        assert not popped_extra
    else:
        popped = []
    trans.patches[orig_patchname] = stack.repository.commit(cd)
    if new_patchname == "":
        new_patchname = stack.patches.make_name(cd.message_str,
                                                allow=orig_patchname)
    if new_patchname is not None and orig_patchname != new_patchname:
        out.start('Renaming patch "%s" to "%s"' %
                  (orig_patchname, new_patchname))
        trans.rename_patch(orig_patchname, new_patchname)
        out.done()
        log_stack_state(stack,
                        'rename %s to %s' % (orig_patchname, new_patchname))
    else:
        new_patchname = orig_patchname
    try:
        for pn in popped:
            if set_tree:
                trans.push_tree(pn)
            else:
                trans.push_patch(pn, iw, allow_interactive=True)
    except transaction.TransactionHalted:
        pass
    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.execute('edit', iw), new_patchname
    except transaction.TransactionException:
        # Transaction aborted -- we couldn't check out files due to
        # dirty index/worktree. The edits were not carried out.
        note_patch_application_failure(
            get_patch_description(stack.repository, cd, orig_patchname,
                                  edit_diff, diff_flags))
        return utils.STGIT_COMMAND_ERROR, new_patchname