def remove(self, package): """Mark a package for deletion. package is the name of the package to be deleted. A ValueError is raised if package is not under version control. If package has state 'A' it is directly removed. """ super(Project, self).remove(package) with wc_lock(self.path): st = self._status(package) if st == '?': msg = "package \"%s\" is not under version control" % package raise ValueError(msg) elif st == 'A': self._remove_wc_dir(package, notify=False) self._packages.remove(package) else: pkg = self.package(package) if pkg is not None: # only remove files for filename in pkg.files(): pkg.remove(filename) self._packages.set(package, 'D') self._packages.write()
def __init__(self, path, verify_format=True, **kwargs): """Constructs a new project object. path is the path to the working copy. Raises a ValueError exception if path is no valid project working copy. Raises a WCInconsistentError if the wc's metadata is corrupt. Keyword arguments: verify_format -- verify working copy format (default: True) kwargs -- see class WorkingCopy for the details """ if verify_format: wc_verify_format(path) meta, xml_data, pkg_data = self.wc_check(path) if meta or xml_data or pkg_data: raise WCInconsistentError(path, meta, xml_data, pkg_data) self.apiurl = wc_read_apiurl(path) self.name = wc_read_project(path) with wc_lock(path): self._packages = wc_read_packages(path) super(Project, self).__init__(path, ProjectUpdateState, ProjectCommitState, **kwargs)
def update(self, revision='latest', **kwargs): """Update working copy. Keyword arguments: revision -- the update revision (default: latest) **kwargs -- optional arguments for the "getfilelist" http request """ with wc_lock(self.path): ustate = PackageUpdateState.read_state(self.path) if not self.is_updateable(rollback=True): if self.has_conflicts(): raise FileConflictError(self.has_conflicts()) # commit can be the only pending transaction raise PendingTransactionError('commit') elif (ustate is not None and ustate.state == UpdateStateMixin.STATE_UPDATING): self._update(ustate) else: uinfo = self._calculate_updateinfo(revision=revision, **kwargs) self._calculate_skips(uinfo) conflicts = uinfo.conflicted if conflicts: # these are _only_ update conflicts raise FileConflictError(conflicts) if not self._transaction_begin('update', uinfo): return # TODO: if ustate is not None check if we can reuse # existing files # states might also contain dynamic states like '!' or 'M' etc. states = dict([(f, self.status(f)) for f in self.files()]) ustate = PackageUpdateState(self.path, uinfo=uinfo, **states) self._update(ustate)
def update(self, *packages, **kwargs): """Update project working copy. If *packages are specified only the specified packages will be updated. Otherwise all packages will be updated. Keyword arguments: **kwargs -- optional keyword arguments which will be passed to the Package's update method """ with wc_lock(self.path): ustate = ProjectUpdateState.read_state(self.path) if not self.is_updateable(rollback=True): raise PendingTransactionError('commit') if (ustate is not None and ustate.state == UpdateStateMixin.STATE_UPDATING): self._clear_uinfo(ustate) self._update(ustate) else: uinfo = self._calculate_updateinfo(*packages) conflicts = uinfo.conflicted if conflicts: # a package might be in conflicts because # its is_updateable method returned False raise FileConflictError(conflicts) if not self._transaction_begin('prj_update', uinfo): return states = dict([(p, self._status(p)) for p in self.packages()]) ustate = ProjectUpdateState(self.path, uinfo=uinfo, **states) self._update(ustate, **kwargs) self.notifier.finished('prj_update', aborted=False)
def resolved(self, filename): """Remove conflicted state from filename. filename is a filename which has state 'C'. A ValueError is raised if filename is not "conflicted". """ with wc_lock(self.path): self._resolved(filename)
def add(self, filename): """Add filename to working copy. Afterwards filename is tracked with state 'A'. A ValueError is raised if filename does not exist or or is no file or if it is already tracked. """ super(Package, self).add(filename) with wc_lock(self.path): self._add(filename)
def remove(self, filename): """Remove file from the working copy. Actually this marks filename for deletion (filename has state 'D' afterwards). A ValueError is raised if filename is not tracked or if filename is conflicted (has state 'C') or if filename is skipped (has state 'S'). """ super(Package, self).remove(filename) with wc_lock(self.path): self._remove(filename)
def revert(self, *packages): """Reverts the specified packages. If no packages are specified, all packages in the working copy will be reverted. If a package in packages is marked as '?', a ValueError is raised. """ if not packages: packages = self.packages() super(Project, self).revert(*packages) with wc_lock(self.path): for package in packages: self._revert(package)
def revert(self, *filenames): """Revert filenames. If no filenames are specified, all working copy files (except skipped files) will be reverted. If filename is marked as 'C', '?' or 'S' a ValueError is raised. """ if not filenames: filenames = [f for f in self.files() if self.status(f) != 'S'] super(Package, self).revert(*filenames) with wc_lock(self.path): for filename in filenames: self._revert(filename)
def add(self, package, *filenames, **kwargs): """Add a new package to the project. package is the name of the directory which will be added. A ValueError is raised if package is already tracked or if package is already an osc working copy. Also if prj/package does not exist or is no directory a ValueError is raised. If filenames are specified they are added to package. If no filenames are specified all files will be added to the package. A ValueError is raised if filenames and no_files=True is specified. Keyword arguments: no_files -- add no files (default: False) """ super(Project, self).add(package) no_files = kwargs.get('no_files', False) if filenames and no_files: raise ValueError("filenames and no_files are mutually exclusive") with wc_lock(self.path): if self._status(package) != '?': raise ValueError("package \"%s\" is already tracked" % package) pkg_path = os.path.join(self.path, package) if not os.path.isdir(pkg_path): raise ValueError("path \"%s\" is no dir" % pkg_path) elif wc_is_project(pkg_path) or wc_is_package(pkg_path): msg = ("path \"%s\" is already an initialized" "working copy" % pkg_path) raise ValueError(msg) storedir = wc_pkg_data_mkdir(self.path, package) pkg = Package.init(pkg_path, self.name, package, self.apiurl, ext_storedir=storedir) self._packages.add(package, state='A') self._packages.write() if no_files: filenames = [] elif not filenames: filenames = [f for f in os.listdir(pkg.path) if os.path.isfile(os.path.join(pkg.path, f))] for filename in filenames: pkg.add(filename)
def __init__(self, path, skip_handlers=None, commit_policies=None, merge_class=Merge, verify_format=True, **kwargs): """Constructs a new package object. path is the path to the working copy. Raises a ValueError exception if path is no valid package working copy. Raises a WCInconsistentError if the wc's metadata is corrupt. Keyword arguments: skip_handlers -- list of FileSkipHandler objects (default: []) commit_policies -- list of FileCommitPolicy objects (default: None) merge_class -- class which is used for a file merge (default: Merge) verify_format -- verify working copy format (default: True) **kwargs -- see class WorkingCopy for the details """ if verify_format: wc_verify_format(path) (meta, xml_data, pkg_data) = self.wc_check(path) if meta or xml_data or pkg_data: raise WCInconsistentError(path, meta, xml_data, pkg_data) self.apiurl = wc_read_apiurl(path) self.project = wc_read_project(path) self.name = wc_read_package(path) self.skip_handlers = skip_handlers or [] self.commit_policies = commit_policies or [] self.merge_class = merge_class with wc_lock(path): self._files = wc_read_files(path) # call super at the end due to finish_pending_transaction super(Package, self).__init__(path, PackageUpdateState, PackageCommitState, **kwargs)
def commit(self, *packages, **kwargs): """Commit project working copy. If *packages are specified only the specified packages will be commited. Otherwise all packages will be updated. Keyword arguments: package_filenames -- a dict which maps a package to a list of filenames (only these filenames will be committed) (default: {}) comment -- a commit message (default: '') """ with wc_lock(self.path): cstate = ProjectCommitState.read_state(self.path) if not self.is_commitable(rollback=True): raise PendingTransactionError('commit') if (cstate is not None and cstate.state == CommitStateMixin.STATE_COMMITTING): self._clear_cinfo(cstate) self._commit(cstate, {}, '') else: package_filenames = kwargs.get('package_filenames', {}) if [p for p in packages if p in package_filenames]: msg = 'package present in *packages and package_filenames' raise ValueError(msg) packages = list(packages) + package_filenames.keys() cinfo = self._calculate_commitinfo(*packages) conflicts = cinfo.conflicted if conflicts: # a package might be in conflicts because # its is_commitable method returned False raise FileConflictError(conflicts) if not self._transaction_begin('prj_commit', cinfo): return states = dict([(p, self._status(p)) for p in self.packages()]) cstate = ProjectCommitState(self.path, cinfo=cinfo, **states) comment = kwargs.get('comment', '') self._commit(cstate, package_filenames, comment) self.notifier.finished('prj_commit', aborted=False)
def commit(self, *packages, **kwargs): """Commit project working copy. If *packages are specified only the specified packages will be commited. Otherwise all packages will be updated. Keyword arguments: package_filenames -- a dict which maps a package to a list of filenames (only these filenames will be committed) (default: {}) comment -- a commit message (default: '') """ with wc_lock(self.path): cstate = ProjectCommitState.read_state(self.path) if not self.is_commitable(rollback=True): raise PendingTransactionError('commit') if (cstate is not None and cstate.state == CommitStateMixin.STATE_COMMITTING): self._clear_cinfo(cstate) self._commit(cstate, {}, '') else: package_filenames = kwargs.get('package_filenames', {}) if [p for p in packages if p in package_filenames]: msg = 'package present in *packages and package_filenames' raise ValueError(msg) packages = list(packages) + list(package_filenames.keys()) cinfo = self._calculate_commitinfo(*packages) conflicts = cinfo.conflicted if conflicts: # a package might be in conflicts because # its is_commitable method returned False raise FileConflictError(conflicts) if not self._transaction_begin('prj_commit', cinfo): return states = dict([(p, self._status(p)) for p in self.packages()]) cstate = ProjectCommitState(self.path, cinfo=cinfo, **states) comment = kwargs.get('comment', '') self._commit(cstate, package_filenames, comment) self.notifier.finished('prj_commit', aborted=False)
def commit(self, *filenames, **kwargs): """Commit working copy. If no filenames are specified all tracked files are committed. Keyword arguments: **kwargs -- optional parameters for the final commit http request """ with wc_lock(self.path): cstate = self._pending_transaction() if not self.is_commitable(rollback=True): if self.has_conflicts(): raise FileConflictError(self.has_conflicts()) # update can be the only pending transaction raise PendingTransactionError('update') elif (cstate is not None and cstate.state == CommitStateMixin.STATE_COMMITTING): self._commit(cstate) else: cinfo = self._calculate_commitinfo(*filenames) self._apply_commit_policies(cinfo) conflicts = cinfo.conflicted if conflicts: # conflicts shouldn't contain real conflicts because # otherwise is_commitable returns False raise FileConflictError(conflicts) remote = self.latest_revision() local = self._files.revision_data().get('srcmd5') if local != remote: msg = 'commit not possible. Please update first' raise WCOutOfDateError(local, remote, msg) if not self._transaction_begin('commit', cinfo): return states = dict([(f, self.status(f)) for f in self.files()]) cstate = PackageCommitState(self.path, cinfo=cinfo, **states) self._commit(cstate, **kwargs)