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)
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)
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())
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)