Ejemplo n.º 1
0
class Add(Command):

    """Command class to add files to the current patch
    """

    file_added = Signal()

    def __init__(self, cwd, quilt_pc, quilt_patches):
        super(Add, self).__init__(cwd)
        self.quilt_pc = Directory(quilt_pc)
        self.quilt_patches = Directory(quilt_patches)
        self.db = Db(quilt_pc)
        self.series = Series(quilt_patches)

    def _file_in_patch(self, filename, patch, ignore):
        """ Checks if a backup file of the filename in the current patch
        exists """
        file = self.quilt_pc + File(os.path.join(patch.get_name(), filename))
        if file.exists():
            if ignore:
                return True
            else:
                raise QuiltError("File %s is already in patch %s" % (filename,
                                 patch.get_name()))
        return False

    def _file_in_next_patches(self, filename, patch):
        """ Checks if a backup file of the filename in the applied patches after
        patch exists """

        if not self.db.is_patch(patch):
            # no patches applied
            return

        patches = self.db.patches_after(patch)
        for patch in patches:
            file = self.quilt_pc + File(os.path.join(patch.get_name(),
                                                     filename))
            if file.exists():
                raise QuiltError("File %s is already modified by patch %s" %
                                 (filename, patch.get_name()))

    def _backup_file(self, file, patch):
        """ Creates a backup of file """
        dest_dir = self.quilt_pc + patch.get_name()
        file_dir = file.get_directory()
        if file_dir:
            #TODO get relative path
            dest_dir = dest_dir + file_dir
        backup = Backup()
        backup.backup_file(file, dest_dir, copy_empty=True)

    def add_file(self, filename, patch_name=None, ignore=False):
        """ Add file to the patch with patch_name.
        If patch_name is None or empty the topmost patch will be used.
        Adding an already added patch will raise an QuiltError if ignore is
        False.
        """
        file = File(filename)

        if patch_name:
            patch = Patch(patch_name)
        else:
            patch = self.db.top_patch()
            if not patch:
                raise NoAppliedPatch(self.db)

        exists = self._file_in_patch(filename, patch, ignore)
        if exists:
            return

        self._file_in_next_patches(filename, patch)

        if file.is_link():
            raise QuiltError("Cannot add symbolic link %s" % filename)

        self._backup_file(file, patch)

        if file.exists():
            # be sure user can write original file
            os.chmod(filename, file.get_mode() | stat.S_IWUSR | stat.S_IRUSR)

        self.file_added(file, patch)

    def add_files(self, filenames, patch_name=None, ignore=False):
        for filename in filenames:
            self.add_file(filename, patch_name, ignore)
