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 refresh(self, patch_name=None, edit=False): """ Refresh patch with patch_name or applied top patch if patch_name is None """ if patch_name: patch = Patch(patch_name) else: patch = self.db.top_patch() if not patch: raise QuiltError("No patch applied. Nothing to refresh.") pc_dir = self.quilt_pc + patch.get_name() patch_file = self.quilt_patches + File(patch.get_name()) files = pc_dir.content()[1] with TmpFile(prefix="pquilt-") as tmpfile: f = tmpfile.open() if patch_file.exists(): header = patch.get_header(self.quilt_patches) tmpfile.write(header) for file_name in files: if file_name == ".timestamp": continue orig_file = pc_dir + File(file_name) new_file = File(file_name) left_label, right_label, index = self._get_labels( file_name, orig_file, new_file) self._write_index(tmpfile, index) diff = Diff(orig_file, new_file) diff.run(self.cwd, fd=f, left_label=left_label, right_label=right_label) if tmpfile.is_empty(): raise QuiltError("Nothing to refresh.") if edit: self.edit_patch(tmpfile) tpatch = Patch(tmpfile.get_name()) tpatch.run(pc_dir.get_name(), dry_run=True, quiet=True) if patch_file.exists(): diff = Diff(patch_file, tmpfile) if diff.equal(self.cwd): raise QuiltError("Nothing to refresh.") tmpfile.copy(patch_file) timestamp = pc_dir + File(".timestamp") timestamp.touch() refresh = self.quilt_pc + File(patch.get_name() + "~refresh") refresh.delete_if_exists() self.refreshed(patch)
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 _delete_patch(self, patch, remove=False, backup=False): if self.series.is_empty(): raise NoPatchesInSeries(self.series) if not self.series.is_patch(patch): raise UnknownPatch(self.series, patch) applied = self.db.top_patch() == patch self.deleting_patch(patch, applied) if applied: self.pop._unapply_patch(patch) self.db = self.pop.db self.db.save() self.series.remove_patch(patch) self.series.save() patch_file = self.quilt_patches + File(patch.get_name()) if remove: if backup: patch_file.copy(File(patch_file.get_name() + "~")) patch_file.delete_if_exists() self.deleted_patch(patch)
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)
class Diff(object): """ Wrapper around the diff util """ @FileParam(["left", "right"]) def __init__(self, left, right): """ left points to the first file and right to the second file """ self.left = left if not self.left.exists(): self.left = File("/dev/null") self.right = right if not self.right.exists(): self.right = File("/dev/null") def run(self, cwd, left_label=None, right_label=None, unified=True, fd=None): cmd = ["diff"] if unified: cmd.append("-u") if left_label: cmd.append("--label") cmd.append(left_label) if right_label: if not left_label: cmd.append("--label") cmd.append(self.right.get_name()) cmd.append("--label") cmd.append(right_label) cmd.append(self.left.get_name()) cmd.append(self.right.get_name()) try: Process(cmd).run(cwd=cwd, stdout=fd) except SubprocessError as e: if e.get_returncode() > 1: raise e def equal(self, cwd): """ Returns True if left and right are equal """ cmd = ["diff"] cmd.append("-q") cmd.append(self.left.get_name()) cmd.append(self.right.get_name()) try: Process(cmd).run(cwd=cwd, suppress_output=True) except SubprocessError as e: if e.get_returncode() == 1: return False else: raise e return True
def backup_dir(self, src_dir, dest_dir, copy_empty=False): for file_name in src.files(): file = File(file_name) file_dir = file.get_directory() if file_dir: dest = dest_dir + file_dir else: dest = dest_dir self.backup_file(file, dest, copy_empty)
def backup_dir(self, src_dir, dest_dir, copy_empty=False): for file_name in src_dir.files(): file = File(file_name) file_dir = file.get_directory() if file_dir: dest = dest_dir + file_dir else: dest = dest_dir self.backup_file(file, dest, copy_empty)
def __init__(self, left, right): """ left points to the first file and right to the second file """ self.left = left if not self.left.exists(): self.left = File("/dev/null") self.right = right if not self.right.exists(): self.right = File("/dev/null")
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 import_patches(self, patches): """ Import several patches into the patch queue """ dest_dir = self.quilt_patches patch_names = [] for patch in patches: patch_name = os.path.basename(patch) patch_file = File(patch) dest_file = dest_dir + File(patch_name) patch_file.copy(dest_file) patch_names.append(patch_name) self._import_patches(patch_names)
def create(self, patchname): """ Adds a new patch with patchname to the queue The new patch will be added as the topmost applied patch. """ patch = Patch(patchname) if self.series.is_patch(patch): raise PatchAlreadyExists(self.series, patchname) patch_dir = self.quilt_patches patch_dir.create() patchfile = patch_dir + File(patchname) patchfile.touch() pc_dir = self.quilt_pc + patchname if pc_dir.exists(): # be sure that the directory is clear pc_dir.delete() # create empty .pc/<patchname> directory as quilt does too pc_dir.create() top = self.db.top_patch() # add new patch after the current topmost applied patch self.series.add_patches([patch], top) # "apply" patch self.db.add_patch(patch) # create patches/series files self.series.save() # create .pc/.version and .pc/applied-patches files self.db.save() self.patch_created(patch)
def import_patch(self, patch_name, new_name=None): """ Import patch into the patch queue The patch is inserted after the current top applied patch """ if new_name: dir_name = os.path.dirname(new_name) name = os.path.basename(new_name) dest_dir = self.quilt_patches + Directory(dir_name) dest_dir.create() else: name = os.path.basename(patch_name) dest_dir = self.quilt_patches patch_file = File(patch_name) dest_file = dest_dir + File(name) patch_file.copy(dest_file) self._import_patches([name])
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 backup_files(self, src_dir, dest_dir, filenames, copy_empty=False): for filename in filenames: src_file = src_dir + File(filename) if not src_file.exists(): continue if src_file.is_empty() and not copy_empty: continue self.backup_file(src_file, dest_dir)
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_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 _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") if refresh.exists(): raise QuiltError("Patch %s needs to be refreshed" % \ patch_name) 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) refresh.delete_if_exists() except SubprocessError, e: refresh.touch() if not force: patch = RollbackPatch(self.cwd, pc_dir) patch.rollback() patch.delete_backup() raise QuiltError("Patch %s does not apply" % patch_name) else: forced = True
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 _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 get_header(self, patch_dir=None): """ Returns bytes """ lines = [] if patch_dir: file = patch_dir + File(self.get_name()) name = file.get_name() else: name = self.get_name() with open(name, "rb") as f: for line in f: if line.startswith(b"---") or line.startswith(b"Index:"): break lines.append(line) return b"".join(lines)
def rollback(self, keep=False): (dirs, files) = self.backup_dir.content() for dir in dirs: newdir = self.cwd + dir if not newdir.exists(): newdir.create() for file in files: file = File(file) backup_file = self.backup_dir + file rollback_file = self.cwd + file if not keep: rollback_file.delete_if_exists() if not backup_file.is_empty(): backup_file.copy(rollback_file)
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 _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)