Example #1
0
 def __init__(self, cwd, quilt_pc, quilt_patches):
     super(Delete, 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)
     self.pop = Pop(cwd, quilt_pc)
Example #2
0
    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)
Example #3
0
    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)
Example #4
0
class PushTest(QuiltTest):

    data_dir = Directory(os.path.join(test_dir, "data", "push"))

    def test_apply_all(self):
        patch2 = Patch("p2.patch")

        test_dir = self.data_dir + "test1"

        with TmpDirectory(dir=self.data_dir.get_name()) as tmp_dir:
            tmp_test_dir = tmp_dir + "test1"
            test_dir.copy(tmp_test_dir)

            pc_dir = tmp_test_dir + "pc"
            patches_dir = tmp_test_dir + "patches"

            f1 = tmp_test_dir + File("f1")
            self.assertFalse(f1.exists())
            f2 = tmp_test_dir + File("f2")
            self.assertFalse(f2.exists())

            push = Push(tmp_test_dir.get_name(), pc_dir.get_name(),
                        patches_dir.get_name())

            self.assertEqual(None, push.db.top_patch())
            push.apply_all(quiet=True)
            self.assertEqual(patch2, push.db.top_patch())

            self.assertTrue(f1.exists())
            self.assertTrue(f2.exists())

    def test_apply_next(self):
        patch1 = Patch("p1.patch")
        patch2 = Patch("p2.patch")

        test_dir = self.data_dir + "test2"

        with TmpDirectory(dir=self.data_dir.get_name()) as tmp_dir:
            tmp_test_dir = tmp_dir + "test2"
            test_dir.copy(tmp_test_dir)

            pc_dir = tmp_test_dir + "pc"
            patches_dir = tmp_test_dir + "patches"

            f1 = tmp_test_dir + File("f1")
            self.assertFalse(f1.exists())
            f2 = tmp_test_dir + File("f2")
            self.assertFalse(f2.exists())

            push = Push(tmp_test_dir.get_name(), pc_dir.get_name(),
                        patches_dir.get_name())
            self.assertEqual(None, push.db.top_patch())

            push.apply_next_patch(quiet=True)
            self.assertEqual(patch1, push.db.top_patch())

            self.assertTrue(f1.exists())
            self.assertFalse(f2.exists())

            push.apply_next_patch(quiet=True)
            self.assertEqual(patch2, push.db.top_patch())

            self.assertTrue(f1.exists())
            self.assertTrue(f2.exists())
    
    def test_upto_applied(self):
        """ Push up to a specified patch when a patch is already applied """
        top = os.path.join(test_dir, "data", "pop", "test1")
        pc = os.path.join(top, "pc")
        patches = os.path.join(top, "patches")
        cmd = Push(top, pc, patches)
        self.assertRaises(AllPatchesApplied, cmd.apply_patch, "p1.patch")
    
    def test_force(self):
        with tmp_series() as [dir, series]:
            self._make_conflict(dir, series)
            series.save()
            cmd = Push(dir, quilt_pc=dir, quilt_patches=series.dirname)
            with six.assertRaisesRegex(
                        self, QuiltError, r"does not apply"), \
                    self._suppress_output():
                cmd.apply_next_patch(quiet=True)
            with six.assertRaisesRegex(self, QuiltError,
                        r"Applied patch.*needs refresh"), \
                    self._suppress_output():
                cmd.apply_next_patch(quiet=True, force=True)
    
    def test_without_refresh(self):
        with tmp_series() as [dir, series]:
            self._make_conflict(dir, series)
            series.add_patch("p2")
            series.save()
            cmd = Push(dir, quilt_pc=dir, quilt_patches=series.dirname)
            with six.assertRaisesRegex(self, QuiltError,
                        r"Applied patch.*needs refresh"), \
                    self._suppress_output():
                cmd.apply_next_patch(quiet=True, force=True)
            with six.assertRaisesRegex(self, QuiltError,
                    r"needs to be refreshed"):
                cmd.apply_next_patch()
    
    def test_fail_after_success(self):
        """ Test where the first patch applies but a later patch fails """
        with tmp_series() as [dir, series]:
            make_file(
                b"--- /dev/null\n"
                b"+++ dir/new-file\n"
                b"@@ -0,0 +1,1 @@\n"
                b"+new file\n", series.dirname, "good.patch")
            series.add_patch(Patch("good.patch"))
            
            self._make_conflict(dir, series)
            series.save()
            cmd = Push(dir, quilt_pc=dir, quilt_patches=series.dirname)
            with six.assertRaisesRegex(self, QuiltError,
                        r"conflict\.patch does not apply"), \
                    self._suppress_output():
                cmd.apply_all()
            [applied] = Db(dir).patches()
            self.assertEqual(applied.get_name(), "good.patch")
            with open(os.path.join(dir, "new-file"), "rb") as file:
                self.assertEqual(file.read(), b"new file\n")
            with open(os.path.join(dir, "file"), "rb") as file:
                self.assertEqual(file.read(), b"conflict\n")
    
    def _make_conflict(self, dir, series):
        series.add_patch(Patch("conflict.patch"))
        make_file(
            b"--- orig/file\n"
            b"+++ new/file\n"
            b"@@ -1 +1 @@\n"
            b"-old\n"
            b"+new\n", series.dirname, "conflict.patch")
        make_file(b"conflict\n", dir, "file")
    
    @contextmanager
    def _suppress_output(self):
        """ Silence error messages from the "patch" command """
        STDOUT_FILENO = 1
        STDERR_FILENO = 2
        with open(os.devnull, "w") as null:
            stdout = os.dup(STDOUT_FILENO)
            stderr = os.dup(STDERR_FILENO)
            os.dup2(null.fileno(), STDOUT_FILENO)
            os.dup2(null.fileno(), STDERR_FILENO)
        try:
            yield
        finally:
            os.dup2(stdout, STDOUT_FILENO)
            os.dup2(stderr, STDERR_FILENO)
            os.close(stdout)
            os.close(stderr)
