def push(self, repo, orig, dest, rev=None, ref_name=None): """ Inherited method :func:`~repoman.repository.Repository.push` """ def update_repo(repo, orig): command_config = ["--config", "extensions.rebase="] command = ["pull", "--rebase", orig, "--tool=false"] repo.rawcommand(command_config + command) return self._new_changeset_object(repo.tip()).hash # Push method should always be ready for the case where there is a new # remote head push_succeded = False last_exception = None MAX_RETRIES = 15 if not rev: rev_hash = "tip" else: rev_hash = rev.hash logger.info("Pushing %s from %s to %s" % (rev_hash, self.path, dest)) retries = 0 while not push_succeded and retries < MAX_RETRIES: try: retries += 1 push_result = repo.push(dest=dest, rev=rev_hash, newbranch=True) if not push_result: logger.info( "hglib.push returned False in %s, retrying..." % self.path) last_exception = "hglib.push returned False..." time.sleep(1) rev_hash = update_repo(repo, orig) else: push_succeded = True except hglib.error.CommandError as e: last_exception = e logger.exception("Push didn't work, why?") logger.debug(e.__str__()) if self.NEW_REMOTE_HEAD_LITERAL not in str(e): logger.error("Error pushing: %s" % e) raise RepositoryError("Error pushing: %s" % e) logger.debug("Error pushing, maybe two heads...") try: rev_hash = update_repo(repo, orig) except hglib.error.CommandError as ex: # Conflicts?? logger.exception("Error merging!") raise RepositoryError("Error merging: %s (%s)" % (e, ex)) if not push_succeded: raise RepositoryError("Five attempts for pushing failed: %s" % last_exception) else: return self[rev_hash]
def wrapped(they, *args, **kwargs): try: with hglib.open(they.path) as repo: return f(they, repo, *args, **kwargs) except self.exceptions as e: if self.error_message: arguments = ", ".join(str(arg) for arg in args) arguments += ", " + str(kwargs) logger.exception("%s (%s)" % (self.error_message, arguments)) raise RepositoryError(self.error_message + str(e)) else: logger.exception(e) raise RepositoryError(e)
def terminate_branch(self, branch_name, repo_origin, repo_dest): """ Inherited method :func:`~repoman.repository.Repository.terminate_branch` """ repo = None try: with hglib.open(self.path) as repo: try: repo.pull(repo_origin, branch=branch_name) except hglib.error.CommandError: # Ignore this error, the branch could be only local thus # the pull can safely fail pass repo.update(branch_name, clean=True) repo.commit(message=str( self.message_builder.close_branch(branch=branch_name)), user=self.signature.user, closebranch=True) parents = repo.parents() self.push(repo_origin, repo_dest, self._new_changeset_object(parents[0])) except hglib.error.CommandError as e: if 'can only close branch heads' in e.err: logger.exception( "Cannot close %s branch, it's already closed" % branch_name) return logger.exception("Error closing the release branch %s: %s" % (branch_name, e)) raise RepositoryError(e)
def push(self, orig, dest, rev=None, ref_name=None, force=False): """Inherited method :func:`~repoman.repository.Repository.push` """ all_tags_option = "--tags" all_notes_refspec = "refs/notes/*:refs/notes/*" if rev is None and ref_name is None: # Push everything refspec = "refs/*:refs/*" elif rev is None: refspec = "%s:%s" % (ref_name, ref_name) elif ref_name is None: raise RepositoryError( "When pushing, revision specified but not reference name") else: if self.tag_exists(ref_name): # We don't know what this ref is in remote, but here it is a tag ref_name = "refs/tags/%s" % ref_name all_tags_option = "" else: # In any other case, we assume it is a branch ref_name = "refs/heads/%s" % ref_name refspec = "%s:%s" % (rev, ref_name) if all_tags_option: self._git("push", dest, refspec, all_tags_option, all_notes_refspec, f=force) else: self._git("push", dest, refspec, all_notes_refspec, f=force) return self.tip()
def push(self, orig, dest, rev=None, ref_name=None, force=False): """Inherited method :func:`~repoman.repository.Repository.push` """ if rev == None and ref_name == None: # Push everything refspec = "refs/*:refs/*" elif rev == None: refspec = "%s:%s" % (ref_name, ref_name) elif ref_name == None: raise RepositoryError( "When pushing, revision specified but not reference name") else: remote_refs = list( self._git("ls-remote", "--tags", "--heads", dest, ref_name, _iter=True)) if len(remote_refs) == 0: # Reference is not qualified if self.tag_exists(ref_name): # We don't know what this ref is in remote, but here it is a tag ref_name = "refs/tags/%s" % ref_name else: # In any other case, we assume it is a branch ref_name = "refs/heads/%s" % ref_name refspec = "%s:%s" % (rev, ref_name) self._git("push", dest, refspec, f=force) return self.tip()
def get_ancestor(self, cs1, cs2): """Inherited method :func:`~repoman.repository.Repository.get_ancestor` """ if not cs1 or not cs2: error = "Error getting ancestor, " +\ "either rev1 or rev2 are None: %s , %s" % (cs1, cs2) logger.error(error) raise RepositoryError(error) return self[self._git('merge-base', cs1.hash, cs2.hash)]
def get_revset(self, cs_from=None, cs_to=None, branch=None, keyword=None, date=None): """ Inherited method :func:`~repoman.repository.Repository.get_revset` """ repo = None if cs_from: if cs_to: revset = "%s::%s" % (cs_from, cs_to) else: revset = cs_from else: revset = None with hglib.open(self.path) as repo: try: # If a revset is given, not heavy, not needed to split # the log in chunks if revset: # Adding cs_from because it's not added due to the :: usage result = [ self._new_changeset_object( repo.log(revrange=cs_from)[0]) ] for revision in repo.log(revrange=revset, branch=branch): changeset = self._new_changeset_object(revision) if changeset not in result: result.append(changeset) for changeset in result: yield changeset else: chunk_size = 15 first = repo.log(limit=1, branch=branch)[0] previous_hash = None first_hash = first.node revrange = None while previous_hash != first_hash: changesets = repo.log(limit=chunk_size, branch=branch, revrange=revrange) previous_hash = first_hash if len(changesets) > chunk_size: first = changesets.pop(-1) first_hash = first.node revrange = "%s:%s" % (first_hash, chunk_size) while changesets: cs = self._new_changeset_object(changesets.pop(0)) yield cs except hglib.error.CommandError as e: logger.exception(e) raise RepositoryError(e)
def parents(self, repo): """ Inherited method :func:`~repoman.repository.Repository.parents` """ repo = hglib.open(self.path) try: return [self._new_changeset_object(cs) for cs in repo.parents()] except TypeError: raise RepositoryError("Working copy for repo %s has no parents " "(is it bare?)" % self.path)
def add(self, files): """ Inherited method :func:`~repoman.repository.Repository.add` """ if not isinstance(files, list): files = [files] with hglib.open(self.path) as repo: for file in files: if not repo.add(os.path.join(self.path, file)): raise RepositoryError("Could not add file '%s'" % file)
def _validate_local_branch(self): if self.local_branch is None: self.local_branch = self.repository.get_branch() return if not isinstance(self.local_branch, Reference): raise RepositoryError( "In merge, local branch must be a Reference object") self._git('checkout', self.local_branch.name)
def get_branch(self, repo, branch_name=None): """ Inherited method :func:`~repoman.repository.Repository.get_branch` """ if not branch_name: repo = hglib.open(self.path) branch_name = repo.branch() else: if not self.branch_exists(branch_name): raise RepositoryError("Branch %s does not exist in repo %s" % (branch_name, self.path)) return self._new_branch_object(branch_name)
def __call__(self, *args, **kwargs): try: cmd = sh.git(_cwd=self.path, _tty_out=False, *args, **kwargs) except sh.ErrorReturnCode as e: raise RepositoryError("'%s' failed in %s: %s" % (e.full_cmd, self.path, e.message)) if '_iter' in kwargs and kwargs['_iter'] != None: return cmd # For convenience, remove last new line of command output return re.sub('(\n|\n\r)$', '', cmd.stdout)
def get_branch(self, branch_name=None): """Inherited method :func:`~repoman.repository.Repository.get_branch` """ if not branch_name: branch_name = self._git('rev-parse', '--abbrev-ref', 'HEAD') else: if not self.branch_exists(branch_name): raise RepositoryError('Branch %s does not exist in repo %s' % (branch_name, self.path)) return self._new_branch_object(branch_name)
def _get_tag_changeset(self, tag_name): """ Gets the changeset that correspond to the given tag :param tag_name: name of the tag :type string """ with hglib.open(self.path) as repo: try: repo = hglib.open(self.path) rev = [t[2] for t in repo.tags() if t[0] == tag_name][0] return self._new_changeset_object(self[rev]) except (IndexError, hglib.error.CommandError) as e: logger.exception("Eror getting tag '%s': %s" % (tag_name, e)) raise RepositoryError(e)
def get_ancestor(self, repo, cs1, cs2): """ Inherited method :func:`~repoman.repository.Repository.get_ancestor` """ if not cs1 or not cs2: error = "Error getting ancestor, either \ rev1 or rev2 are None: %s , %s" % (cs1, cs2) logger.error(error) raise RepositoryError(error) rev1 = cs1.hash rev2 = cs2.hash logger.debug("Getting ancestor for: %s, %s" % (rev1, rev2)) ancestor_rev = repo.log(revrange="ancestor('%s', '%s')" % (rev1, rev2))[0] return self._new_changeset_object(ancestor_rev)
def _get_sorted_branches(self, active=False, closed=False): """ Get the underlaying repository branches, sorted by name :param active: indicates active branches :type active: bool :param closed: indicates closed branches :type closed: bool """ # TODO implement sorted branches with repo indexers try: with hglib.open(self.path) as repo: branches = repo.branches(active, closed) branches.sort(key=lambda branch: branch[0], reverse=True) for branch in branches: yield self._new_branch_object(branch[0]) except (hglib.error.CommandError, hglib.error.ServerError) as e: logger.exception(e) raise RepositoryError(e)
def _get_branch_tip(self, branch_name): """ Returns the changeset being the branch tip :param branch_name: name of the branch :type string """ with hglib.open(self.path) as repo: try: tip = repo.log(revrange="'%s'" % branch_name)[0] return self._new_changeset_object(tip) except hglib.error.CommandError as e: # Checking if it's a branch recently created, if so, returning # current changeset (due to hglib.log does not work with not # commited branches) if repo.branch() == branch_name: return self._new_changeset_object(repo.parents()[0]) logger.exception("Error getting branch '%s' tip: %s" % (branch_name, e)) raise RepositoryError(e)
def merge(self, local_branch=None, other_rev=None, other_branch_name=None, dry_run=False): """ Inherited method :func:`~repoman.repository.Repository.merge` """ log_message = 'Initiating merge. Local branch: %s. ' + \ 'Other branch: %s@%s.' logger.debug(log_message % (local_branch, other_branch_name, other_rev)) if not other_rev: raise RepositoryError("No revision to merge with specified") if local_branch and not type(local_branch) == Reference: raise RepositoryError( "local_branch (%s) parameter must be a Reference " + "instead of %s" % (local_branch, type(local_branch))) commit = None repo = None try: repo = hglib.open(self.path) if local_branch: repo.update(rev=local_branch.name, clean=True) else: local_branch = self._new_branch_object(repo.branch()) try: repo.merge(rev=other_rev.hash, tool='false') except hglib.error.CommandError as e: if dry_run: # Restoring state repo.update(rev=local_branch.name, clean=True) # Error can mean either no need to commit, or conflicts basic_error_msg = \ 'Found an error during merge. local: %s, remote: %s@%s' % \ (local_branch.name, other_branch_name, other_rev.hash) if "merging" in str(e) and "failed" in str(e): logger.exception("Merging failed with conflicts:") raise MergeConflictError(e.out) elif self.MERGING_WITH_ANCESTOR_LITERAL in e.err: # Ugly way to detect this error, but the e.ret is not # correct logger.info("Nothing to merge, already merged: %s" % e.err) return None elif e.ret == -1: logger.exception(basic_error_msg) if 'response expected' in e.err: raise RepositoryError(e.out) else: # Unknown error logger.exception(e) raise RepositoryError(e) else: # Merge failed because it was not needed return None if not dry_run: commit_message = self.message_builder.merge( other_branch=other_branch_name or "", other_revision=other_rev.shorthash, local_branch=local_branch.name, local_revision=local_branch.get_changeset().shorthash) commit = repo.commit(message=str(commit_message), user=self.signature.user) return self._new_changeset_object(repo.tip()) else: # Restoring state repo.update(rev=local_branch.name, clean=True) return None except hglib.error.CommandError as e: logger.exception("Error merging branch %s into %s" % (other_rev.hash, local_branch.name)) # Undoing the merge to get rid of partial merges repo.update(clean=True) raise RepositoryError(e) finally: if repo: repo.close() return commit