Example #1
0
    def pop_patch(self, name, keep=False):
        """Pops the top patch from the stack
        """
        applied = self.get_applied()
        applied.reverse()
        assert(name in applied)

        patch = self.get_patch(name)

        if git.get_head_file() == self.get_name():
            if keep and not git.apply_diff(git.get_head(), patch.get_bottom(),
                                           check_index=False):
                raise StackException(
                    'Failed to pop patches while preserving the local changes')
            git.switch(patch.get_bottom(), keep)
        else:
            git.set_branch(self.get_name(), patch.get_bottom())

        # save the new applied list
        idx = applied.index(name) + 1

        popped = applied[:idx]
        popped.reverse()
        unapplied = popped + self.get_unapplied()
        write_strings(self.__unapplied_file, unapplied)

        del applied[:idx]
        applied.reverse()
        write_strings(self.__applied_file, applied)
Example #2
0
    def push_patch(self, name, empty=False):
        """Pushes a patch on the stack
        """
        unapplied = self.get_unapplied()
        assert (name in unapplied)

        patch = Patch(name, self.__patch_dir, self.__refs_dir)

        head = git.get_head()
        bottom = patch.get_bottom()
        top = patch.get_top()

        ex = None
        modified = False

        # top != bottom always since we have a commit for each patch
        if empty:
            # just make an empty patch (top = bottom = HEAD). This
            # option is useful to allow undoing already merged
            # patches. The top is updated by refresh_patch since we
            # need an empty commit
            patch.set_bottom(head, backup=True)
            patch.set_top(head, backup=True)
            modified = True
        elif head == bottom:
            # reset the backup information. No need for logging
            patch.set_bottom(bottom, backup=True)
            patch.set_top(top, backup=True)

            git.switch(top)
        else:
            # new patch needs to be refreshed.
            # The current patch is empty after merge.
            patch.set_bottom(head, backup=True)
            patch.set_top(head, backup=True)

            # 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(bottom, head, top, recursive=True)
                except git.GitException, ex:
                    out.error(
                        'The merge failed during "push".',
                        'Use "refresh" after fixing the conflicts or'
                        ' revert the operation with "push --undo".')
Example #3
0
    def merged_patches(self, names):
        """Test which patches were merged upstream by reverse-applying
        them in reverse order. The function returns the list of
        patches detected to have been applied. The state of the tree
        is restored to the original one
        """
        patches = [self.get_patch(name) for name in names]
        patches.reverse()

        merged = []
        for p in patches:
            if git.apply_diff(p.get_top(), p.get_bottom()):
                merged.append(p.get_name())
        merged.reverse()

        git.reset()

        return merged
Example #4
0
def __pick_commit(commit_id, patchname, options):
    """Pick a commit id.
    """
    commit = git.Commit(commit_id)

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

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

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

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

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

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

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

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

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

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

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

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

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

        if crt_series.empty_patch(patchname):
            out.done('empty patch')
        elif modified:
            out.done('modified')
        else:
            out.done()
Example #5
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
Example #6
0
def func(parser, options, args):
    """Import a commit object as a new patch
    """
    if len(args) != 1:
        parser.error('incorrect number of arguments')

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

    commit_str = args[0]
    commit_id = git_id(commit_str)
    commit = git.Commit(commit_id)

    if options.fold or options.update:
        if not crt_series.get_current():
            raise CmdException, 'No patches applied'
    else:
        patch_branch = commit_str.split('@')
        if options.name:
            patchname = options.name
        elif len(patch_branch) == 2:
            patchname = patch_branch[0]
        else:
            patchname = None

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

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

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

        # try a direct git-apply first
        if not git.apply_diff(bottom, top):
            git.merge(bottom, git.get_head(), top, recursive=True)

        out.done()
    elif options.update:
        rev1 = git_id('//bottom')
        rev2 = git_id('//top')
        files = git.barefiles(rev1, rev2).split('\n')

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

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

        out.done()
    else:
        message = commit.get_log()
        if options.expose:
            message += '(imported from commit %s)\n' % commit.get_id_hash()
        author_name, author_email, author_date = \
                     name_email_date(commit.get_author())

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

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

        # find a patchlog to fork from
        (refpatchname, refbranchname, refpatchid) = parse_rev(commit_str)
        if refpatchname and not refpatchid and \
               (not refpatchid or refpatchid == 'top'):
            # FIXME: should also support picking //top.old
            if refbranchname:
                # assume the refseries is OK, since we already resolved
                # commit_str to a git_id
                refseries = Series(refbranchname)
            else:
                refseries = crt_series
            patch = refseries.get_patch(refpatchname)
            if patch.get_log():
                out.info("Log was %s" % newpatch.get_log())
                out.info("Setting log to %s\n" % patch.get_log())
                newpatch.set_log(patch.get_log())
                out.info("Log is now %s" % newpatch.get_log())
            else:
                out.info("No log for %s\n" % patchname)

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

        if crt_series.empty_patch(patchname):
            out.done('empty patch')
        elif modified:
            out.done('modified')
        else:
            out.done()

    print_crt_patch()