def _get_repo(self, create, src_url=None, update_after_clone=False, bare=False): if create and os.path.exists(self.path): raise RepositoryError("Location already exist") if src_url and not create: raise RepositoryError("Create should be set to True if src_url is " "given (clone operation creates repository)") try: if create and src_url: self._check_url(src_url) self.clone(src_url, update_after_clone, bare) return Repo(self.path) elif create: os.mkdir(self.path) if bare: return Repo.init_bare(self.path) else: return Repo.init(self.path) else: return Repo(self.path) except (NotGitRepository, OSError), err: raise RepositoryError(err)
def _local_merge(self, merge_message, user_name, user_email, heads): """ Merge the given head into the checked out branch. It will force a merge commit. Currently it raises an error if the repo is empty, as it is not possible to create a merge commit in an empty repo. :param merge_message: The message to use for the merge commit. :param heads: the heads to merge. """ if self.bare: raise RepositoryError('Cannot merge into a bare git repository') if not heads: return if self.is_empty(): # TODO(skreft): do somehting more robust in this case. raise RepositoryError( 'Do not know how to merge into empty repositories yet') # N.B.(skreft): the --no-ff option is used to enforce the creation of a # commit message. We also specify the user who is doing the merge. cmd = ['-c', 'user.name=%s' % safe_str(user_name), '-c', 'user.email=%s' % safe_str(user_email), 'merge', '--no-ff', '-m', safe_str(merge_message)] cmd.extend(heads) try: self.run_git_command(cmd, fail_on_stderr=False) except RepositoryError: # Cleanup any merge leftovers self.run_git_command(['merge', '--abort'], fail_on_stderr=False) raise
def get_branch(self): headpath = self.repository._repo.refs.refpath('HEAD') try: content = open(headpath).read() match = re.match(r'^ref: refs/heads/(?P<branch>.+)\n$', content) if match: return match.groupdict()['branch'] else: raise RepositoryError("Couldn't compute workdir's branch") except IOError: # Try naive way... raise RepositoryError("Couldn't compute workdir's branch")
def get_commit_from_ref_name(repo, ref_name, ref_type=None): """ Gets the commit for a `ref_name` taking into account `ref_type`. Needed in case a bookmark / tag share the same name. :param repo: the repo instance :param ref_name: the name of the ref to get :param ref_type: optional, used to disambiguate colliding refs """ repo_scm = repo.scm_instance() ref_type_mapping = { 'book': repo_scm.bookmarks, 'bookmark': repo_scm.bookmarks, 'tag': repo_scm.tags, 'branch': repo_scm.branches, } commit_id = ref_name if repo_scm.alias != 'svn': # pass svn refs straight to backend until # the branch issue with svn is fixed if ref_type and ref_type in ref_type_mapping: try: commit_id = ref_type_mapping[ref_type][ref_name] except KeyError: raise RepositoryError('%s "%s" does not exist' % (ref_type, ref_name)) return repo_scm.get_commit(commit_id)
def __init__(self, repository, revision): self._stat_modes = {} self.repository = repository try: commit = self.repository._repo.get_object(revision) if isinstance(commit, objects.Tag): revision = commit.object[1] commit = self.repository._repo.get_object(commit.object[1]) except KeyError: raise RepositoryError("Cannot get object with id %s" % revision) self.raw_id = revision self.id = self.raw_id self.short_id = self.raw_id[:12] self._commit = commit self._tree_id = commit.tree self._committer_property = 'committer' self._author_property = 'author' self._date_property = 'commit_time' self._date_tz_property = 'commit_timezone' self.revision = repository.revisions.index(revision) self.message = safe_unicode(commit.message) self.nodes = {} self._paths = {}
def _get_repo(self, create, src_url=None, update_after_clone=False): """ Function will check for mercurial repository in given path and return a localrepo object. If there is no repository in that path it will raise an exception unless ``create`` parameter is set to True - in that case repository would be created and returned. If ``src_url`` is given, would try to clone repository from the location at given clone_point. Additionally it'll make update to working copy accordingly to ``update_after_clone`` flag """ try: if src_url: url = str(self._get_url(src_url)) opts = {} if not update_after_clone: opts.update({'noupdate': True}) try: self._check_url(url) clone(self.baseui, url, self.path, **opts) # except urllib2.URLError: # raise Abort("Got HTTP 404 error") except Exception: raise # Don't try to create if we've already cloned repo create = False return localrepository(self.baseui, self.path, create=create) except (Abort, RepoError), err: if create: msg = "Cannot create repository at %s. Original error was %s"\ % (self.path, err) else: msg = "Not valid repository at %s. Original error was %s"\ % (self.path, err) raise RepositoryError(msg)
def index(self, revision): c.anchor_url = anchor_url c.ignorews_url = _ignorews_url c.context_url = _context_url limit_off = request.GET.get('fulldiff') #get ranges of revisions if preset rev_range = revision.split('...')[:2] enable_comments = True try: if len(rev_range) == 2: enable_comments = False rev_start = rev_range[0] rev_end = rev_range[1] rev_ranges = c.rhodecode_repo.get_changesets(start=rev_start, end=rev_end) else: rev_ranges = [c.rhodecode_repo.get_changeset(revision)] c.cs_ranges = list(rev_ranges) if not c.cs_ranges: raise RepositoryError('Changeset range returned empty result') except (RepositoryError, ChangesetDoesNotExistError, Exception), e: log.error(traceback.format_exc()) h.flash(str(e), category='warning') return redirect(url('home'))
def run_git_command(self, cmd): """ Runs given ``cmd`` as git command and returns tuple (returncode, stdout, stderr). .. note:: This method exists only until log/blame functionality is implemented at Dulwich (see https://bugs.launchpad.net/bugs/645142). Parsing os command's output is road to hell... :param cmd: git command to be executed """ _copts = [ '-c', 'core.quotepath=false', ] _str_cmd = False if isinstance(cmd, basestring): cmd = [cmd] _str_cmd = True cmd = ['GIT_CONFIG_NOGLOBAL=1', 'git'] + _copts + cmd if _str_cmd: cmd = ' '.join(cmd) try: opts = dict(shell=isinstance(cmd, basestring), stdout=PIPE, stderr=PIPE) if os.path.isdir(self.path): opts['cwd'] = self.path p = Popen(cmd, **opts) except OSError, err: raise RepositoryError("Couldn't run git command (%s).\n" "Original error was:%s" % (cmd, err))
def filectxfn(_repo, memctx, path): """ Marks given path as added/changed/removed in a given _repo. This is for internal mercurial commit function. """ # check if this path is removed if path in (node.path for node in self.removed): # Raising exception is a way to mark node for removal raise IOError(errno.ENOENT, '%s is deleted' % path) # check if this path is added for node in self.added: if node.path == path: return memfilectx( path=node.path, data=(node.content.encode('utf8') if not node.is_binary else node.content), islink=False, isexec=node.is_executable, copied=False) # or changed for node in self.changed: if node.path == path: return memfilectx( path=node.path, data=(node.content.encode('utf8') if not node.is_binary else node.content), islink=False, isexec=node.is_executable, copied=False) raise RepositoryError("Given path haven't been marked as added," "changed or removed (%s)" % path)
def repo_scan(self, repos_path=None): """ Listing of repositories in given path. This path should not be a repository itself. Return a dictionary of repository objects :param repos_path: path to directory containing repositories """ if repos_path is None: repos_path = self.repos_path log.info('scanning for repositories in %s', repos_path) config = make_db_config() config.set('extensions', 'largefiles', '') repos = {} for name, path in get_filesystem_repos(repos_path, recursive=True): # name need to be decomposed and put back together using the / # since this is internal storage separator for rhodecode name = Repository.normalize_repo_name(name) try: if name in repos: raise RepositoryError('Duplicate repository name %s ' 'found in %s' % (name, path)) elif path[0] in rhodecode.BACKENDS: klass = get_backend(path[0]) repos[name] = klass(path[1], config=config) except OSError: continue log.debug('found %s paths with repositories', len(repos)) return repos
def _init_repo(self, create, src_url=None, update_after_clone=False): """ Function will check for mercurial repository in given path. If there is no repository in that path it will raise an exception unless `create` parameter is set to True - in that case repository would be created. If `src_url` is given, would try to clone repository from the location at given clone_point. Additionally it'll make update to working copy accordingly to `update_after_clone` flag. """ if create and os.path.exists(self.path): raise RepositoryError( "Cannot create repository at %s, location already exist" % self.path) if src_url: url = str(self._get_url(src_url)) MercurialRepository.check_url(url, self.config) self._remote.clone(url, self.path, update_after_clone) # Don't try to create if we've already cloned repo create = False if create: os.makedirs(self.path, mode=0755) self._remote.localrepository(create)
def tag(self, name, user, revision=None, message=None, date=None, **kwargs): """ Creates and returns a tag for the given ``revision``. :param name: name for new tag :param user: full username, i.e.: "Joe Doe <*****@*****.**>" :param revision: changeset id for which new tag would be created :param message: message of the tag's commit :param date: date of tag's commit :raises TagAlreadyExistError: if tag with same name already exists """ if name in self.tags: raise TagAlreadyExistError("Tag %s already exists" % name) changeset = self.get_changeset(revision) local = kwargs.setdefault('local', False) if message is None: message = "Added tag %s for changeset %s" % (name, changeset.short_id) if date is None: date = datetime.datetime.now().ctime() try: self._repo.tag(name, changeset._ctx.node(), message, local, user, date) except Abort, e: raise RepositoryError(e.message)
def pull(self, url): """ Tries to pull changes from external location. """ url = self._get_url(url) try: pull(self.baseui, self._repo, url) except Abort, err: # Propagate error but with vcs's type raise RepositoryError(str(err))
def test_raises_an_error_when_commit_not_found(self): repo = Mock() ref = 'branch:master:{}'.format(self.commit_hash) with patch( 'rhodecode.api.utils.get_commit_from_ref_name') as get_commit: get_commit.side_effect = RepositoryError('Commit not found') with pytest.raises(JSONRPCError) as excinfo: utils.get_commit_or_error(ref, repo) expected_message = 'Ref `{}` does not exist'.format(ref) assert excinfo.value.message == expected_message
def get_tree_for_dir(tree, dirname): for name, mode, id in tree.iteritems(): if name == dirname: obj = self.repository._repo[id] if isinstance(obj, objects.Tree): return obj else: raise RepositoryError( "Cannot create directory %s " "at tree %s as path is occupied and is not a " "Tree" % (dirname, tree)) return None
def _local_pull(self, repository_path, branch_name): """ Pull a branch from a local repository. """ if self.bare: raise RepositoryError('Cannot pull into a bare git repository') # N.B.(skreft): The --ff-only option is to make sure this is a # fast-forward (i.e., we are only pulling new changes and there are no # conflicts with our current branch) # Additionally, that option needs to go before --no-tags, otherwise git # pull complains about it being an unknown flag. cmd = ['pull', '--ff-only', '--no-tags', repository_path, branch_name] self.run_git_command(cmd, fail_on_stderr=False)
def _init_repo(self, create, src_url): if create and os.path.exists(self.path): raise RepositoryError( "Cannot create repository at %s, location already exist" % self.path) if create: self._remote.create_repository(settings.SVN_COMPATIBLE_VERSION) if src_url: src_url = _sanitize_url(src_url) self._remote.import_remote_repository(src_url) else: self._check_path()
def mark_as_fork(self, repo, fork, user): repo = self._get_repo(repo) fork = self._get_repo(fork) if fork and repo.repo_id == fork.repo_id: raise Exception("Cannot set repository as fork of itself") if fork and repo.repo_type != fork.repo_type: raise RepositoryError( "Cannot set repository as fork of repository with other type") repo.fork = fork self.sa.add(repo) return repo
def _run_git_command(cls, cmd, **opts): """ Runs given ``cmd`` as git command and returns tuple (stdout, stderr). :param cmd: git command to be executed :param opts: env options to pass into Subprocess command """ if '_bare' in opts: _copts = [] del opts['_bare'] else: _copts = ['-c', 'core.quotepath=false', ] safe_call = False if '_safe' in opts: #no exc on failure del opts['_safe'] safe_call = True _str_cmd = False if isinstance(cmd, basestring): cmd = [cmd] _str_cmd = True gitenv = os.environ # need to clean fix GIT_DIR ! if 'GIT_DIR' in gitenv: del gitenv['GIT_DIR'] gitenv['GIT_CONFIG_NOGLOBAL'] = '1' _git_path = settings.GIT_EXECUTABLE_PATH cmd = [_git_path] + _copts + cmd if _str_cmd: cmd = ' '.join(cmd) try: _opts = dict( env=gitenv, shell=True, ) _opts.update(opts) p = subprocessio.SubprocessIOChunker(cmd, **_opts) except (EnvironmentError, OSError), err: tb_err = ("Couldn't run git command (%s).\n" "Original error was:%s\n" % (cmd, err)) log.error(tb_err) if safe_call: return '', err else: raise RepositoryError(tb_err)
def _init_repo(self, create, src_url=None, update_after_clone=False, bare=False): if create and os.path.exists(self.path): raise RepositoryError( "Cannot create repository at %s, location already exist" % self.path) try: if create and src_url: GitRepository.check_url(src_url, self.config) self.clone(src_url, update_after_clone, bare) elif create: os.makedirs(self.path, mode=0755) if bare: self._remote.init_bare() else: self._remote.init() else: self._remote.assert_correct_path() # TODO: johbo: check if we have to translate the OSError here except OSError as err: raise RepositoryError(err)
def get_commits(self, start_id=None, end_id=None, start_date=None, end_date=None, branch_name=None, pre_load=None): if self.is_empty(): raise EmptyRepositoryError("There are no commit_ids yet") self._validate_branch_name(branch_name) if start_id is not None: self._validate_commit_id(start_id) if end_id is not None: self._validate_commit_id(end_id) start_raw_id = self._sanitize_commit_id(start_id) start_pos = self.commit_ids.index(start_raw_id) if start_id else None end_raw_id = self._sanitize_commit_id(end_id) end_pos = max(0, self.commit_ids.index(end_raw_id)) if end_id else None if None not in [start_id, end_id] and start_pos > end_pos: raise RepositoryError( "Start commit '%s' cannot be after end commit '%s'" % (start_id, end_id)) if end_pos is not None: end_pos += 1 # Date based filtering if start_date or end_date: start_raw_id, end_raw_id = self._remote.lookup_interval( date_astimestamp(start_date) if start_date else None, date_astimestamp(end_date) if end_date else None) start_pos = start_raw_id - 1 end_pos = end_raw_id commit_ids = self.commit_ids # TODO: johbo: Reconsider impact of DEFAULT_BRANCH_NAME here if branch_name not in [None, self.DEFAULT_BRANCH_NAME]: svn_rev = long(self.commit_ids[-1]) commit_ids = self._remote.node_history(path=branch_name, revision=svn_rev, limit=None) commit_ids = [str(i) for i in reversed(commit_ids)] if start_pos or end_pos: commit_ids = commit_ids[start_pos:end_pos] return base.CollectionGenerator(self, commit_ids, pre_load=pre_load)
def _current_branch(self): """ Return the name of the current branch. It only works for non bare repositories (i.e. repositories with a working copy) """ if self.bare: raise RepositoryError('Bare git repos do not have active branches') if self.is_empty(): return None stdout, _ = self.run_git_command(['rev-parse', '--abbrev-ref', 'HEAD']) return stdout.strip()
def test_merge_raises_exception(self, vcsbackend): self.prepare_for_success(vcsbackend) expected_merge_response = MergeResponse(False, False, None, MergeFailureReason.UNKNOWN) with mock.patch.object(self.target_repo, '_merge_repo', side_effect=RepositoryError()): merge_response = self.target_repo.merge(self.target_ref, self.source_repo, self.source_ref, self.workspace, dry_run=True) assert merge_response == expected_merge_response
def get_changesets(self, start=None, end=None, start_date=None, end_date=None, branch_name=None, reverse=False): """ Returns iterator of ``MercurialChangeset`` objects from start to end (both are inclusive) :param start: None, str, int or mercurial lookup format :param end: None, str, int or mercurial lookup format :param start_date: :param end_date: :param branch_name: :param reversed: return changesets in reversed order """ start_raw_id = self._get_revision(start) start_pos = self.revisions.index(start_raw_id) if start else None end_raw_id = self._get_revision(end) end_pos = self.revisions.index(end_raw_id) if end else None if None not in [start, end] and start_pos > end_pos: raise RepositoryError("Start revision '%s' cannot be " "after end revision '%s'" % (start, end)) if branch_name and branch_name not in self.allbranches.keys(): raise BranchDoesNotExistError('Branch %s not found in' ' this repository' % branch_name) if end_pos is not None: end_pos += 1 slice_ = reversed(self.revisions[start_pos:end_pos]) if reverse else \ self.revisions[start_pos:end_pos] for id_ in slice_: cs = self.get_changeset(id_) if branch_name and cs.branch != branch_name: continue if start_date and cs.date < start_date: continue if end_date and cs.date > end_date: continue yield cs
def __get_filenode_or_redirect(self, repo_name, cs, path): """ Returns file_node, if error occurs or given path is directory, it'll redirect to top level path :param repo_name: repo_name :param cs: given changeset :param path: path to lookup """ try: file_node = cs.get_node(path) if file_node.is_dir(): raise RepositoryError('given path is a directory') except RepositoryError, e: h.flash(str(e), category='error') raise HTTPNotFound()
def test_merge_can_be_rejected_by_pre_push_hook(self, pr_util, user_admin, capture_rcextensions, merge_extras): pull_request = pr_util.create_pull_request(approved=True, mergeable=True) # TODO: johbo: Needed for sqlite, try to find an automatic way for it Session().commit() with mock.patch('rhodecode.EXTENSIONS.PRE_PUSH_HOOK') as pre_pull: pre_pull.side_effect = RepositoryError("Disallow push!") merge_status = PullRequestModel().merge(pull_request, user_admin, extras=merge_extras) assert not merge_status.executed assert 'pre_push' not in capture_rcextensions assert 'post_push' not in capture_rcextensions
def __get_filenode_or_redirect(self, repo_name, cs, path): """ Returns file_node, if error occurs or given path is directory, it'll redirect to top level path :param repo_name: repo_name :param cs: given changeset :param path: path to lookup """ try: file_node = cs.get_node(path) if file_node.is_dir(): raise RepositoryError('given path is a directory') except RepositoryError, e: h.flash(str(e), category='warning') redirect( h.url('files_home', repo_name=repo_name, revision=cs.raw_id))
def _checkout(self, branch_name, create=False): """ Checkout a branch in the working directory. It tries to create the branch if create is True, failing if the branch already exists. It only works for non bare repositories (i.e. repositories with a working copy) """ if self.bare: raise RepositoryError('Cannot checkout branches in a bare git repo') cmd = ['checkout'] if create: cmd.append('-b') cmd.append(branch_name) self.run_git_command(cmd, fail_on_stderr=False)
def get_commit(self, commit_id=None, commit_idx=None, pre_load=None): """ Returns `GitCommit` object representing commit from git repository at the given `commit_id` or head (most recent commit) if None given. """ if commit_id is not None: self._validate_commit_id(commit_id) elif commit_idx is not None: self._validate_commit_idx(commit_idx) commit_id = commit_idx commit_id = self._get_commit_id(commit_id) try: # Need to call remote to translate id for tagging scenario commit_id = self._remote.get_object(commit_id)["commit_id"] idx = self._commit_ids[commit_id] except KeyError: raise RepositoryError("Cannot get object with id %s" % commit_id) return GitCommit(self, commit_id, idx, pre_load=pre_load)
def remove_tag(self, name, user, message=None, date=None): """ Removes tag with the given ``name``. :param name: name of the tag to be removed :param user: full username, i.e.: "Joe Doe <*****@*****.**>" :param message: message of the tag's removal commit :param date: date of tag's removal commit :raises TagDoesNotExistError: if tag with given name does not exists """ if name not in self.tags: raise TagDoesNotExistError("Tag %s does not exist" % name) tagpath = posixpath.join(self._repo.refs.path, 'refs', 'tags', name) try: os.remove(tagpath) self.tags = self._get_tags() except OSError, e: raise RepositoryError(e.strerror)