Exemple #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)
Exemple #2
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)
Exemple #3
0
    def push_empty_patch(self, name):
        """Pushes an empty patch on the stack
        """
        unapplied = self.get_unapplied()
        assert(name in unapplied)

        # patch = self.get_patch(name)
        head = git.get_head()

        append_string(self.__applied_file, name)

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

        self.refresh_patch(bottom = head, cache_update = False, log = 'push(m)')
Exemple #4
0
    def push_empty_patch(self, name):
        """Pushes an empty patch on the stack
        """
        unapplied = self.get_unapplied()
        assert(name in unapplied)

        # patch = self.get_patch(name)
        head = git.get_head()

        append_string(self.__applied_file, name)

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

        self.refresh_patch(bottom=head, cache_update=False, log='push(m)')
Exemple #5
0
    def delete_patch(self, name, keep_log = False):
        """Deletes a patch
        """
        self.__patch_name_valid(name)
        patch = self.get_patch(name)

        if self.__patch_is_current(patch):
            self.pop_patch(name)
        elif self.patch_applied(name):
            raise StackException('Cannot remove an applied patch, "%s", '
                                 'which is not current' % name)
        elif name not in self.get_unapplied():
            raise StackException('Unknown patch "%s"' % name)

        # save the commit id to a trash file
        write_string(os.path.join(self.__trash_dir, name), patch.get_top())

        patch.delete(keep_log = keep_log)

        unapplied = self.get_unapplied()
        unapplied.remove(name)
        write_strings(self.__unapplied_file, unapplied)
Exemple #6
0
    def delete_patch(self, name, keep_log=False):
        """Deletes a patch
        """
        self.__patch_name_valid(name)
        patch = self.get_patch(name)

        if self.__patch_is_current(patch):
            self.pop_patch(name)
        elif self.patch_applied(name):
            raise StackException('Cannot remove an applied patch, "%s", '
                                 'which is not current' % name)
        elif name not in self.get_unapplied():
            raise StackException('Unknown patch "%s"' % name)

        # save the commit id to a trash file
        write_string(os.path.join(self.__trash_dir, name), patch.get_top())

        patch.delete(keep_log=keep_log)

        unapplied = self.get_unapplied()
        unapplied.remove(name)
        write_strings(self.__unapplied_file, unapplied)
Exemple #7
0
    def rename_patch(self, oldname, newname):
        self.__patch_name_valid(newname)

        applied = self.get_applied()
        unapplied = self.get_unapplied()

        if oldname == newname:
            raise StackException('"To" name and "from" name are the same')

        if newname in applied or newname in unapplied:
            raise StackException('Patch "%s" already exists' % newname)

        if oldname in unapplied:
            self.get_patch(oldname).rename(newname)
            unapplied[unapplied.index(oldname)] = newname
            write_strings(self.__unapplied_file, unapplied)
        elif oldname in applied:
            self.get_patch(oldname).rename(newname)

            applied[applied.index(oldname)] = newname
            write_strings(self.__applied_file, applied)
        else:
            raise StackException('Unknown patch "%s"' % oldname)
Exemple #8
0
    def rename_patch(self, oldname, newname):
        self.__patch_name_valid(newname)

        applied = self.get_applied()
        unapplied = self.get_unapplied()

        if oldname == newname:
            raise StackException('"To" name and "from" name are the same')

        if newname in applied or newname in unapplied:
            raise StackException('Patch "%s" already exists' % newname)

        if oldname in unapplied:
            self.get_patch(oldname).rename(newname)
            unapplied[unapplied.index(oldname)] = newname
            write_strings(self.__unapplied_file, unapplied)
        elif oldname in applied:
            self.get_patch(oldname).rename(newname)

            applied[applied.index(oldname)] = newname
            write_strings(self.__applied_file, applied)
        else:
            raise StackException('Unknown patch "%s"' % oldname)
