def _get_revision(self, revision): """ For git backend we always return integer here. This way we ensure that changset's revision attribute would become integer. """ pattern = re.compile(r'^[[0-9a-fA-F]{12}|[0-9a-fA-F]{40}]$') is_bstr = lambda o: isinstance(o, (str, unicode)) is_null = lambda o: len(o) == revision.count('0') if len(self.revisions) == 0: raise EmptyRepositoryError("There are no changesets yet") if revision in (None, '', 'tip', 'HEAD', 'head', -1): revision = self.revisions[-1] if ((is_bstr(revision) and revision.isdigit() and len(revision) < 12) or isinstance(revision, int) or is_null(revision)): try: revision = self.revisions[int(revision)] except: raise ChangesetDoesNotExistError("Revision %r does not exist " "for this repository %s" % (revision, self)) elif is_bstr(revision): if not pattern.match(revision) or revision not in self.revisions: raise ChangesetDoesNotExistError("Revision %r does not exist " "for this repository %s" % (revision, self)) # Ensure we return full id if not pattern.match(str(revision)): raise ChangesetDoesNotExistError( "Given revision %r not recognized" % revision) return revision
def change(self, *filenodes): """ Marks given ``FileNode`` objects to be *changed* in next commit. :raises ``EmptyRepositoryError``: if there are no changesets yet :raises ``NodeAlreadyExistsError``: if node with same path is already marked to be *changed* :raises ``NodeAlreadyRemovedError``: if node with same path is already marked to be *removed* :raises ``NodeDoesNotExistError``: if node doesn't exist in latest changeset :raises ``NodeNotChangedError``: if node hasn't really be changed """ for node in filenodes: if node.path in (n.path for n in self.removed): raise NodeAlreadyRemovedError("Node at %s is already marked " "as removed" % node.path) try: self.repository.get_changeset() except EmptyRepositoryError: raise EmptyRepositoryError("Nothing to change - try to *add* new " "nodes rather than changing them") for node in filenodes: if node.path in (n.path for n in self.changed): raise NodeAlreadyChangedError("Node at '%s' is already " "marked as changed" % node.path) self.changed.append(node)
def _get_readme_from_cache(key): readme_data = None readme_file = None log.debug('Looking for README file') try: # get's the landing revision! or tip if fails cs = db_repo.get_landing_changeset() if isinstance(cs, EmptyChangeset): raise EmptyRepositoryError() renderer = MarkupRenderer() for f in README_FILES: try: readme = cs.get_node(f) if not isinstance(readme, FileNode): continue readme_file = f log.debug('Found README file `%s` rendering...' % readme_file) readme_data = renderer.render(readme.content, f) break except NodeDoesNotExistError: continue except ChangesetError: log.error(traceback.format_exc()) pass except EmptyRepositoryError: pass except Exception: log.error(traceback.format_exc()) return readme_data, readme_file
def _generate_readme(cache_key): readme_data = None readme_file = None try: # gets the landing revision or tip if fails commit = db_repo.get_landing_commit() if isinstance(commit, EmptyCommit): raise EmptyRepositoryError() renderer = MarkupRenderer() for f in README_FILES: try: node = commit.get_node(f) except NodeDoesNotExistError: continue if not node.is_file(): continue readme_file = f log.debug('Found README file `%s` rendering...', readme_file) readme_data = renderer.render(node.content, filename=f) break except CommitError: log.exception("Problem getting commit") pass except EmptyRepositoryError: pass except Exception: log.exception("General failure") return readme_data, readme_file
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 get_commit(self, commit_id=None, commit_idx=None, pre_load=None): if self.is_empty(): raise EmptyRepositoryError("There are no commits yet") if commit_id is not None: self._validate_commit_id(commit_id) elif commit_idx is not None: self._validate_commit_idx(commit_idx) try: commit_id = self.commit_ids[commit_idx] except IndexError: raise CommitDoesNotExistError commit_id = self._sanitize_commit_id(commit_id) commit = SubversionCommit(repository=self, commit_id=commit_id) return commit
def _get_revision(self, revision): """ For git backend we always return integer here. This way we ensure that changset's revision attribute would become integer. """ pattern = re.compile(r'^[[0-9a-fA-F]{12}|[0-9a-fA-F]{40}]$') is_bstr = lambda o: isinstance(o, (str, unicode)) is_null = lambda o: len(o) == revision.count('0') if len(self.revisions) == 0: raise EmptyRepositoryError("There are no changesets yet") if revision in (None, '', 'tip', 'HEAD', 'head', -1): revision = self.revisions[-1] if ((is_bstr(revision) and revision.isdigit() and len(revision) < 12) or isinstance(revision, int) or is_null(revision)): try: revision = self.revisions[int(revision)] except Exception: raise ChangesetDoesNotExistError("Revision %s does not exist " "for this repository" % (revision)) elif is_bstr(revision): # get by branch/tag name _ref_revision = self._parsed_refs.get(revision) _tags_shas = self.tags.values() if _ref_revision: # and _ref_revision[1] in ['H', 'RH', 'T']: return _ref_revision[0] # maybe it's a tag ? we don't have them in self.revisions elif revision in _tags_shas: return _tags_shas[_tags_shas.index(revision)] elif not pattern.match(revision) or revision not in self.revisions: raise ChangesetDoesNotExistError("Revision %s does not exist " "for this repository" % (revision)) # Ensure we return full id if not pattern.match(str(revision)): raise ChangesetDoesNotExistError("Given revision %s not recognized" % revision) return revision
def _get_commit_id(self, commit_id_or_idx): def is_null(value): return len(value) == commit_id_or_idx.count('0') if self.is_empty(): raise EmptyRepositoryError("There are no commits yet") if commit_id_or_idx in (None, '', 'tip', 'HEAD', 'head', -1): return self.commit_ids[-1] is_bstr = isinstance(commit_id_or_idx, (str, unicode)) if ((is_bstr and commit_id_or_idx.isdigit() and len(commit_id_or_idx) < 12) or isinstance(commit_id_or_idx, int) or is_null(commit_id_or_idx)): try: commit_id_or_idx = self.commit_ids[int(commit_id_or_idx)] except Exception: msg = "Commit %s does not exist for %s" % ( commit_id_or_idx, self) raise CommitDoesNotExistError(msg) elif is_bstr: # get by branch/tag name ref_id = self._parsed_refs.get(commit_id_or_idx) if ref_id: # and ref_id[1] in ['H', 'RH', 'T']: return ref_id[0] tag_ids = self.tags.values() # maybe it's a tag ? we don't have them in self.commit_ids if commit_id_or_idx in tag_ids: return commit_id_or_idx elif (not SHA_PATTERN.match(commit_id_or_idx) or commit_id_or_idx not in self.commit_ids): msg = "Commit %s does not exist for %s" % ( commit_id_or_idx, self) raise CommitDoesNotExistError(msg) # Ensure we return full id if not SHA_PATTERN.match(str(commit_id_or_idx)): raise CommitDoesNotExistError( "Given commit id %s not recognized" % commit_id_or_idx) return commit_id_or_idx
def get_commit(self, commit_id=None, commit_idx=None, pre_load=None): """ Returns ``MercurialCommit`` object representing repository's commit at the given `commit_id` or `commit_idx`. """ if self.is_empty(): raise EmptyRepositoryError("There are no commits yet") if commit_id is not None: self._validate_commit_id(commit_id) try: idx = self._commit_ids[commit_id] return MercurialCommit(self, commit_id, idx, pre_load=pre_load) except KeyError: pass elif commit_idx is not None: self._validate_commit_idx(commit_idx) commit_idx = self._sanitize_commit_idx(commit_idx) try: id_ = self.commit_ids[commit_idx] if commit_idx < 0: commit_idx += len(self.commit_ids) return MercurialCommit(self, id_, commit_idx, pre_load=pre_load) except IndexError: commit_id = commit_idx else: commit_id = "tip" # TODO Paris: Ugly hack to "serialize" long for msgpack if isinstance(commit_id, long): commit_id = float(commit_id) if isinstance(commit_id, unicode): commit_id = safe_str(commit_id) raw_id, idx = self._remote.lookup(commit_id, both=True) return MercurialCommit(self, raw_id, idx, pre_load=pre_load)
def _get_revision(self, revision): """ Get's an ID revision given as str. This will always return a fill 40 char revision number :param revision: str or int or None """ if self._empty: raise EmptyRepositoryError("There are no changesets yet") if revision in [-1, 'tip', None]: revision = 'tip' try: revision = hex(self._repo.lookup(revision)) except (IndexError, ValueError, RepoLookupError, TypeError): raise ChangesetDoesNotExistError("Revision %s does not " "exist for this repository" % (revision)) return revision
def get_commits( self, start_id=None, end_id=None, start_date=None, end_date=None, branch_name=None, pre_load=None): """ Returns generator of `GitCommit` objects from start to end (both are inclusive), in ascending date order. :param start_id: None, str(commit_id) :param end_id: None, str(commit_id) :param start_date: if specified, commits with commit date less than ``start_date`` would be filtered out from returned set :param end_date: if specified, commits with commit date greater than ``end_date`` would be filtered out from returned set :param branch_name: if specified, commits not reachable from given branch would be filtered out from returned set :raise BranchDoesNotExistError: If given `branch_name` does not exist. :raise CommitDoesNotExistError: If commits for given `start` or `end` could not be found. """ if self.is_empty(): raise EmptyRepositoryError("There are no commits 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._get_commit_id(start_id) start_pos = self._commit_ids[start_raw_id] if start_id else None end_raw_id = self._get_commit_id(end_id) end_pos = max(0, self._commit_ids[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 filter_ = [] if branch_name: filter_.append({'branch_name': branch_name}) if start_date and not end_date: filter_.append({'since': start_date}) if end_date and not start_date: filter_.append({'until': end_date}) if start_date and end_date: filter_.append({'since': start_date}) filter_.append({'until': end_date}) # if start_pos or end_pos: # filter_.append({'start': start_pos}) # filter_.append({'end': end_pos}) if filter_: revfilters = { 'branch_name': branch_name, 'since': start_date.strftime('%m/%d/%y %H:%M:%S') if start_date else None, 'until': end_date.strftime('%m/%d/%y %H:%M:%S') if end_date else None, 'start': start_pos, 'end': end_pos, } commit_ids = self._get_all_commit_ids(filters=revfilters) # pure python stuff, it's slow due to walker walking whole repo # def get_revs(walker): # for walker_entry in walker: # yield walker_entry.commit.id # revfilters = {} # commit_ids = list(reversed(list(get_revs(self._repo.get_walker(**revfilters))))) else: commit_ids = self.commit_ids if start_pos or end_pos: commit_ids = commit_ids[start_pos: end_pos] return CollectionGenerator(self, commit_ids, pre_load=pre_load)
def get_commits(self, start_id=None, end_id=None, start_date=None, end_date=None, branch_name=None, pre_load=None): """ Returns generator of ``MercurialCommit`` objects from start to end (both are inclusive) :param start_id: None, str(commit_id) :param end_id: None, str(commit_id) :param start_date: if specified, commits with commit date less than ``start_date`` would be filtered out from returned set :param end_date: if specified, commits with commit date greater than ``end_date`` would be filtered out from returned set :param branch_name: if specified, commits not reachable from given branch would be filtered out from returned set :raise BranchDoesNotExistError: If given ``branch_name`` does not exist. :raise CommitDoesNotExistError: If commit for given ``start`` or ``end`` could not be found. """ # actually we should check now if it's not an empty repo branch_ancestors = False if self.is_empty(): raise EmptyRepositoryError("There are no commits yet") self._validate_branch_name(branch_name) if start_id is not None: self._validate_commit_id(start_id) c_start = self.get_commit(commit_id=start_id) start_pos = self._commit_ids[c_start.raw_id] else: start_pos = None if end_id is not None: self._validate_commit_id(end_id) c_end = self.get_commit(commit_id=end_id) end_pos = max(0, self._commit_ids[c_end.raw_id]) else: end_pos = 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 commit_filter = [] if branch_name and not branch_ancestors: commit_filter.append('branch("%s")' % branch_name) elif branch_name and branch_ancestors: commit_filter.append('ancestors(branch("%s"))' % branch_name) if start_date and not end_date: commit_filter.append('date(">%s")' % start_date) if end_date and not start_date: commit_filter.append('date("<%s")' % end_date) if start_date and end_date: commit_filter.append('date(">%s") and date("<%s")' % (start_date, end_date)) # TODO: johbo: Figure out a simpler way for this solution collection_generator = CollectionGenerator if commit_filter: commit_filter = map(safe_str, commit_filter) revisions = self._remote.rev_range(commit_filter) collection_generator = MercurialIndexBasedCollectionGenerator else: revisions = self.commit_ids if start_pos or end_pos: revisions = revisions[start_pos:end_pos] return collection_generator(self, revisions, pre_load=pre_load)
def get_changesets(self, start=None, end=None, start_date=None, end_date=None, branch_name=None, reverse=False): """ Returns iterator of ``GitChangeset`` objects from start to end (both are inclusive), in ascending date order (unless ``reverse`` is set). :param start: changeset ID, as str; first returned changeset :param end: changeset ID, as str; last returned changeset :param start_date: if specified, changesets with commit date less than ``start_date`` would be filtered out from returned set :param end_date: if specified, changesets with commit date greater than ``end_date`` would be filtered out from returned set :param branch_name: if specified, changesets not reachable from given branch would be filtered out from returned set :param reverse: if ``True``, returned generator would be reversed (meaning that returned changesets would have descending date order) :raise BranchDoesNotExistError: If given ``branch_name`` does not exist. :raise ChangesetDoesNotExistError: If changeset for given ``start`` or ``end`` could not be found. """ if branch_name and branch_name not in self.branches: raise BranchDoesNotExistError("Branch '%s' not found" \ % branch_name) # actually we should check now if it's not an empty repo to not spaw # subprocess commands if self._empty: raise EmptyRepositoryError("There are no changesets yet") # %H at format means (full) commit hash, initial hashes are retrieved # in ascending date order cmd_template = 'log --date-order --reverse --pretty=format:"%H"' cmd_params = {} if start_date: cmd_template += ' --since "$since"' cmd_params['since'] = start_date.strftime('%m/%d/%y %H:%M:%S') if end_date: cmd_template += ' --until "$until"' cmd_params['until'] = end_date.strftime('%m/%d/%y %H:%M:%S') if branch_name: cmd_template += ' $branch_name' cmd_params['branch_name'] = branch_name else: rev_filter = _git_path = settings.GIT_REV_FILTER cmd_template += ' %s' % (rev_filter) cmd = string.Template(cmd_template).safe_substitute(**cmd_params) revs = self.run_git_command(cmd)[0].splitlines() start_pos = 0 end_pos = len(revs) if start: _start = self._get_revision(start) try: start_pos = revs.index(_start) except ValueError: pass if end is not None: _end = self._get_revision(end) try: end_pos = revs.index(_end) except ValueError: pass if None not in [start, end] and start_pos > end_pos: raise RepositoryError('start cannot be after end') if end_pos is not None: end_pos += 1 revs = revs[start_pos:end_pos] if reverse: revs = reversed(revs) return CollectionGenerator(self, revs)
def _get_repo_pullrequest_sources(self, repo, commit_id=None, branch=None, bookmark=None): """ Return a structure with repo's interesting commits, suitable for the selectors in pullrequest controller :param commit_id: a commit that must be in the list somehow and selected by default :param branch: a branch that must be in the list and selected by default - even if closed :param bookmark: a bookmark that must be in the list and selected """ commit_id = safe_str(commit_id) if commit_id else None branch = safe_str(branch) if branch else None bookmark = safe_str(bookmark) if bookmark else None selected = None # order matters: first source that has commit_id in it will be selected sources = [] sources.append( ('book', repo.bookmarks.items(), _('Bookmarks'), bookmark)) sources.append( ('branch', repo.branches.items(), _('Branches'), branch)) if commit_id: ref_commit = (h.short_id(commit_id), commit_id) sources.append(('rev', [ref_commit], _('Commit IDs'), commit_id)) sources.append(('branch', repo.branches_closed.items(), _('Closed Branches'), branch), ) groups = [] for group_key, ref_list, group_name, match in sources: group_refs = [] for ref_name, ref_id in ref_list: ref_key = '%s:%s:%s' % (group_key, ref_name, ref_id) group_refs.append((ref_key, ref_name)) if not selected: if set([commit_id, match]) & set([ref_id, ref_name]): selected = ref_key if group_refs: groups.append((group_refs, group_name)) if not selected: ref = commit_id or branch or bookmark if ref: raise CommitDoesNotExistError( 'No commit refs could be found matching: %s' % ref) elif repo.DEFAULT_BRANCH_NAME in repo.branches: selected = 'branch:%s:%s' % ( repo.DEFAULT_BRANCH_NAME, repo.branches[repo.DEFAULT_BRANCH_NAME]) elif repo.commit_ids: rev = repo.commit_ids[0] selected = 'rev:%s:%s' % (rev, rev) else: raise EmptyRepositoryError() return groups, selected