Ejemplo n.º 2
0
class Revert(Command):

    """Command class to remove files from the current patch
    """

    file_reverted = Signal()
    file_unchanged = Signal()

    def __init__(self, cwd, quilt_pc, quilt_patches):
        super(Revert, self).__init__(cwd)
        self.quilt_pc = Directory(quilt_pc)
        self.quilt_patches = Directory(quilt_patches)
        self.db = Db(quilt_pc)
        self.series = Series(quilt_patches)

    def _file_in_patch(self, filename, patch):
        """ Checks if a backup file of the filename in the current patch
        exists and raises a QuiltError if not.
        """
        pc_dir = self.quilt_pc + patch.get_name()
        file = pc_dir + File(filename)
        if not file.exists():
            raise QuiltError("File %s is not in patch %s" % (filename,
                             patch.get_name()))

    def _file_in_next_patches(self, filename, patch):
        """ Checks if a backup file of the filename in the applied patches after
        patch exists """

        if not self.db.is_patch(patch):
            # no patches applied
            return

        patches = self.db.patches_after(patch)
        for patch in patches:
            file = self.quilt_pc + File(os.path.join(patch.get_name(),
                                                     filename))
            if file.exists():
                raise QuiltError("File %s is modified by patch %s" %
                                 (filename, patch.get_name()))

    def _apply_patch_temporary(self, tmpdir, file, patch):
        backup = Backup()
        backup_file = backup.backup_file(file, tmpdir)
        patch_file = self.quilt_patches + File(patch.get_name())

        if patch_file.exists() and not patch_file.is_empty():
            try:
                patch.run(self.cwd, self.quilt_patches.get_absdir(),
                          work_dir=tmpdir, no_backup_if_mismatch=True,
                          remove_empty_files=True, force=True,
                          quiet=True, suppress_output=True,
                )
            except SubprocessError:
                pass  # Expected to fail if there are other files in patch
        return backup_file

    def revert_file(self, filename, patch_name=None):
        """ Revert not added changes of filename.
        If patch_name is None or empty the topmost patch will be used.
        """
        file = File(filename)

        if patch_name:
            patch = Patch(patch_name)
        else:
            patch = self.db.top_patch()

            if not patch:
                raise QuiltError("No patch available. Nothing to revert.")

        self._file_in_patch(filename, patch)
        self._file_in_next_patches(filename, patch)
        pc_dir = self.quilt_pc + patch.get_name()
        pc_file = pc_dir + file

        if not file.exists() and pc_file.is_empty():
            # new and empty file will be reverted
            pc_file.delete()
            self.file_reverted(file, patch)
            return

        with TmpDirectory(prefix="pquilt-") as tmpdir:
            # apply current patch in temporary directory to revert changes of
            # file that aren't committed in the patch
            tmp_file = self._apply_patch_temporary(tmpdir, pc_file, patch)
            if tmp_file and tmp_file.exists() and not tmp_file.is_empty():

                diff = Diff(file, tmp_file)
                if diff.equal(self.cwd):
                    self.file_unchanged(file, patch)
                    return

                dir = file.get_directory()
                if not dir:
                    dir = Directory(os.getcwd())
                else:
                    dir.create()
                tmp_file.copy(dir)
                self.file_reverted(file, patch)
            else:
                self.file_unchanged(file, patch)

    def revert_files(self, filenames, patch_name=None):
        for filename in filenames:
            self.revert_file(filename, patch_name)
Ejemplo n.º 3
0
class Pop(Command):

    unapplying = Signal()
    unapplied = Signal()
    unapplied_patch = Signal()
    empty_patch = Signal()

    def __init__(self, cwd, quilt_pc):
        super(Pop, self).__init__(cwd)
        self.quilt_pc = Directory(quilt_pc)
        self.db = Db(quilt_pc)

    def _check(self, force=False):
        if not self.db.exists() or not self.db.patches():
            raise NoAppliedPatch(self.db)
        if not force:
            patch = self.db.top_patch()
            pc_dir = self.quilt_pc + patch.get_name()
            refresh = File(pc_dir.get_name() + "~refresh")
            if refresh.exists():
                raise QuiltError("Patch %s needs to be refreshed first." %
                                 patch.get_name())

    def _unapply_patch(self, patch):
        self.unapplying(patch)

        patch_name = patch.get_name()
        pc_dir = self.quilt_pc + patch_name
        timestamp = pc_dir + File(".timestamp")
        timestamp.delete_if_exists()

        if pc_dir.is_empty():
            pc_dir.delete()
            self.empty_patch(patch)
        else:
            unpatch = RollbackPatch(self.cwd, pc_dir)
            unpatch.rollback()
            unpatch.delete_backup()

        self.db.remove_patch(patch)

        refresh = File(pc_dir.get_name() + "~refresh")
        refresh.delete_if_exists()

        self.unapplied_patch(patch)

    def unapply_patch(self, patch_name, force=False):
        """ Unapply patches up to patch_name. patch_name will end up as top
            patch """
        self._check(force)

        patches = self.db.patches_after(Patch(patch_name))
        for patch in reversed(patches):
            self._unapply_patch(patch)

        self.db.save()

        self.unapplied(self.db.top_patch())

    def unapply_top_patch(self, force=False):
        """ Unapply top patch """
        self._check(force)

        patch = self.db.top_patch()
        self._unapply_patch(patch)

        self.db.save()

        self.unapplied(self.db.top_patch())

    def unapply_all(self, force=False):
        """ Unapply all patches """
        self._check(force)

        for patch in reversed(self.db.applied_patches()):
            self._unapply_patch(patch)

        self.db.save()

        self.unapplied(self.db.top_patch())