Exemple #9
0
    def new_patch(self, name, message = None, can_edit = True,
                  unapplied = False, show_patch = False,
                  top = None, bottom = None, commit = True,
                  author_name = None, author_email = None, author_date = None,
                  committer_name = None, committer_email = None,
                  before_existing = False, sign_str = None):
        """Creates a new patch, either pointing to an existing commit object,
        or by creating a new commit object.
        """

        assert commit or (top and bottom)
        assert not before_existing or (top and bottom)
        assert not (commit and before_existing)
        assert (top and bottom) or (not top and not bottom)
        assert commit or (not top or (bottom == git.get_commit(top).get_parent()))

        if name is not None:
            self.__patch_name_valid(name)
            if self.patch_exists(name):
                raise StackException('Patch "%s" already exists' % name)

        # TODO: move this out of the stgit.stack module, it is really
        # for higher level commands to handle the user interaction
        def sign(msg):
            return add_sign_line(msg, sign_str,
                                 committer_name or git.committer().name,
                                 committer_email or git.committer().email)
        if not message and can_edit:
            descr = edit_file(
                self, sign(''),
                'Please enter the description for the patch above.',
                show_patch)
        else:
            descr = sign(message)

        head = git.get_head()

        if name is None:
            name = make_patch_name(descr, self.patch_exists)

        patch = self.get_patch(name)
        patch.create()

        patch.set_description(descr)
        patch.set_authname(author_name)
        patch.set_authemail(author_email)
        patch.set_authdate(author_date)
        patch.set_commname(committer_name)
        patch.set_commemail(committer_email)

        if before_existing:
            insert_string(self.__applied_file, patch.get_name())
        elif unapplied:
            patches = [patch.get_name()] + self.get_unapplied()
            write_strings(self.__unapplied_file, patches)
            set_head = False
        else:
            append_string(self.__applied_file, patch.get_name())
            set_head = True

        if commit:
            if top:
                top_commit = git.get_commit(top)
            else:
                bottom = head
                top_commit = git.get_commit(head)

            # create a commit for the patch (may be empty if top == bottom);
            # only commit on top of the current branch
            assert(unapplied or bottom == head)
            commit_id = git.commit(message = descr, parents = [bottom],
                                   cache_update = False,
                                   tree_id = top_commit.get_tree(),
                                   allowempty = True, set_head = set_head,
                                   author_name = author_name,
                                   author_email = author_email,
                                   author_date = author_date,
                                   committer_name = committer_name,
                                   committer_email = committer_email)
            # set the patch top to the new commit
            patch.set_top(commit_id)
        else:
            patch.set_top(top)

        self.log_patch(patch, 'new')

        return patch
Exemple #10
0
 def set_hidden(self, hidden):
     write_strings(self.__hidden_file, hidden)
Exemple #11
0
 def set_unapplied(self, unapplied):
     write_strings(self.__unapplied_file, unapplied)
Exemple #12
0
 def set_applied(self, applied):
     write_strings(self.__applied_file, applied)
Exemple #13
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
Exemple #14
0
    def new_patch(
        self,
        name,
        message=None,
        can_edit=True,
        unapplied=False,
        show_patch=False,
        top=None,
        bottom=None,
        commit=True,
        author_name=None,
        author_email=None,
        author_date=None,
        committer_name=None,
        committer_email=None,
        sign_str=None,
    ):
        """Creates a new patch, either pointing to an existing commit object,
        or by creating a new commit object.
        """

        assert commit or (top and bottom)
        assert (top and bottom) or (not top and not bottom)
        assert commit or (
            not top
            or bottom == git.get_commit(top).get_parent()
        )

        if name is not None:
            self.__patch_name_valid(name)
            if self.patch_exists(name):
                raise StackException('Patch "%s" already exists' % name)

        # TODO: move this out of the stgit.stack module, it is really
        # for higher level commands to handle the user interaction
        def sign(msg):
            return add_sign_line(msg, sign_str,
                                 committer_name or git.committer().name,
                                 committer_email or git.committer().email)
        if not message and can_edit:
            descr = edit_file(
                self, sign(''),
                'Please enter the description for the patch above.',
                show_patch)
        else:
            descr = sign(message)

        head = git.get_head()

        if name is None:
            name = make_patch_name(descr, self.patch_exists)

        patch = self.get_patch(name)
        patch.create()

        patch.set_description(descr)
        patch.set_authname(author_name)
        patch.set_authemail(author_email)
        patch.set_authdate(author_date)
        patch.set_commname(committer_name)
        patch.set_commemail(committer_email)

        if unapplied:
            patches = [patch.get_name()] + self.get_unapplied()
            write_strings(self.__unapplied_file, patches)
            set_head = False
        else:
            append_string(self.__applied_file, patch.get_name())
            set_head = True

        if commit:
            if top:
                top_commit = git.get_commit(top)
            else:
                bottom = head
                top_commit = git.get_commit(head)

            # create a commit for the patch (may be empty if top == bottom);
            # only commit on top of the current branch
            assert(unapplied or bottom == head)
            commit_id = git.commit(
                message=descr,
                parents=[bottom],
                cache_update=False,
                tree_id=top_commit.get_tree(),
                allowempty=True,
                set_head=set_head,
                author_name=author_name,
                author_email=author_email,
                author_date=author_date,
                committer_name=committer_name,
                committer_email=committer_email,
            )
            # set the patch top to the new commit
            patch.set_top(commit_id)
        else:
            patch.set_top(top)

        self.log_patch(patch, 'new')

        return patch
