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 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 run(self, suppress_output=False, inputdata=None, **kw): """Run command as a subprocess and wait until it is finished. The command should be given as a list of strings to avoid problems with shell quoting. If the command exits with a return code other than 0, a SubprocessError is raised. """ if inputdata is not None: kw["stdin"] = subprocess.PIPE if suppress_output: kw["stdout"] = open(os.devnull, "w") kw["stderr"] = kw["stdout"] try: try: process = subprocess.Popen(self.cmd, **kw) finally: if suppress_output: kw["stdout"].close() except OSError as e: msg = "Failed starting command {!r}: {}".format(self.cmd, e) raise QuiltError(msg) if inputdata is not None: process.stdin.write(inputdata) process.stdin.close() ret = process.wait() if ret != 0: raise SubprocessError(self.cmd, ret)
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 _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 _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 _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, 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 delete_next(self, remove=False, backup=False): """ Delete next unapplied patch If remove is True the patch file will also be removed. If remove and backup are True a copy of the deleted patch file will be made. """ patch = self.db.top_patch() if patch: after = self.series.patch_after(patch) else: after = self.series.first_patch() if not after: raise QuiltError("No next patch") self._delete_patch(after, remove=remove, backup=backup)
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 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)