Ejemplo n.º 4
0
class Revert(Command):
    """Command class to remove files from the current patch
    """

    file_reverted = Signal()
    file_unchanged = Signal()

    def __init__(self, cwd, quilt_pc, quilt_patches):
        super(Revert, self).__init__(cwd)
        self.quilt_pc = Directory(quilt_pc)
        self.quilt_patches = Directory(quilt_patches)
        self.db = Db(quilt_pc)
        self.series = Series(quilt_patches)

    def _file_in_patch(self, filename, patch):
        """ Checks if a backup file of the filename in the current patch
        exists and raises a QuiltError if not.
        """
        pc_dir = self.quilt_pc + patch.get_name()
        file = pc_dir + File(filename)
        if not file.exists():
            raise QuiltError("File %s is not in patch %s" %
                             (filename, patch.get_name()))

    def _file_in_next_patches(self, filename, patch):
        """ Checks if a backup file of the filename in the applied patches after
        patch exists """

        if not self.db.is_patch(patch):
            # no patches applied
            return

        patches = self.db.patches_after(patch)
        for patch in patches:
            file = self.quilt_pc + File(
                os.path.join(patch.get_name(), filename))
            if file.exists():
                raise QuiltError("File %s is modified by patch %s" %
                                 (filename, patch.get_name()))

    def _apply_patch_temporary(self, tmpdir, file, patch):
        backup = Backup()
        backup_file = backup.backup_file(file, tmpdir)
        patch_file = self.quilt_patches + File(patch.get_name())

        if patch_file.exists() and not patch_file.is_empty():
            try:
                patch.run(
                    self.cwd,
                    self.quilt_patches.get_absdir(),
                    work_dir=tmpdir,
                    no_backup_if_mismatch=True,
                    remove_empty_files=True,
                    force=True,
                    quiet=True,
                    suppress_output=True,
                )
            except SubprocessError:
                pass  # Expected to fail if there are other files in patch
        return backup_file

    def revert_file(self, filename, patch_name=None):
        """ Revert not added changes of filename.
        If patch_name is None or empty the topmost patch will be used.
        """
        file = File(filename)

        if patch_name:
            patch = Patch(patch_name)
        else:
            patch = self.db.top_patch()

            if not patch:
                raise QuiltError("No patch available. Nothing to revert.")

        self._file_in_patch(filename, patch)
        self._file_in_next_patches(filename, patch)
        pc_dir = self.quilt_pc + patch.get_name()
        pc_file = pc_dir + file

        if not file.exists() and pc_file.is_empty():
            # new and empty file will be reverted
            pc_file.delete()
            self.file_reverted(file, patch)
            return

        with TmpDirectory(prefix="pquilt-") as tmpdir:
            # apply current patch in temporary directory to revert changes of
            # file that aren't committed in the patch
            tmp_file = self._apply_patch_temporary(tmpdir, pc_file, patch)
            if tmp_file and tmp_file.exists() and not tmp_file.is_empty():

                diff = Diff(file, tmp_file)
                if diff.equal(self.cwd):
                    self.file_unchanged(file, patch)
                    return

                dir = file.get_directory()
                if not dir:
                    dir = Directory(os.getcwd())
                else:
                    dir.create()
                tmp_file.copy(dir)
                self.file_reverted(file, patch)
            else:
                self.file_unchanged(file, patch)

    def revert_files(self, filenames, patch_name=None):
        for filename in filenames:
            self.revert_file(filename, patch_name)