Exemple #15
0
 def set_hidden(self, hidden):
     write_strings(self.__hidden_file, hidden)
Exemple #16
0
 def set_unapplied(self, unapplied):
     write_strings(self.__unapplied_file, unapplied)
Exemple #17
0
 def set_applied(self, applied):
     write_strings(self.__applied_file, applied)
Exemple #18
0
    def forward_patches(self, names):
        """Try to fast-forward an array of patches.

        On return, patches in names[0:returned_value] have been pushed on the
        stack. Apply the rest with push_patch
        """
        unapplied = self.get_unapplied()

        forwarded = 0
        top = git.get_head()

        for name in names:
            assert(name in unapplied)

            patch = self.get_patch(name)

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

            # top != bottom always since we have a commit for each patch
            if head == bottom:
                # reset the backup information. No logging since the
                # patch hasn't changed
                patch.set_top(top, backup = True)

            else:
                head_tree = git.get_commit(head).get_tree()
                bottom_tree = git.get_commit(bottom).get_tree()
                if head_tree == bottom_tree:
                    # We must just reparent this patch and create a new commit
                    # for it
                    descr = patch.get_description()
                    author_name = patch.get_authname()
                    author_email = patch.get_authemail()
                    author_date = patch.get_authdate()
                    committer_name = patch.get_commname()
                    committer_email = patch.get_commemail()

                    top_tree = git.get_commit(top).get_tree()

                    top = git.commit(message = descr, parents = [head],
                                     cache_update = False,
                                     tree_id = top_tree,
                                     allowempty = True,
                                     author_name = author_name,
                                     author_email = author_email,
                                     author_date = author_date,
                                     committer_name = committer_name,
                                     committer_email = committer_email)

                    patch.set_top(top, backup = True)

                    self.log_patch(patch, 'push(f)')
                else:
                    top = head
                    # stop the fast-forwarding, must do a real merge
                    break

            forwarded+=1
            unapplied.remove(name)

        if forwarded == 0:
            return 0

        git.switch(top)

        append_strings(self.__applied_file, names[0:forwarded])
        write_strings(self.__unapplied_file, unapplied)

        return forwarded
Exemple #19
0
    def forward_patches(self, names):
        """Try to fast-forward an array of patches.

        On return, patches in names[0:returned_value] have been pushed on the
        stack. Apply the rest with push_patch
        """
        unapplied = self.get_unapplied()

        forwarded = 0
        top = git.get_head()

        for name in names:
            assert(name in unapplied)

            patch = self.get_patch(name)

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

            # top != bottom always since we have a commit for each patch
            if head == bottom:
                # reset the backup information. No logging since the
                # patch hasn't changed
                patch.set_top(top, backup=True)

            else:
                head_tree = git.get_commit(head).get_tree()
                bottom_tree = git.get_commit(bottom).get_tree()
                if head_tree == bottom_tree:
                    # We must just reparent this patch and create a new commit
                    # for it
                    descr = patch.get_description()
                    author_name = patch.get_authname()
                    author_email = patch.get_authemail()
                    author_date = patch.get_authdate()
                    committer_name = patch.get_commname()
                    committer_email = patch.get_commemail()

                    top_tree = git.get_commit(top).get_tree()

                    top = git.commit(
                        message=descr,
                        parents=[head],
                        cache_update=False,
                        tree_id=top_tree,
                        allowempty=True,
                        author_name=author_name,
                        author_email=author_email,
                        author_date=author_date,
                        committer_name=committer_name,
                        committer_email=committer_email,
                    )

                    patch.set_top(top, backup=True)

                    self.log_patch(patch, 'push(f)')
                else:
                    top = head
                    # stop the fast-forwarding, must do a real merge
                    break

            forwarded += 1
            unapplied.remove(name)

        if forwarded == 0:
            return 0

        git.switch(top)

        append_strings(self.__applied_file, names[0:forwarded])
        write_strings(self.__unapplied_file, unapplied)

        return forwarded
Exemple #20
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
Exemple #21
0
 def _write_file(self, fn, val):
     utils.write_strings(os.path.join(self._stack.directory, fn), val)
Exemple #22
0
 def __write_file(self, fn, val):
     utils.write_strings(os.path.join(self.__stack.directory, fn), val)