Example #5
0
 def __init__(self, cwd, quilt_pc):
     super(Pop, self).__init__(cwd)
     self.quilt_pc = Directory(quilt_pc)
     self.db = Db(quilt_pc)
Example #6
0
 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)
Example #7
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)
Example #8
0
 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)
Example #9
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)
Example #10
0
class Push(Command):

    applying = Signal()
    applying_patch = Signal()
    applied = Signal()
    applied_patch = Signal()
    applied_empty_patch = Signal()

    def __init__(self, cwd, quilt_pc, quilt_patches):
        super(Push, 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 _apply_patch(self, patch, force=False, quiet=False):
        patch_name = patch.get_name()
        pc_dir = self.quilt_pc + patch_name
        patch_file = self.quilt_patches + File(patch_name)
        refresh = File(pc_dir.get_name() + "~refresh")

        forced = False
        self.applying_patch(patch)

        if patch_file.exists():
            try:
                patch.run(self.cwd, patch_dir=self.quilt_patches, backup=True,
                          prefix=pc_dir.get_name(), quiet=quiet)
            except SubprocessError as e:
                if not force:
                    patch = RollbackPatch(self.cwd, pc_dir)
                    patch.rollback()
                    patch.delete_backup()
                    raise QuiltError("Patch %s does not apply" % patch_name)
                else:
                    refresh.touch()
                    forced = True

        self.db.add_patch(patch)

        if pc_dir.exists():
            timestamp = pc_dir + File(".timestamp")
            timestamp.touch()
        else:
            pc_dir.create()

        if not patch_file.exists():
            self.applied_empty_patch(patch, False)
        elif pc_dir.is_empty():
            self.applied_empty_patch(patch, True)
        elif forced:
            raise QuiltError("Applied patch %s (forced; needs refresh)" %
                             patch.get_name())
        else:
            self.applied_patch(patch)

    def _check(self):
        if not self.series.exists() or not self.series.patches():
            raise NoPatchesInSeries(self.series)
        
        top = self.db.top_patch()
        if top is not None:
            refresh = top.get_name() + "~refresh"
            refresh = os.path.join(self.quilt_pc.get_name(), refresh)
            if os.path.exists(refresh):
                raise QuiltError("Patch %s needs to be refreshed" % \
                                      top.get_name())

    def apply_patch(self, patch_name, force=False, quiet=False):
        """ Apply all patches up to patch_name """
        self._check()
        patch = Patch(patch_name)
        patches = self.series.patches_until(patch)[:]

        applied = self.db.applied_patches()
        for patch in applied:
            if patch in patches:
                patches.remove(patch)

        if not patches:
            raise AllPatchesApplied(self.series, self.db.top_patch())

        self.applying(patch)

        try:
            for cur_patch in patches:
                self._apply_patch(cur_patch, force, quiet)
        finally:
            self.db.save()

        self.applied(self.db.top_patch())

    def apply_next_patch(self, force=False, quiet=False):
        """ Apply next patch in series file """
        self._check()
        top = self.db.top_patch()
        if not top:
            patch = self.series.first_patch()
        else:
            patch = self.series.patch_after(top)

        if not patch:
            raise AllPatchesApplied(self.series, top)

        self.applying(patch)

        self._apply_patch(patch, force, quiet)

        self.db.save()

        self.applied(self.db.top_patch())

    def apply_all(self, force=False, quiet=False):
        """ Apply all patches in series file """
        self._check()
        top = self.db.top_patch()
        if top:
            patches = self.series.patches_after(top)
        else:
            patches = self.series.patches()

        if not patches:
            raise AllPatchesApplied(self.series, top)

        try:
            for patch in patches:
                self.applying(patch)
                self._apply_patch(patch, force, quiet)
        finally:
            self.db.save()

        self.applied(self.db.top_patch())
Example #11
0
class PopTest(QuiltTest):

    data_dir = Directory(os.path.join(test_dir, "data", "pop"))

    def test_unapply_all(self):
        patch2 = Patch("p2.patch")

        test_dir = self.data_dir + "test1"

        with TmpDirectory(dir=self.data_dir.get_name()) as tmp_dir:
            tmp_test_dir = tmp_dir + "test1"
            test_dir.copy(tmp_test_dir)

            pc_dir = tmp_test_dir + "pc"

            f1 = tmp_test_dir + File("f1")
            self.assertTrue(f1.exists())
            f2 = tmp_test_dir + File("f2")
            self.assertTrue(f2.exists())

            pop = Pop(tmp_test_dir.get_name(), pc_dir.get_name())

            self.assertEqual(patch2, pop.db.top_patch())
            pop.unapply_all()
            self.assertEqual(None, pop.db.top_patch())

            self.assertFalse(f1.exists())
            self.assertFalse(f2.exists())

    def test_apply_next(self):
        patch1 = Patch("p1.patch")
        patch2 = Patch("p2.patch")

        test_dir = self.data_dir + "test1"

        with TmpDirectory(dir=self.data_dir.get_name()) as tmp_dir:
            tmp_test_dir = tmp_dir + "test2"

            test_dir.copy(tmp_test_dir)

            pc_dir = tmp_test_dir + "pc"

            f1 = tmp_test_dir + File("f1")
            self.assertTrue(f1.exists())
            f2 = tmp_test_dir + File("f2")
            self.assertTrue(f2.exists())

            pop = Pop(tmp_test_dir.get_name(), pc_dir.get_name())
            self.assertEqual(patch2, pop.db.top_patch())

            pop.unapply_top_patch()
            self.assertEqual(patch1, pop.db.top_patch())

            self.assertTrue(f1.exists())
            self.assertFalse(f2.exists())

            pop.unapply_top_patch()
            self.assertEqual(None, pop.db.top_patch())

            self.assertFalse(f1.exists())
            self.assertFalse(f2.exists())
    
    def test_unrefreshed(self):
        with TmpDirectory() as dir:
            db = Db(dir.get_name())
            db.add_patch(Patch("unrefreshed.patch"))
            db.save()
            make_file(b"", db.dirname, "unrefreshed.patch~refresh")
            cmd = Pop(dir.get_name(), db.dirname)
            with six.assertRaisesRegex(self, QuiltError,
                    r"needs to be refreshed"):
                cmd.unapply_top_patch()