diff --git a/docs/uri-scheme-browse-origin.rst b/docs/uri-scheme-browse-origin.rst --- a/docs/uri-scheme-browse-origin.rst +++ b/docs/uri-scheme-browse-origin.rst @@ -257,13 +257,16 @@ The following data are displayed for each log entry: + * link to browse the associated revision in the origin context * author of the revision - * link to the revision metadata - * message associated the revision * date of the revision - * link to browse the associated source tree in the origin context + * message associated the revision + * commit date of the revision - N log entries are displayed per page (default is 20). In order to navigate + By default, the revisions are ordered in reverse chronological order of + their commit date. + + N log entries are displayed per page (default is 100). In order to navigate in a large history, two buttons are present at the bottom of the view: * *Newer*: fetch and display if available the N more recent log entries @@ -279,13 +282,10 @@ :param string origin_type: the type of the SWH origin (*git*, *svn*, *deb* ...) :param string origin_url: the url of the origin (e.g. https://github.com/(user)/(repo)/) - :query string revs_breadcrumb: used internally to store - the navigation breadcrumbs (i.e. the list of descendant revisions - visited so far). It must be a string in the form - "(rev_1)[/(rev_2)/.../(rev_n)]" where rev_i corresponds to a - revision *sha1_git*. :query int per_page: the number of log entries to display per page - (default is 20, max is 50) + :query int offset: the number of revisions to skip before returning those to display + :query str revs_ordering: specify the revisions ordering, possible values are *committer_date*, + *dfs*, *dfs_post* and *bfs* :query string branch: specify the origin branch name from which to retrieve the commit log :query string release: specify the origin release name from which diff --git a/docs/uri-scheme-browse-revision.rst b/docs/uri-scheme-browse-revision.rst --- a/docs/uri-scheme-browse-revision.rst +++ b/docs/uri-scheme-browse-revision.rst @@ -47,13 +47,16 @@ a given one. In other words, it shows a commit log. The following data are displayed for each log entry: - * author of the revision * link to browse the revision - * message associated to the revision + * author of the revision * date of the revision - * link to browse the associated source tree + * message associated to the revision + * commit date of the revision + + By default, the revisions are ordered in reverse chronological order of + their commit date. - N log entries are displayed per page (default is 20). In order to navigate + N log entries are displayed per page (default is 100). In order to navigate in a large history, two buttons are present at the bottom of the view: * *Newer*: fetch and display if available the N more recent log entries @@ -63,13 +66,10 @@ :param string sha1_git: hexadecimal representation for the *sha1_git* identifier of a SWH revision - :query string revs_breadcrumb: used internally to store - the navigation breadcrumbs (i.e. the list of descendant revisions - visited so far). It must be a string in the form - "[//.../]" where rev_i corresponds to a - revision sha1_git. :query int per_page: the number of log entries to display per page - (default is 20, max is 50) + :query int offset: the number of revisions to skip before returning those to display + :query str revs_ordering: specify the revisions ordering, possible values are *committer_date*, + *dfs*, *dfs_post* and *bfs* :statuscode 200: no error :statuscode 404: requested revision can not be found in the SWH archive diff --git a/swh/web/algos/__init__.py b/swh/web/algos/__init__.py new file mode 100644 diff --git a/swh/web/algos/revisions_walker.py b/swh/web/algos/revisions_walker.py new file mode 100644 --- /dev/null +++ b/swh/web/algos/revisions_walker.py @@ -0,0 +1,462 @@ +# Copyright (C) 2018 The Software Heritage developers +# See the AUTHORS file at the top-level directory of this distribution +# License: GNU General Public License version 3, or any later version +# See top-level LICENSE file for more information + +import heapq + +from abc import ABCMeta, abstractmethod +from collections import deque + +from swh.web.common import service +from swh.web.common.utils import parse_timestamp + +_revs_walker_classes = {} + + +class _RevisionsWalkerMetaClass(ABCMeta): + def __new__(cls, clsname, bases, attrs): + newclass = super().__new__(cls, clsname, bases, attrs) + if 'rw_type' in attrs: + _revs_walker_classes[attrs['rw_type']] = newclass + return newclass + + +class RevisionsWalker(metaclass=_RevisionsWalkerMetaClass): + """ + Abtract base class encapsulating the logic to walk across + a revisions history starting from a given one. + + It defines an iterator returning the revisions according + to a specific ordering implemented in derived classes. + + The iteration step performs the following operations: + + 1) Check if the iteration is finished by calling method + :meth:`is_finished` and raises :exc:`StopIteration` if it + it is the case + + 2) Get the next unseen revision by calling method + :meth:`get_next_rev_id` + + 3) Process parents of that revision by calling method + :meth:`process_parent_revs` for the next iteration + steps + + 4) Check if the revision should be returned by calling + method :meth:`should_return` and returns it if + it is the case + + Args: + rev_start (str): hexadecimal representation of a revision + identifier + max_revs (int): maximum number of revisions to return + state (dict): optional previous state of that revisions walker + """ + + def __init__(self, rev_start, max_revs=None, state=None): + self._revs_to_visit = [] + self._done = set() + self._revs = {} + self._last_rev = None + self._num_revs = 0 + self._max_revs = max_revs + if state: + self._revs_to_visit = state['revs_to_visit'] + self._done = state['done'] + self._last_rev = state['last_rev'] + self._num_revs = state['num_revs'] + self.process_rev(rev_start) + + @abstractmethod + def process_rev(self, rev_id): + """ + Abstract method whose purpose is to process a newly visited + revision during the walk. + Derived classes must implement it according to the desired way + to walk across the revisions history. + + Args: + rev_id (str): hexadecimal representation of the newly visited + revision identifier + """ + pass + + @abstractmethod + def get_next_rev_id(self): + """ + Abstract method whose purpose is to return the next revision + during the iteration. + Derived classes must implement it according to the desired way + to walk across the revisions history. + + Returns: + dict: A dict describing a revision as returned by + :func:`swh.web.common.service.lookup_revision` + """ + pass + + def process_parent_revs(self, rev): + """ + Method that processes the parents of a revision when it + is iterated. The default implementation simply calls + :meth:`process_rev` for each parent revision in the + order they are declared. + + Args: + rev (dict): A dict describing a revision as returned by + :func:`swh.web.common.service.lookup_revision` + """ + for parent_id in rev['parents']: + self.process_rev(parent_id) + + def should_return(self, rev): + """ + Method used to filter out a revision to return if needed. + Default implementation returns all iterated revisions. + + Args: + rev (dict): A dict describing a revision as returned by + :func:`swh.web.common.service.lookup_revision` + + Returns: + bool: Wether or not to return the revision in the iteration + """ + return True + + def is_finished(self): + """ + Method called at the beginning of each iteration loop to determine + if the iteration is finished or not. + + Returns: + bool: Wether or not the iteration is finished + """ + if self._max_revs is not None and self._num_revs >= self._max_revs: + return True + if not self._revs_to_visit: + return True + return False + + def _get_rev(self, rev_id): + rev = self._revs.get(rev_id, None) + if not rev: + # cache some revisions in advance to avoid sending too much + # requests to storage and thus speedup the revisions walk + for rev in service.lookup_revision_log(rev_id, limit=100): + self._revs[rev['id']] = rev + return self._revs[rev_id] + + def export_state(self): + """ + Export the internal state of that revision walker to a dict. + Its purpose is to continue the iteration in a pagination context. + + Returns: + dict: A dict containing the internal state of that revisions walker + """ + return { + 'revs_to_visit': self._revs_to_visit, + 'done': self._done, + 'last_rev': self._last_rev, + 'num_revs': self._num_revs + } + + def __next__(self): + if self.is_finished(): + raise StopIteration + while self._revs_to_visit: + rev_id = self.get_next_rev_id() + if rev_id in self._done: + continue + self._done.add(rev_id) + rev = self._get_rev(rev_id) + self.process_parent_revs(rev) + if self.should_return(rev): + self._num_revs += 1 + self._last_rev = rev + return rev + raise StopIteration + + def __iter__(self): + return self + + +class CommitterDateRevisionsWalker(RevisionsWalker): + """ + Revisions walker that returns revisions in reverse chronological + order according to committer date (same behaviour as ``git log``) + """ + + rw_type = 'committer_date' + + def process_rev(self, rev_id): + """ + Add the revision to a priority queue according to the committer date. + + Args: + rev_id (str): hexadecimal representation of the newly visited + revision identifier + """ + if rev_id not in self._done: + rev = self._get_rev(rev_id) + commit_time = parse_timestamp(rev['committer_date']).timestamp() + heapq.heappush(self._revs_to_visit, (-commit_time, rev_id)) + + def get_next_rev_id(self): + """ + Returns the smallest revision from the priority queue, i.e. + the one with highest committer date. + + Returns: + dict: A dict describing a revision as returned by + :func:`swh.web.common.service.lookup_revision` + """ + _, rev_id = heapq.heappop(self._revs_to_visit) + return rev_id + + +class BFSRevisionsWalker(RevisionsWalker): + """ + Revisions walker that returns revisions in the same order + as when performing a breadth-first search on the revisions + tree. + """ + + rw_type = 'bfs' + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self._revs_to_visit = deque(self._revs_to_visit) + + def process_rev(self, rev_id): + """ + Append the revision to a queue. + + Args: + rev_id (str): hexadecimal representation of the newly visited + revision identifier + """ + if rev_id not in self._done: + self._revs_to_visit.append(rev_id) + + def get_next_rev_id(self): + """ + Returns the next revision from the queue. + + Returns: + dict: A dict describing a revision as returned by + :func:`swh.web.common.service.lookup_revision` + """ + return self._revs_to_visit.popleft() + + +class DFSPostRevisionsWalker(RevisionsWalker): + """ + Revisions walker that returns revisions in the same order + as when performing a depth-first search in post-order on the + revisions tree (i.e. after visiting a merge commit, + the merged commit will be visited before the base it was + merged on). + """ + + rw_type = 'dfs_post' + + def process_rev(self, rev_id): + """ + Append the revision to a stack. + + Args: + rev_id (str): hexadecimal representation of the newly visited + revision identifier + """ + if rev_id not in self._done: + self._revs_to_visit.append(rev_id) + + def get_next_rev_id(self): + """ + Returns the next revision from the stack. + + Returns: + dict: A dict describing a revision as returned by + :func:`swh.web.common.service.lookup_revision` + """ + return self._revs_to_visit.pop() + + +class DFSRevisionsWalker(DFSPostRevisionsWalker): + """ + Revisions walker that returns revisions in the same order + as when performing a depth-first search in pre-order on the + revisions tree (i.e. after visiting a merge commit, + the base commit it was merged on will be visited before + the merged commit). + """ + + rw_type = 'dfs' + + def process_parent_revs(self, rev): + """ + Process the parents of a revision when it is iterated in + the reversed order they are declared. + + Args: + rev (dict): A dict describing a revision as returned by + :func:`swh.web.common.service.lookup_revision` + """ + for parent_id in reversed(rev['parents']): + self.process_rev(parent_id) + + +class PathRevisionsWalker(CommitterDateRevisionsWalker): + """ + Revisions walker that returns revisions where a specific + path in the source tree has been modified, in other terms + it allows to get the history for a specific file or directory. + + It has a behaviour similar to what ``git log`` offers by + default, meaning the returned history is simplified in + order to only show relevant revisions. + + .. warning:: Due to client-side implementation, performances + are not optimal when the total numbers of revisions to walk + is large. This should only be used when the total number of + revisions does not exceed a couple of thousands. + + Args: + rev_start (str): hexadecimal representation of a revision + identifier + path (str): the path in the source tree to retrieve the history + max_revs (int): maximum number of revisions to return + state (dict): optional previous state of that revisions walker + """ + + rw_type = 'path' + + def __init__(self, rev_start, path, **kwargs): + super().__init__(rev_start, **kwargs) + self._path = path + self._rev_dir_path = {} + + def _get_path_sha1(self, rev_id): + + if not self._path: + return None + + rev = self._get_rev(rev_id) + + rev_dir_id = rev['directory'] + + if rev_dir_id not in self._rev_dir_path: + try: + dir_info = \ + service.lookup_directory_with_path(rev_dir_id, self._path) + self._rev_dir_path[rev_dir_id] = dir_info['target'] + except Exception: + self._rev_dir_path[rev_dir_id] = None + + return self._rev_dir_path[rev_dir_id] + + def is_finished(self): + """ + Check if the revisions iteration is finished or not by verifying + that the specified path is still present in the source trees + associated to the parents of the last returned revision. + If not, the iteration is considered as finished. + + Returns: + bool: Wether or not to return the revision in the iteration + """ + if self._path and self._last_rev: + last_rev_parents = self._last_rev['parents'] + last_rev_parents_path_sha1s = [self._get_path_sha1(p_rev) + for p_rev in last_rev_parents] + no_path = all([path_sha1 is None + for path_sha1 in last_rev_parents_path_sha1s]) + if no_path: + return True + return super().is_finished() + + def process_parent_revs(self, rev): + """ + Process parents when a new revision is iterated. + It enables to get a simplified revisions history in the same + manner as ``git log``. When a revision has multiple parents, + the following process is applied. If the revision was a merge, + and has the same path identifier to one parent, follow only that + parent (even if there are several parents with the same path + identifier, follow only one of them.) Otherwise, follow all parents. + + Args: + rev (dict): A dict describing a revision as returned by + :func:`swh.web.common.service.lookup_revision` + """ + rev_path_sha1 = self._get_path_sha1(rev['id']) + + if rev_path_sha1: + if len(rev['parents']) == 1: + self.process_rev(rev['parents'][0]) + else: + parent_rev_path_sha1s = [self._get_path_sha1(p_rev) + for p_rev in rev['parents']] + different_trees = all([path_sha1 != rev_path_sha1 + for path_sha1 in parent_rev_path_sha1s]) + for i, p_rev in enumerate(rev['parents']): + if different_trees or \ + parent_rev_path_sha1s[i] == rev_path_sha1: + self.process_rev(p_rev) + if not different_trees: + break + else: + super().process_parent_revs(rev) + + def should_return(self, rev): + """ + Checks if a revision should be returned when iterating. + It verifies that the specified path has been modified + by the revision but also that all parents have a path + identifier different from the revision one in order + to get a simplified history. + + Args: + rev (dict): A dict describing a revision as returned by + :func:`swh.web.common.service.lookup_revision` + + Returns: + bool: Wether or not to return the revision in the iteration + """ + if not self._path: + return True + + rev_path_sha1 = self._get_path_sha1(rev['id']) + + parent_rev_path_sha1s = [self._get_path_sha1(p_rev) + for p_rev in rev['parents']] + different_trees = all([path_sha1 != rev_path_sha1 + for path_sha1 in parent_rev_path_sha1s]) + + if rev_path_sha1 != parent_rev_path_sha1s[0] and different_trees: + return True + + return False + + +def get_revisions_walker(rev_walker_type, *args, **kwargs): + """ + Utility function to instantiate a revisions walker of a given type. + + Args: + rev_walker_type (str): the type of revisions walker to return, + possible values are: *committer_date*, *dfs*, *dfs_post*, + *bfs* and *path* + args (list): position arguments to pass to the revisions walker + constructor + kwargs (dict): keyword arguments to pass to the revisions walker + constructor + + """ + if rev_walker_type not in _revs_walker_classes: + raise Exception('No revisions walker found for type "%s"' + % rev_walker_type) + revs_walker_class = _revs_walker_classes[rev_walker_type] + return revs_walker_class(*args, **kwargs) diff --git a/swh/web/assets/src/bundles/browse/browse.css b/swh/web/assets/src/bundles/browse/browse.css --- a/swh/web/assets/src/bundles/browse/browse.css +++ b/swh/web/assets/src/bundles/browse/browse.css @@ -73,9 +73,9 @@ } .swh-log-entry-message { - min-width: 460px; - max-width: 460px; - width: 460px; + min-width: 440px; + max-width: 440px; + width: 440px; } .swh-popover { diff --git a/swh/web/assets/src/bundles/revision/index.js b/swh/web/assets/src/bundles/revision/index.js --- a/swh/web/assets/src/bundles/revision/index.js +++ b/swh/web/assets/src/bundles/revision/index.js @@ -9,3 +9,4 @@ import './revision.css'; export * from './diff-utils'; +export * from './log-utils'; diff --git a/swh/web/assets/src/bundles/revision/log-utils.js b/swh/web/assets/src/bundles/revision/log-utils.js new file mode 100644 --- /dev/null +++ b/swh/web/assets/src/bundles/revision/log-utils.js @@ -0,0 +1,20 @@ +export function revsOrderingTypeClicked(event) { + let urlParams = new URLSearchParams(window.location.search); + let orderingType = $(event.target).val(); + if (orderingType) { + urlParams.set('revs_ordering', $(event.target).val()); + } else if (urlParams.has('revs_ordering')) { + urlParams.delete('revs_ordering'); + } + window.location.search = urlParams.toString(); +} + +export function initRevisionsLog() { + $(document).ready(() => { + let urlParams = new URLSearchParams(window.location.search); + let revsOrderingType = urlParams.get('revs_ordering'); + if (revsOrderingType) { + $(`:input[value="${revsOrderingType}"]`).prop('checked', true); + } + }); +} diff --git a/swh/web/assets/src/bundles/revision/revision.css b/swh/web/assets/src/bundles/revision/revision.css --- a/swh/web/assets/src/bundles/revision/revision.css +++ b/swh/web/assets/src/bundles/revision/revision.css @@ -44,7 +44,13 @@ } .swh-revision-log-entry-date { - min-width: 230px; - max-width: 230px; - width: 230px; + min-width: 200px; + max-width: 200px; + width: 200px; +} + +.swh-revision-log-entry-commit-date { + min-width: 200px; + max-width: 200px; + width: 200px; } diff --git a/swh/web/browse/utils.py b/swh/web/browse/utils.py --- a/swh/web/browse/utils.py +++ b/swh/web/browse/utils.py @@ -748,7 +748,7 @@ return gen_link(content_url, link_text, link_attrs) -def get_revision_log_url(revision_id, snapshot_context=None): +def get_revision_log_url(revision_id, snapshot_context=None, path=None): """ Utility function for getting the URL for a SWH revision log HTML view (possibly in the context of an origin). @@ -781,7 +781,8 @@ query_params=query_params) else: revision_log_url = reverse('browse-revision-log', - kwargs={'sha1_git': revision_id}) + kwargs={'sha1_git': revision_id}, + query_params={'path': path}) return revision_log_url @@ -810,28 +811,7 @@ return gen_link(revision_log_url, link_text, link_attrs) -def _format_log_entries(revision_log, per_page, snapshot_context=None): - revision_log_data = [] - for i, log in enumerate(revision_log): - if i == per_page: - break - author_name = 'None' - author_link = 'None' - if log['author']: - author_name = log['author']['name'] or log['author']['fullname'] - author_link = gen_person_link(log['author']['id'], author_name, - snapshot_context) - revision_log_data.append( - {'author': author_link, - 'revision': gen_revision_link(log['id'], True, snapshot_context), - 'message': log['message'], - 'date': format_utc_iso_date(log['date']), - 'directory': log['directory']}) - return revision_log_data - - -def prepare_revision_log_for_display(revision_log, per_page, revs_breadcrumb, - snapshot_context=None): +def format_log_entries(revision_log, per_page, snapshot_context=None): """ Utility functions that process raw revision log data for HTML display. Its purpose is to: @@ -840,48 +820,32 @@ * format date in human readable format * truncate the message log - It also computes the data needed to generate the links for navigating back - and forth in the history log. - Args: revision_log (list): raw revision log as returned by the SWH web api per_page (int): number of log entries per page - revs_breadcrumb (str): breadcrumbs of revisions navigated so far, - in the form 'rev1[/rev2/../revN]'. Each revision corresponds to - the first one displayed in the HTML view for history log. snapshot_context (dict): if provided, generate snapshot-dependent browsing link """ - current_rev = revision_log[0]['id'] - next_rev = None - prev_rev = None - next_revs_breadcrumb = None - prev_revs_breadcrumb = None - if len(revision_log) == per_page + 1: - prev_rev = revision_log[-1]['id'] - - prev_rev_bc = current_rev - if snapshot_context: - prev_rev_bc = prev_rev - - if revs_breadcrumb: - revs = revs_breadcrumb.split('/') - next_rev = revs[-1] - if len(revs) > 1: - next_revs_breadcrumb = '/'.join(revs[:-1]) - if len(revision_log) == per_page + 1: - prev_revs_breadcrumb = revs_breadcrumb + '/' + prev_rev_bc - else: - prev_revs_breadcrumb = prev_rev_bc - - return {'revision_log_data': _format_log_entries(revision_log, per_page, - snapshot_context), - 'prev_rev': prev_rev, - 'prev_revs_breadcrumb': prev_revs_breadcrumb, - 'next_rev': next_rev, - 'next_revs_breadcrumb': next_revs_breadcrumb} + revision_log_data = [] + for i, log in enumerate(revision_log): + if i == per_page: + break + author_name = 'None' + author_link = 'None' + if log['author']: + author_name = log['author']['name'] or log['author']['fullname'] + author_link = gen_person_link(log['author']['id'], author_name, + snapshot_context) + revision_log_data.append({ + 'author': author_link, + 'revision': gen_revision_link(log['id'], True, snapshot_context), + 'message': log['message'], + 'date': format_utc_iso_date(log['date']), + 'commit_date': format_utc_iso_date(log['committer_date']) + }) + return revision_log_data # list of origin types that can be found in the swh archive diff --git a/swh/web/browse/views/revision.py b/swh/web/browse/views/revision.py --- a/swh/web/browse/views/revision.py +++ b/swh/web/browse/views/revision.py @@ -13,6 +13,7 @@ from django.utils.safestring import mark_safe from swh.model.identifiers import persistent_identifier +from swh.web.algos.revisions_walker import get_revisions_walker from swh.web.common import service from swh.web.common.utils import ( reverse, format_utc_iso_date, gen_path_info @@ -21,7 +22,7 @@ from swh.web.browse.browseurls import browse_route from swh.web.browse.utils import ( gen_link, gen_person_link, gen_revision_link, - prepare_revision_log_for_display, + format_log_entries, get_snapshot_context, gen_snapshot_directory_link, get_revision_log_url, get_directory_entries, gen_directory_link, request_content, prepare_content_for_display, @@ -171,7 +172,7 @@ return HttpResponse(diff_data_json, content_type='application/json') -NB_LOG_ENTRIES = 20 +NB_LOG_ENTRIES = 100 @browse_route(r'revision/(?P[0-9a-f]+)/log/', @@ -181,50 +182,56 @@ Django view that produces an HTML display of the history log for a SWH revision identified by its id. - The url that points to it is :http:get:`/browse/revision/(sha1_git)/log/`. + The url that points to it is :http:get:`/browse/revision/(sha1_git)/log/` """ # noqa try: per_page = int(request.GET.get('per_page', NB_LOG_ENTRIES)) - revision_log = service.lookup_revision_log(sha1_git, - limit=per_page+1) - revision_log = list(revision_log) + offset = int(request.GET.get('offset', 0)) + revs_ordering = request.GET.get('revs_ordering', 'committer_date') + session_key = 'rev_%s_log_ordering_%s' % (sha1_git, revs_ordering) + rev_log_session = request.session.get(session_key, None) + rev_log = [] + revs_walker_state = None + if rev_log_session: + rev_log = rev_log_session['rev_log'] + revs_walker_state = rev_log_session['revs_walker_state'] + + if len(rev_log) < offset+per_page: + revs_walker = get_revisions_walker(revs_ordering, sha1_git, + max_revs=offset+per_page+1, + state=revs_walker_state) + + rev_log += list(revs_walker) + revs_walker_state = revs_walker.export_state() + + revision_log = rev_log[offset:offset+per_page] + + request.session[session_key] = { + 'rev_log': rev_log, + 'revs_walker_state': revs_walker_state + } except Exception as exc: return handle_view_exception(request, exc) - revs_breadcrumb = request.GET.get('revs_breadcrumb', None) + revs_ordering = request.GET.get('revs_ordering', '') - revision_log_display_data = prepare_revision_log_for_display( - revision_log, per_page, revs_breadcrumb) - - prev_rev = revision_log_display_data['prev_rev'] - prev_revs_breadcrumb = revision_log_display_data['prev_revs_breadcrumb'] prev_log_url = None - if prev_rev: - prev_log_url = \ - reverse('browse-revision-log', - kwargs={'sha1_git': prev_rev}, - query_params={'revs_breadcrumb': prev_revs_breadcrumb, - 'per_page': per_page}) - - next_rev = revision_log_display_data['next_rev'] - next_revs_breadcrumb = revision_log_display_data['next_revs_breadcrumb'] + if len(rev_log) > offset + per_page: + prev_log_url = reverse('browse-revision-log', + kwargs={'sha1_git': sha1_git}, + query_params={'per_page': per_page, + 'offset': offset + per_page, + 'revs_ordering': revs_ordering}) + next_log_url = None - if next_rev: - next_log_url = \ - reverse('browse-revision-log', - kwargs={'sha1_git': next_rev}, - query_params={'revs_breadcrumb': next_revs_breadcrumb, - 'per_page': per_page}) - - revision_log_data = revision_log_display_data['revision_log_data'] - - for log in revision_log_data: - log['directory'] = gen_directory_link( - log['directory'], - link_text='Browse files', - link_attrs={'class': 'btn btn-default btn-sm', - 'role': 'button'}) + if offset != 0: + next_log_url = reverse('browse-revision-log', + kwargs={'sha1_git': sha1_git}, + query_params={'per_page': per_page, + 'offset': offset - per_page, + 'revs_ordering': revs_ordering}) + + revision_log_data = format_log_entries(revision_log, per_page) swh_rev_id = persistent_identifier('revision', sha1_git) @@ -336,7 +343,7 @@ revision_data['committer'] = \ gen_person_link(revision['committer']['id'], revision['committer']['name'], snapshot_context) - revision_data['committer date'] = format_utc_iso_date( + revision_data['committer_date'] = format_utc_iso_date( revision['committer_date']) revision_data['date'] = format_utc_iso_date(revision['date']) if snapshot_context: @@ -485,7 +492,7 @@ readme_name, readme_url, readme_html = get_readme_to_display(readmes) - top_right_link = get_revision_log_url(sha1_git, snapshot_context) + top_right_link = get_revision_log_url(sha1_git, snapshot_context, path) top_right_link_text = mark_safe( '' 'History') diff --git a/swh/web/browse/views/utils/snapshot_context.py b/swh/web/browse/views/utils/snapshot_context.py --- a/swh/web/browse/views/utils/snapshot_context.py +++ b/swh/web/browse/views/utils/snapshot_context.py @@ -16,11 +16,11 @@ get_snapshot_context, get_directory_entries, gen_directory_link, gen_revision_link, request_content, gen_content_link, prepare_content_for_display, content_display_max_size, - prepare_revision_log_for_display, gen_snapshot_directory_link, - gen_revision_log_link, gen_link, get_readme_to_display, - get_swh_persistent_ids, process_snapshot_branches + format_log_entries, gen_revision_log_link, gen_link, + get_readme_to_display, get_swh_persistent_ids, process_snapshot_branches ) +from swh.web.algos.revisions_walker import get_revisions_walker from swh.web.common import service from swh.web.common.exc import ( handle_view_exception, NotFoundExc @@ -306,6 +306,7 @@ browse_view_name = 'browse-' + swh_type + '-log' + query_params['path'] = path history_url = reverse(browse_view_name, kwargs=url_args, query_params=query_params) @@ -547,8 +548,8 @@ 'breadcrumbs': breadcrumbs, 'top_right_link': content_raw_url, 'top_right_link_text': mark_safe( - 'Raw File'), + 'Raw File'), 'snapshot_context': snapshot_context, 'vault_cooking': None, 'show_actions_menu': True, @@ -575,15 +576,32 @@ timestamp, browse_context='log') # noqa revision_id = snapshot_context['revision_id'] - current_rev = revision_id - per_page = int(request.GET.get('per_page', PER_PAGE)) - revs_breadcrumb = request.GET.get('revs_breadcrumb', None) - if revs_breadcrumb: - current_rev = revs_breadcrumb.split('/')[-1] - revision_log = service.lookup_revision_log(current_rev, - limit=per_page+1) - revision_log = list(revision_log) + per_page = int(request.GET.get('per_page', PER_PAGE)) + offset = int(request.GET.get('offset', 0)) + revs_ordering = request.GET.get('revs_ordering', 'committer_date') + session_key = 'rev_%s_log_ordering_%s' % (revision_id, revs_ordering) + rev_log_session = request.session.get(session_key, None) + rev_log = [] + revs_walker_state = None + if rev_log_session: + rev_log = rev_log_session['rev_log'] + revs_walker_state = rev_log_session['revs_walker_state'] + + if len(rev_log) < offset+per_page: + revs_walker = get_revisions_walker(revs_ordering, revision_id, + max_revs=offset+per_page+1, + state=revs_walker_state) + + rev_log += list(revs_walker) + revs_walker_state = revs_walker.export_state() + + revision_log = rev_log[offset:offset+per_page] + + request.session[session_key] = { + 'rev_log': rev_log, + 'revs_walker_state': revs_walker_state + } except Exception as exc: return handle_view_exception(request, exc) @@ -596,46 +614,27 @@ snapshot_id = snapshot_context['snapshot_id'] query_params['per_page'] = per_page - - revision_log_display_data = prepare_revision_log_for_display( - revision_log, per_page, revs_breadcrumb, snapshot_context) + revs_ordering = request.GET.get('revs_ordering', '') + query_params['revs_ordering'] = revs_ordering browse_view_name = 'browse-' + swh_type + '-log' - prev_rev = revision_log_display_data['prev_rev'] - prev_revs_breadcrumb = revision_log_display_data['prev_revs_breadcrumb'] prev_log_url = None - query_params['revs_breadcrumb'] = prev_revs_breadcrumb - if prev_rev: - prev_log_url = \ - reverse(browse_view_name, - kwargs=url_args, - query_params=query_params) - - next_rev = revision_log_display_data['next_rev'] - next_revs_breadcrumb = revision_log_display_data['next_revs_breadcrumb'] + if len(rev_log) > offset + per_page: + query_params['offset'] = offset + per_page + prev_log_url = reverse(browse_view_name, + kwargs=url_args, + query_params=query_params) + next_log_url = None - query_params['revs_breadcrumb'] = next_revs_breadcrumb - if next_rev: - next_log_url = \ - reverse(browse_view_name, - kwargs=url_args, - query_params=query_params) - - revision_log_data = revision_log_display_data['revision_log_data'] - - for i, log in enumerate(revision_log_data): - params = { - 'revision': revision_log[i]['id'], - } - if 'visit_id' in query_params: - params['visit_id'] = query_params['visit_id'] - log['directory'] = gen_snapshot_directory_link( - snapshot_context, revision_log[i]['id'], - link_text='Browse files', - link_attrs={'class': 'btn btn-default btn-sm', - 'role': 'button'}) + if offset != 0: + query_params['offset'] = offset - per_page + next_log_url = reverse(browse_view_name, + kwargs=url_args, + query_params=query_params) + + revision_log_data = format_log_entries(revision_log, per_page, + snapshot_context) browse_log_link = \ gen_revision_log_link(revision_id, link_text='Browse', diff --git a/swh/web/settings/common.py b/swh/web/settings/common.py --- a/swh/web/settings/common.py +++ b/swh/web/settings/common.py @@ -87,7 +87,6 @@ WSGI_APPLICATION = 'swh.web.wsgi.application' - DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', @@ -231,3 +230,5 @@ LOGIN_URL = '/admin/login/' LOGIN_REDIRECT_URL = 'admin' + +SESSION_ENGINE = 'django.contrib.sessions.backends.cache' diff --git a/swh/web/templates/browse/revision-log.html b/swh/web/templates/browse/revision-log.html --- a/swh/web/templates/browse/revision-log.html +++ b/swh/web/templates/browse/revision-log.html @@ -19,15 +19,40 @@ {% include "includes/top-navigation.html" %} {% endif %} +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
- - + + @@ -35,14 +60,19 @@ - - + + {% endfor %}
Revision AuthorMessage DateMessageCommit Date
{{ log.revision }} {{ log.message }} {{ log.directory }}{{ log.message }}{{ log.commit_date }}
+ + + {% endblock %} {% block swh-browse-after-content %} diff --git a/swh/web/templates/browse/revision.html b/swh/web/templates/browse/revision.html --- a/swh/web/templates/browse/revision.html +++ b/swh/web/templates/browse/revision.html @@ -19,7 +19,8 @@ {% block swh-browse-content %}
Revision {{ swh_object_metadata.id }} - authored by {{ swh_object_metadata.author }} on {{ swh_object_metadata.date }} + authored by {{ swh_object_metadata.author }} on {{ swh_object_metadata.date }}, + committed by {{ swh_object_metadata.committer }} on {{ swh_object_metadata.committer_date }}
diff --git a/swh/web/tests/algos/__init__.py b/swh/web/tests/algos/__init__.py new file mode 100644 diff --git a/swh/web/tests/algos/data/__init__.py b/swh/web/tests/algos/data/__init__.py new file mode 100644 diff --git a/swh/web/tests/algos/data/revisions_walker_data.py b/swh/web/tests/algos/data/revisions_walker_data.py new file mode 100644 --- /dev/null +++ b/swh/web/tests/algos/data/revisions_walker_data.py @@ -0,0 +1,1456 @@ +# Copyright (C) 2017-2018 The Software Heritage developers +# See the AUTHORS file at the top-level directory of this distribution +# License: GNU Affero General Public License version 3, or any later version +# See top-level LICENSE file for more information + +# flake8: noqa + +_test_revisions = \ +[{'author': {'email': 'peda@axentia.se', + 'fullname': 'Peter Rosin ', + 'id': 20423, + 'name': 'Peter Rosin'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-10-12T17:36:39+02:00', + 'date': '2018-10-12T14:46:46+00:00', + 'directory': '958488bee48a6fc59a3f5e1f6e0c9d7c297d26d3', + 'id': 'b40afc0066405e5ecfce73949b56ddd6bb65bd10', + 'merge': False, + 'parents': ['38a12607a82f3cba4149d36cc54695bd33f5ce04'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'ilias.apalodimas@linaro.org', + 'fullname': 'Ilias Apalodimas ', + 'id': 16007587, + 'name': 'Ilias Apalodimas'}, + 'committer': {'email': 'davem@davemloft.net', + 'fullname': 'David S. Miller ', + 'id': 9679, + 'name': 'David S. Miller'}, + 'committer_date': '2018-10-11T12:04:08-07:00', + 'date': '2018-10-11T15:28:26+03:00', + 'directory': 'a3d7e659ba40da4b3aff973ece4e3b40c38262c6', + 'id': '2a1e89df785082a0fd7264ca6d3d834abe84fa25', + 'merge': False, + 'parents': ['26450608348e91a782691dbfcc836478f4381071'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'swboyd@chromium.org', + 'fullname': 'Stephen Boyd ', + 'id': 19166245, + 'name': 'Stephen Boyd'}, + 'committer': {'email': 'linus.walleij@linaro.org', + 'fullname': 'Linus Walleij ', + 'id': 6282, + 'name': 'Linus Walleij'}, + 'committer_date': '2018-10-10T14:03:27+02:00', + 'date': '2018-10-08T09:32:13-07:00', + 'directory': 'e6cc2a6e4e82f1e0ddf47e8029fbe6ad9985902a', + 'id': '3e779a2e7f909015f21428b66834127496110b6d', + 'merge': False, + 'parents': ['0238df646e6224016a45505d2c111a24669ebe21'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-10-12T12:35:02+02:00', + 'date': '2018-10-12T12:35:02+02:00', + 'directory': 'e8e148c679517b0da74d82660a55444adcd32bc2', + 'id': 'eb81bfb224ced3701bcc7b08f309665bf0549252', + 'merge': True, + 'parents': ['0c53b6a5f82a539b59cdd669c4a866238371fa23', + 'cecf10704899467a787975e3d94a1f0129b9688e'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-10-12T12:57:05+02:00', + 'date': '2018-10-12T12:57:05+02:00', + 'directory': '46517e9473a3845bae5d40900a10bccf68fe91af', + 'id': '4ea07abbfbdadb10bc67c2b78dc2dbfafa4b1009', + 'merge': True, + 'parents': ['60bd7be764e8adada97034b4836875b3e449bd7c', + '4f666675cdff0b986195413215eb062b7da6586f'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'airlied@redhat.com', + 'fullname': 'Dave Airlie ', + 'id': 7651, + 'name': 'Dave Airlie'}, + 'committer': {'email': 'airlied@redhat.com', + 'fullname': 'Dave Airlie ', + 'id': 7651, + 'name': 'Dave Airlie'}, + 'committer_date': '2018-10-08T16:37:59+10:00', + 'date': '2018-10-08T16:37:56+10:00', + 'directory': '01e5489d6be31ac96103e110244c716fa78f82b0', + 'id': '5ba15878f23c4bafb95afa0dabe9d08d89520259', + 'merge': True, + 'parents': ['0238df646e6224016a45505d2c111a24669ebe21', + 'e46368cf77f2cb6304c51d9ff5f147cfb7dc0074'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-10-07T17:26:02+02:00', + 'date': '2018-10-07T17:26:02+02:00', + 'directory': 'da09b3454af44d2d8914ca3a719e66ff233bd029', + 'id': '0238df646e6224016a45505d2c111a24669ebe21', + 'merge': False, + 'parents': ['fb1c592cf4c9eeb84ec6bce13c13c11c7d05b6c7'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'mail@maciej.szmigiero.name', + 'fullname': 'Maciej S. Szmigiero ', + 'id': 19866, + 'name': 'Maciej S. Szmigiero'}, + 'committer': {'email': 'davem@davemloft.net', + 'fullname': 'David S. Miller ', + 'id': 9679, + 'name': 'David S. Miller'}, + 'committer_date': '2018-10-11T12:08:04-07:00', + 'date': '2018-10-11T16:02:10+02:00', + 'directory': 'bc23d803d7d71a1eebfe1eb370af75eeb3a64f87', + 'id': '511cfd580f23b0e0fcd5659931ef14c6e2c062b0', + 'merge': False, + 'parents': ['2a1e89df785082a0fd7264ca6d3d834abe84fa25'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-10-12T12:33:54+02:00', + 'date': '2018-10-12T12:33:54+02:00', + 'directory': '7999f45531fc8a693581d883a730ff9c8deb5a3f', + 'id': '0c53b6a5f82a539b59cdd669c4a866238371fa23', + 'merge': True, + 'parents': ['90ad18418c2d3db23ee827cdd74fed2ca9b70a18', + '52c8ee5bad8f33d02c567f6609f43d69303fc48d'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'f.fainelli@gmail.com', + 'fullname': 'Florian Fainelli ', + 'id': 7483, + 'name': 'Florian Fainelli'}, + 'committer': {'email': 'davem@davemloft.net', + 'fullname': 'David S. Miller ', + 'id': 9679, + 'name': 'David S. Miller'}, + 'committer_date': '2018-10-11T15:19:54-07:00', + 'date': '2018-10-09T16:48:58-07:00', + 'directory': '03791696cc800c5e8f1461b45f72d17893639e61', + 'id': '54baca096386d862d19c10f58f34bf787c6b3cbe', + 'merge': False, + 'parents': ['bf3b452b7af787b8bf27de6490dc4eedf6f97599'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'arnd@arndb.de', + 'fullname': 'Arnd Bergmann ', + 'id': 8479, + 'name': 'Arnd Bergmann'}, + 'committer': {'email': 'boris.brezillon@bootlin.com', + 'fullname': 'Boris Brezillon ', + 'id': 18434285, + 'name': 'Boris Brezillon'}, + 'committer_date': '2018-10-12T09:17:46+02:00', + 'date': '2018-10-11T13:06:17+02:00', + 'directory': '39bf89a0fa0faad1bfc759e5bf3cd4ef4765ca0d', + 'id': 'f0fe77f601c3d6a821198f88f7adb0a05b8fe03e', + 'merge': False, + 'parents': ['0238df646e6224016a45505d2c111a24669ebe21'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'dmitry.torokhov@gmail.com', + 'fullname': 'Dmitry Torokhov ', + 'id': 5834, + 'name': 'Dmitry Torokhov'}, + 'committer': {'email': 'dmitry.torokhov@gmail.com', + 'fullname': 'Dmitry Torokhov ', + 'id': 5834, + 'name': 'Dmitry Torokhov'}, + 'committer_date': '2018-10-05T11:42:32-07:00', + 'date': '2018-10-04T17:50:48-07:00', + 'directory': '07edd95c8cff1b92df3b63907f3a5a4bdf808574', + 'id': 'cecf10704899467a787975e3d94a1f0129b9688e', + 'merge': False, + 'parents': ['36d2582ff235b4e01ad64a734c877a52dc762d9c'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'Valentinef@mellanox.com', + 'fullname': 'Valentine Fatiev ', + 'id': 21084984, + 'name': 'Valentine Fatiev'}, + 'committer': {'email': 'dledford@redhat.com', + 'fullname': 'Doug Ledford ', + 'id': 7484, + 'name': 'Doug Ledford'}, + 'committer_date': '2018-10-10T14:52:43-04:00', + 'date': '2018-10-10T09:56:25+03:00', + 'directory': '5a88118732bb51ddc9155e4db24f2b4c15a7ce3b', + 'id': 'dd9a403495704fc80fb9f399003013ef2be2ee23', + 'merge': False, + 'parents': ['5c5702e259dc66e6fceed5117effab79c186e87a'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-10-12T17:41:27+02:00', + 'date': '2018-10-12T17:41:27+02:00', + 'directory': '2961b46722cb65e2ae69251fc6ae260fd7f84f91', + 'id': 'bab5c80b211035739997ebd361a679fa85b39465', + 'merge': True, + 'parents': ['f014ffb025c159fd51d19af8af0022a991aaa4f8', + '3f4258bbe0363b3d5330b93de0e86b7796ff1a74'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-10-12T12:53:48+02:00', + 'date': '2018-10-12T12:53:48+02:00', + 'directory': '03743efdbbac363afec5411f8debd1cc2e016cdc', + 'id': '62d2e531d7f353a77356c461bac12a0dc23aff7c', + 'merge': True, + 'parents': ['c789174bde280c90bb8cdc680cf58a8f03fcdca0', + '5ba15878f23c4bafb95afa0dabe9d08d89520259'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'davem@davemloft.net', + 'fullname': 'David S. Miller ', + 'id': 9679, + 'name': 'David S. Miller'}, + 'committer': {'email': 'davem@davemloft.net', + 'fullname': 'David S. Miller ', + 'id': 9679, + 'name': 'David S. Miller'}, + 'committer_date': '2018-10-11T15:20:00-07:00', + 'date': '2018-10-11T15:20:00-07:00', + 'directory': '03791696cc800c5e8f1461b45f72d17893639e61', + 'id': '6b9bab550cac108d86c731c207e3e74ea10eb638', + 'merge': True, + 'parents': ['052858663db31bd1ead76744df5d39d8bb703c77', + '54baca096386d862d19c10f58f34bf787c6b3cbe'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-10-12T12:56:25+02:00', + 'date': '2018-10-12T12:56:25+02:00', + 'directory': '1a0797a089f19627932a0256339662cb4f79b4d2', + 'id': '60bd7be764e8adada97034b4836875b3e449bd7c', + 'merge': True, + 'parents': ['ef0e75a47c4ad518aa3c91bb6a86a329b890f263', + '3e779a2e7f909015f21428b66834127496110b6d'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-10-12T09:01:59+02:00', + 'date': '2018-10-12T09:01:59+02:00', + 'directory': 'd495ed813ebf8acd4dec5ad1efe50da07268b112', + 'id': '90ad18418c2d3db23ee827cdd74fed2ca9b70a18', + 'merge': True, + 'parents': ['0778a9f2dd924c3af41971ba40eec44793aea531', + '6b9bab550cac108d86c731c207e3e74ea10eb638'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-10-12T12:53:06+02:00', + 'date': '2018-10-12T12:53:06+02:00', + 'directory': 'f65aa0bb90e4a627ea24698d24efcbedfc80a9b5', + 'id': 'c789174bde280c90bb8cdc680cf58a8f03fcdca0', + 'merge': True, + 'parents': ['eb81bfb224ced3701bcc7b08f309665bf0549252', + 'dd9a403495704fc80fb9f399003013ef2be2ee23'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'arnd@arndb.de', + 'fullname': 'Arnd Bergmann ', + 'id': 8479, + 'name': 'Arnd Bergmann'}, + 'committer': {'email': 'arnd@arndb.de', + 'fullname': 'Arnd Bergmann ', + 'id': 8479, + 'name': 'Arnd Bergmann'}, + 'committer_date': '2018-10-04T18:01:20+02:00', + 'date': '2018-10-04T17:37:17+02:00', + 'directory': '23a3018c9811fabb530a566c026098e1f256497f', + 'id': '3f4258bbe0363b3d5330b93de0e86b7796ff1a74', + 'merge': True, + 'parents': ['30a0af8826942cc6b1277f89a6ed3e3b986d1643', + '5a1eb8b9542884592a018829bb1ff20c9695d925'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'm.felsch@pengutronix.de', + 'fullname': 'Marco Felsch ', + 'id': 19714314, + 'name': 'Marco Felsch'}, + 'committer': {'email': 'linus.walleij@linaro.org', + 'fullname': 'Linus Walleij ', + 'id': 6282, + 'name': 'Linus Walleij'}, + 'committer_date': '2018-10-10T14:36:35+02:00', + 'date': '2018-10-02T10:06:46+02:00', + 'directory': 'ecea64683b1e6d765739ddbec161c810cd8dd641', + 'id': 'f259f896f2348f0302f6f88d4382378cf9d23a7e', + 'merge': False, + 'parents': ['0238df646e6224016a45505d2c111a24669ebe21'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'oberpar@linux.ibm.com', + 'fullname': 'Peter Oberparleiter ', + 'id': 20811234, + 'name': 'Peter Oberparleiter'}, + 'committer': {'email': 'sfr@canb.auug.org.au', + 'fullname': 'Stephen Rothwell ', + 'id': 7200, + 'name': 'Stephen Rothwell'}, + 'committer_date': '2018-10-12T08:54:58+11:00', + 'date': '2018-09-13T12:59:59+02:00', + 'directory': 'ae7b52e3e4b92eb90fbe745d75550a54c034292b', + 'id': '8dcf86caa1e3daf4a6ccf38e97f4f752b411f829', + 'merge': False, + 'parents': ['0778a9f2dd924c3af41971ba40eec44793aea531'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-10-12T12:54:26+02:00', + 'date': '2018-10-12T12:54:26+02:00', + 'directory': '4a56ee2340eefb14f8d2508538e2879b1964f893', + 'id': 'a291ab2d40e97a5ae0c07bf62f04ad7c438f66d0', + 'merge': True, + 'parents': ['62d2e531d7f353a77356c461bac12a0dc23aff7c', + 'f0fe77f601c3d6a821198f88f7adb0a05b8fe03e'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'daniel@zonque.org', + 'fullname': 'Daniel Mack ', + 'id': 18662, + 'name': 'Daniel Mack'}, + 'committer': {'email': 'ulf.hansson@linaro.org', + 'fullname': 'Ulf Hansson ', + 'id': 21162, + 'name': 'Ulf Hansson'}, + 'committer_date': '2018-10-10T14:01:50+02:00', + 'date': '2018-10-08T22:03:57+02:00', + 'directory': '098c48aa15d8994a40411d4b24a3a9a3f8941952', + 'id': '4f666675cdff0b986195413215eb062b7da6586f', + 'merge': False, + 'parents': ['41591b38f5f8f78344954b68582b5f00e56ffe61'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'f.fainelli@gmail.com', + 'fullname': 'Florian Fainelli ', + 'id': 7483, + 'name': 'Florian Fainelli'}, + 'committer': {'email': 'davem@davemloft.net', + 'fullname': 'David S. Miller ', + 'id': 9679, + 'name': 'David S. Miller'}, + 'committer_date': '2018-10-11T15:19:40-07:00', + 'date': '2018-10-09T16:48:57-07:00', + 'directory': '7d3ceba5185dc48b4fbc0d76fbfce2e6948cc661', + 'id': 'bf3b452b7af787b8bf27de6490dc4eedf6f97599', + 'merge': False, + 'parents': ['052858663db31bd1ead76744df5d39d8bb703c77'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'dhowells@redhat.com', + 'fullname': 'David Howells ', + 'id': 10239, + 'name': 'David Howells'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-10-12T17:36:40+02:00', + 'date': '2018-10-12T14:00:57+01:00', + 'directory': '110f6d403c8d19328d972c0ef271e759686a1f79', + 'id': 'f014ffb025c159fd51d19af8af0022a991aaa4f8', + 'merge': False, + 'parents': ['b40afc0066405e5ecfce73949b56ddd6bb65bd10'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-10-12T12:55:47+02:00', + 'date': '2018-10-12T12:55:47+02:00', + 'directory': '6277c5ffe503dcef640ff0d2f3d8ada800985bc6', + 'id': 'ef0e75a47c4ad518aa3c91bb6a86a329b890f263', + 'merge': True, + 'parents': ['a291ab2d40e97a5ae0c07bf62f04ad7c438f66d0', + 'f259f896f2348f0302f6f88d4382378cf9d23a7e'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'bigeasy@linutronix.de', + 'fullname': 'Sebastian Andrzej Siewior ', + 'id': 6504, + 'name': 'Sebastian Andrzej Siewior'}, + 'committer': {'email': 'davem@davemloft.net', + 'fullname': 'David S. Miller ', + 'id': 9679, + 'name': 'David S. Miller'}, + 'committer_date': '2018-10-11T12:10:06-07:00', + 'date': '2018-10-11T17:06:21+02:00', + 'directory': '798d792ae2975d6fe5d57b71edf952ba9e094c8a', + 'id': '052858663db31bd1ead76744df5d39d8bb703c77', + 'merge': False, + 'parents': ['511cfd580f23b0e0fcd5659931ef14c6e2c062b0'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-10-11T19:24:01+02:00', + 'date': '2018-10-11T19:24:01+02:00', + 'directory': '0df1b3fcfd364447046c88efec32668b20857078', + 'id': '0778a9f2dd924c3af41971ba40eec44793aea531', + 'merge': True, + 'parents': ['e5337178f7023bd06a39c06e1fab88817559c0f3', + '479adb89a97b0a33e5a9d702119872cc82ca21aa'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'peda@axentia.se', + 'fullname': 'Peter Rosin ', + 'id': 20423, + 'name': 'Peter Rosin'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-10-12T17:36:39+02:00', + 'date': '2018-10-12T14:46:40+00:00', + 'directory': '66c6db18c49af52ce134c72f877af66b3d200ed1', + 'id': '38a12607a82f3cba4149d36cc54695bd33f5ce04', + 'merge': False, + 'parents': ['6b3944e42e2e554aa5a4be681ecd70dccd459114'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'oberpar@linux.ibm.com', + 'fullname': 'Peter Oberparleiter ', + 'id': 20811234, + 'name': 'Peter Oberparleiter'}, + 'committer': {'email': 'sfr@canb.auug.org.au', + 'fullname': 'Stephen Rothwell ', + 'id': 7200, + 'name': 'Stephen Rothwell'}, + 'committer_date': '2018-10-12T08:55:29+11:00', + 'date': '2018-09-13T13:00:00+02:00', + 'directory': '9a89e266e294bc1ae01a418d33bf3920e41712ea', + 'id': '52c8ee5bad8f33d02c567f6609f43d69303fc48d', + 'merge': False, + 'parents': ['8dcf86caa1e3daf4a6ccf38e97f4f752b411f829'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'dhowells@redhat.com', + 'fullname': 'David Howells ', + 'id': 10239, + 'name': 'David Howells'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-10-12T13:18:57+02:00', + 'date': '2018-10-11T22:45:49+01:00', + 'directory': '811bc9bccb4da48aed07bd73891b65181b6e5873', + 'id': '6b3944e42e2e554aa5a4be681ecd70dccd459114', + 'merge': False, + 'parents': ['4ea07abbfbdadb10bc67c2b78dc2dbfafa4b1009'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'damien.lemoal@wdc.com', + 'fullname': 'Damien Le Moal ', + 'id': 13775071, + 'name': 'Damien Le Moal'}, + 'committer': {'email': 'snitzer@redhat.com', + 'fullname': 'Mike Snitzer ', + 'id': 6293, + 'name': 'Mike Snitzer'}, + 'committer_date': '2018-10-10T23:22:24-04:00', + 'date': '2018-10-11T11:45:30+09:00', + 'directory': 'ec3d4b6b6432a68c2ca3808961094e486e87a590', + 'id': '118aa47c7072bce05fc39bd40a1c0a90caed72ab', + 'merge': False, + 'parents': ['beb9caac211c1be1bc118bb62d5cf09c4107e6a5'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'srikar@linux.vnet.ibm.com', + 'fullname': 'Srikar Dronamraju ', + 'id': 8404, + 'name': 'Srikar Dronamraju'}, + 'committer': {'email': 'mingo@kernel.org', + 'fullname': 'Ingo Molnar ', + 'id': 6870, + 'name': 'Ingo Molnar'}, + 'committer_date': '2018-10-09T08:30:51+02:00', + 'date': '2018-10-06T16:53:19+05:30', + 'directory': 'ab3f8787ea79cd2b4dd850daf70e1749b3528866', + 'id': 'e054637597ba36d3729ba6a3a3dd7aad8e2a3003', + 'merge': False, + 'parents': ['0238df646e6224016a45505d2c111a24669ebe21'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-10-11T07:18:39+02:00', + 'date': '2018-10-11T07:18:39+02:00', + 'directory': '907532488dd7ba0562159d5534b8a52bc2c739b5', + 'id': '9dcd936c5312f870955f108e8a1bfebf3eb6f688', + 'merge': True, + 'parents': ['4718dcad7decac3a43b7339b2226f3d987cca75c', + '118aa47c7072bce05fc39bd40a1c0a90caed72ab'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-10-11T07:17:42+02:00', + 'date': '2018-10-11T07:17:42+02:00', + 'directory': '0b80ffa06cbc6d307203696bde1cbeb50c76dcd7', + 'id': '4718dcad7decac3a43b7339b2226f3d987cca75c', + 'merge': True, + 'parents': ['b8db9e69dba97075d37d7bc20ef49f39298e3875', + 'b39989009bdb84992915c9869f58094ed5becf10'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-10-11T12:27:03+02:00', + 'date': '2018-10-11T12:27:03+02:00', + 'directory': 'f603eb3a89d27ec9e38e56f9a4b9998e03b30f0a', + 'id': '6302aad48c5cb56c9f9c3fb7aa73d2f1f2f10540', + 'merge': True, + 'parents': ['9dcd936c5312f870955f108e8a1bfebf3eb6f688', + 'c1883f10cfe05c707cce46d6999411c50a2413ca'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-10-11T19:10:30+02:00', + 'date': '2018-10-11T19:10:30+02:00', + 'directory': 'ac7f551b7192bc9236bc45d4641053267733a8e1', + 'id': '834d3cd294abd9ad81c6cbaefdda28aa491ceb06', + 'merge': True, + 'parents': ['9f203e2f2f065cd74553e6474f0ae3675f39fb0f', + '329e09893909d409039f6a79757d9b80b67efe39'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'yamada.masahiro@socionext.com', + 'fullname': 'Masahiro Yamada ', + 'id': 19990, + 'name': 'Masahiro Yamada'}, + 'committer': {'email': 'yamada.masahiro@socionext.com', + 'fullname': 'Masahiro Yamada ', + 'id': 19990, + 'name': 'Masahiro Yamada'}, + 'committer_date': '2018-10-11T02:15:46+09:00', + 'date': '2018-09-18T12:58:33+09:00', + 'directory': '20d919244dc011d8abbd9aae795ac8f8149b7cc7', + 'id': '5318321d367c21ca1fe9eb00caefc239c938750a', + 'merge': False, + 'parents': ['ef8c4ed9db80261f397f0c0bf723684601ae3b52'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-10-11T19:23:07+02:00', + 'date': '2018-10-11T19:23:07+02:00', + 'directory': '04e9e9a0c674e0f9bab9102d02c269177406ac5c', + 'id': 'e5337178f7023bd06a39c06e1fab88817559c0f3', + 'merge': True, + 'parents': ['834d3cd294abd9ad81c6cbaefdda28aa491ceb06', + '5318321d367c21ca1fe9eb00caefc239c938750a'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-10-11T12:28:23+02:00', + 'date': '2018-10-11T12:28:23+02:00', + 'directory': '43dbe4103fe13a6305b5118f1905056f78788c3e', + 'id': '9f203e2f2f065cd74553e6474f0ae3675f39fb0f', + 'merge': True, + 'parents': ['a22dd3629e257e5db51ad12610d00bb2856b291d', + '184d47f0fd365108bd06ab26cdb3450b716269fd'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-10-10T22:13:20+02:00', + 'date': '2018-10-10T22:13:20+02:00', + 'directory': '498bb3324f159653079919f85364cc6b488e1846', + 'id': 'b8db9e69dba97075d37d7bc20ef49f39298e3875', + 'merge': True, + 'parents': ['588b593821b8dd6efe6b6850930b501fc3c94a13', + 'beb9caac211c1be1bc118bb62d5cf09c4107e6a5'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'keescook@chromium.org', + 'fullname': 'Kees Cook ', + 'id': 7782, + 'name': 'Kees Cook'}, + 'committer': {'email': 'keescook@chromium.org', + 'fullname': 'Kees Cook ', + 'id': 7782, + 'name': 'Kees Cook'}, + 'committer_date': '2018-10-05T18:06:30-07:00', + 'date': '2018-10-05T16:21:46-07:00', + 'directory': 'd83759b28dcc8760167ed7c082d5846f63a55d2d', + 'id': '329e09893909d409039f6a79757d9b80b67efe39', + 'merge': False, + 'parents': ['57361846b52bc686112da6ca5368d11210796804'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'david@fromorbit.com', + 'fullname': 'Dave Chinner ', + 'id': 5566, + 'name': 'Dave Chinner'}, + 'committer': {'email': 'david@fromorbit.com', + 'fullname': 'Dave Chinner ', + 'id': 5566, + 'name': 'Dave Chinner'}, + 'committer_date': '2018-10-06T11:44:39+10:00', + 'date': '2018-10-06T11:44:39+10:00', + 'directory': '574f76831c2cd648c830512c18dabba768ac96eb', + 'id': 'b39989009bdb84992915c9869f58094ed5becf10', + 'merge': False, + 'parents': ['dceeb47b0ed65e14de53507a8a9c32a90831cfa1'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'tj@kernel.org', + 'fullname': 'Tejun Heo ', + 'id': 5197, + 'name': 'Tejun Heo'}, + 'committer': {'email': 'tj@kernel.org', + 'fullname': 'Tejun Heo ', + 'id': 5197, + 'name': 'Tejun Heo'}, + 'committer_date': '2018-10-04T13:28:08-07:00', + 'date': '2018-10-04T13:28:08-07:00', + 'directory': '37f96aab9020c33766d1b0c75d61ac2a3429371c', + 'id': '479adb89a97b0a33e5a9d702119872cc82ca21aa', + 'merge': False, + 'parents': ['ac0657edb13ac7971569aa88cc6f27ea26deb883'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'keescook@chromium.org', + 'fullname': 'Kees Cook ', + 'id': 7782, + 'name': 'Kees Cook'}, + 'committer': {'email': 'mingo@kernel.org', + 'fullname': 'Ingo Molnar ', + 'id': 6870, + 'name': 'Ingo Molnar'}, + 'committer_date': '2018-10-09T08:55:07+02:00', + 'date': '2018-10-08T16:54:34-07:00', + 'directory': '4be24aedef29acd528c29ee904868e8539c2bc9a', + 'id': '184d47f0fd365108bd06ab26cdb3450b716269fd', + 'merge': False, + 'parents': ['49e00eee00612b1357596fed8a88b621a7648c14'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-10-11T12:27:47+02:00', + 'date': '2018-10-11T12:27:47+02:00', + 'directory': '9589763bcba11e2c9fcecd5e6ad49caa9f95e069', + 'id': 'a22dd3629e257e5db51ad12610d00bb2856b291d', + 'merge': True, + 'parents': ['6302aad48c5cb56c9f9c3fb7aa73d2f1f2f10540', + 'e054637597ba36d3729ba6a3a3dd7aad8e2a3003'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'mingo@kernel.org', + 'fullname': 'Ingo Molnar ', + 'id': 6870, + 'name': 'Ingo Molnar'}, + 'committer': {'email': 'mingo@kernel.org', + 'fullname': 'Ingo Molnar ', + 'id': 6870, + 'name': 'Ingo Molnar'}, + 'committer_date': '2018-10-05T18:14:00+02:00', + 'date': '2018-10-05T18:14:00+02:00', + 'directory': '32d568c275f8cc135ae40f69f82852ff5558695e', + 'id': 'c1883f10cfe05c707cce46d6999411c50a2413ca', + 'merge': True, + 'parents': ['d7cbbe49a9304520181fb8c9272d1327deec8453', + '7a8a8fcf7b860e4b2d4edc787c844d41cad9dfcf'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'torvalds@linux-foundation.org', + 'fullname': 'Linus Torvalds ', + 'id': 8275, + 'name': 'Linus Torvalds'}, + 'committer': {'email': 'torvalds@linux-foundation.org', + 'fullname': 'Linus Torvalds ', + 'id': 8275, + 'name': 'Linus Torvalds'}, + 'committer_date': '2018-08-17T09:52:15-07:00', + 'date': '2018-08-17T09:52:15-07:00', + 'directory': 'f005473e83be269f7a69a43af5c304a3fe4c3e0c', + 'id': 'b0e5c29426940bd6f137b6a3222fe87766323ae5', + 'merge': True, + 'parents': ['2645b9d1a49c2c2cf23895657bdf9a56e07a4da8', + '1e1132ea21da6d7be92a72195204379c819cb70b'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'djeffery@redhat.com', + 'fullname': 'David Jeffery ', + 'id': 7947, + 'name': 'David Jeffery'}, + 'committer': {'email': 'snitzer@redhat.com', + 'fullname': 'Mike Snitzer ', + 'id': 6293, + 'name': 'Mike Snitzer'}, + 'committer_date': '2018-08-08T10:41:49-04:00', + 'date': '2018-08-07T16:56:00-04:00', + 'directory': 'f88276f29b02ad50a15e72ffcf9c67e395262db0', + 'id': '3db2776d9fca45305e6c2065905d9a0e7b2c8212', + 'merge': False, + 'parents': ['784c9a29e99eb40b842c29ecf1cc3a79e00fb629'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'torvalds@linux-foundation.org', + 'fullname': 'Linus Torvalds ', + 'id': 8275, + 'name': 'Linus Torvalds'}, + 'committer': {'email': 'torvalds@linux-foundation.org', + 'fullname': 'Linus Torvalds ', + 'id': 8275, + 'name': 'Linus Torvalds'}, + 'committer_date': '2018-08-26T13:39:05-07:00', + 'date': '2018-08-26T13:39:05-07:00', + 'directory': 'ac02c7e9c6776e8208275e0b4f333138438fc0ee', + 'id': 'b933d6ebf2d367647af870441341f76242259898', + 'merge': True, + 'parents': ['aba16dc5cf9318b4e0fe92f8261779cd9f1d2d77', + 'fd991a23c8f6ef30692f77409602ccf3614353b2'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'arnd@arndb.de', + 'fullname': 'Arnd Bergmann ', + 'id': 8479, + 'name': 'Arnd Bergmann'}, + 'committer': {'email': 'arnd@arndb.de', + 'fullname': 'Arnd Bergmann ', + 'id': 8479, + 'name': 'Arnd Bergmann'}, + 'committer_date': '2018-10-04T17:34:53+02:00', + 'date': '2018-10-04T17:33:37+02:00', + 'directory': '389649890e49406e93d7645da513efa9d02aa75e', + 'id': '30a0af8826942cc6b1277f89a6ed3e3b986d1643', + 'merge': True, + 'parents': ['17b57b1883c1285f3d0dc2266e8f79286a7bef38', + 'eea96566c189c77e5272585984eb2729881a2f1d'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'olof@lixom.net', + 'fullname': 'Olof Johansson ', + 'id': 6861, + 'name': 'Olof Johansson'}, + 'committer': {'email': 'olof@lixom.net', + 'fullname': 'Olof Johansson ', + 'id': 6861, + 'name': 'Olof Johansson'}, + 'committer_date': '2018-09-25T12:31:19-07:00', + 'date': '2018-09-25T12:30:16-07:00', + 'directory': 'ab3e8911c34b58575b2d1c3a2d7f6fa75a448962', + 'id': '9f71e7d5d97b8ed1262b777fd5b37667aa18d3fd', + 'merge': True, + 'parents': ['e6f0e1c7569508f5b0e23cbac6e5d9da3f9bb284', + '379e36d3f591cb62e781907edafea17d75e8c1fa'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'f.fainelli@gmail.com', + 'fullname': 'Florian Fainelli ', + 'id': 7483, + 'name': 'Florian Fainelli'}, + 'committer': {'email': 'f.fainelli@gmail.com', + 'fullname': 'Florian Fainelli ', + 'id': 7483, + 'name': 'Florian Fainelli'}, + 'committer_date': '2018-09-24T11:04:04-07:00', + 'date': '2018-09-19T17:14:01-07:00', + 'directory': '5cdd01e69affb4c4ba7fdcc2aab0174e995f4070', + 'id': '3ab97942d0213b6583a5408630a8cbbfbf54730f', + 'merge': False, + 'parents': ['5b394b2ddf0347bef56e50c69a58773c94343ff3'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'agrover@redhat.com', + 'fullname': 'Andy Grover ', + 'id': 2402, + 'name': 'Andy Grover'}, + 'committer': {'email': 'snitzer@redhat.com', + 'fullname': 'Mike Snitzer ', + 'id': 6293, + 'name': 'Mike Snitzer'}, + 'committer_date': '2018-07-30T11:49:08-04:00', + 'date': '2018-07-27T15:51:57-07:00', + 'directory': 'c40e72bd99cdac2f87fea33df6cf666740c0d0cf', + 'id': '63c8ecb6261abcb79191a264778e8dae222e67cf', + 'merge': False, + 'parents': ['9ff07e7d634cb005e7c5dc5e2c28a06508eb4fbf'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'mpatocka@redhat.com', + 'fullname': 'Mikulas Patocka ', + 'id': 10199, + 'name': 'Mikulas Patocka'}, + 'committer': {'email': 'snitzer@redhat.com', + 'fullname': 'Mike Snitzer ', + 'id': 6293, + 'name': 'Mike Snitzer'}, + 'committer_date': '2018-08-13T15:28:41-04:00', + 'date': '2018-08-10T11:23:56-04:00', + 'directory': '963521449bca32037e0ac110e9faefc8f7a898c7', + 'id': 'bc9e9cf0401f18e33b78d4c8a518661b8346baf7', + 'merge': False, + 'parents': ['5b1fe7bec8a8d0cc547a22e7ddc2bd59acd67de4'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'jpittman@redhat.com', + 'fullname': 'John Pittman ', + 'id': 12191058, + 'name': 'John Pittman'}, + 'committer': {'email': 'snitzer@redhat.com', + 'fullname': 'Mike Snitzer ', + 'id': 6293, + 'name': 'Mike Snitzer'}, + 'committer_date': '2018-08-08T09:16:24-04:00', + 'date': '2018-08-06T15:53:12-04:00', + 'directory': 'ce11f4e06591c991e0fea624392b23a45b8b05c1', + 'id': '784c9a29e99eb40b842c29ecf1cc3a79e00fb629', + 'merge': False, + 'parents': ['fd2fa95416188a767a63979296fa3e169a9ef5ec'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'houtao1@huawei.com', + 'fullname': 'Hou Tao ', + 'id': 12102431, + 'name': 'Hou Tao'}, + 'committer': {'email': 'snitzer@redhat.com', + 'fullname': 'Mike Snitzer ', + 'id': 6293, + 'name': 'Mike Snitzer'}, + 'committer_date': '2018-08-07T14:30:29-04:00', + 'date': '2018-08-02T16:18:24+08:00', + 'directory': 'b60b709a7048a7864763f61b9e7628f51de8778a', + 'id': '75294442d896f2767be34f75aca7cc2b0d01301f', + 'merge': False, + 'parents': ['7209049d40dc37791ce0f3738965296f30e26044'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'qiang.zhao@nxp.com', + 'fullname': 'Zhao Qiang ', + 'id': 8166284, + 'name': 'Zhao Qiang'}, + 'committer': {'email': 'olof@lixom.net', + 'fullname': 'Olof Johansson ', + 'id': 6861, + 'name': 'Olof Johansson'}, + 'committer_date': '2018-09-25T13:57:26-07:00', + 'date': '2018-02-01T14:54:32+08:00', + 'directory': '0937fc7a47037c76bbe3880d75177de4f9839939', + 'id': '96fc74333f84cfdf8d434c6c07254e215e2aad00', + 'merge': False, + 'parents': ['64e9e22e68512da8df3c9a7430f07621e48db3c2'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'sean.j.christopherson@intel.com', + 'fullname': 'Sean Christopherson ' + '', + 'id': 12071225, + 'name': 'Sean Christopherson'}, + 'committer': {'email': 'torvalds@linux-foundation.org', + 'fullname': 'Linus Torvalds ', + 'id': 8275, + 'name': 'Linus Torvalds'}, + 'committer_date': '2018-08-17T10:27:36-07:00', + 'date': '2018-08-17T10:27:36-07:00', + 'directory': '6aa5e5ca91887897c171ea5d7bb810cf1e2573e0', + 'id': 'f19f5c49bbc3ffcc9126cc245fc1b24cc29f4a37', + 'merge': False, + 'parents': ['b0e5c29426940bd6f137b6a3222fe87766323ae5'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'mpatocka@redhat.com', + 'fullname': 'Mikulas Patocka ', + 'id': 10199, + 'name': 'Mikulas Patocka'}, + 'committer': {'email': 'snitzer@redhat.com', + 'fullname': 'Mike Snitzer ', + 'id': 6293, + 'name': 'Mike Snitzer'}, + 'committer_date': '2018-08-16T13:43:01-04:00', + 'date': '2018-08-16T12:23:19-04:00', + 'directory': 'b983945975d697c37ac0193d18ca022cca4c3fb0', + 'id': '1e1132ea21da6d7be92a72195204379c819cb70b', + 'merge': False, + 'parents': ['bc9e9cf0401f18e33b78d4c8a518661b8346baf7'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'torvalds@linux-foundation.org', + 'fullname': 'Linus Torvalds ', + 'id': 8275, + 'name': 'Linus Torvalds'}, + 'committer': {'email': 'torvalds@linux-foundation.org', + 'fullname': 'Linus Torvalds ', + 'id': 8275, + 'name': 'Linus Torvalds'}, + 'committer_date': '2018-08-26T14:11:59-07:00', + 'date': '2018-08-26T14:11:59-07:00', + 'directory': 'c671c685ac53348259d386d35e4b0efbbe41e5aa', + 'id': '5b394b2ddf0347bef56e50c69a58773c94343ff3', + 'merge': False, + 'parents': ['b933d6ebf2d367647af870441341f76242259898'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'laurentiu.tudor@nxp.com', + 'fullname': 'Laurentiu Tudor ', + 'id': 14341520, + 'name': 'Laurentiu Tudor'}, + 'committer': {'email': 'leoyang.li@nxp.com', + 'fullname': 'Li Yang ', + 'id': 9171189, + 'name': 'Li Yang'}, + 'committer_date': '2018-09-27T15:43:35-05:00', + 'date': '2018-09-26T16:22:30+03:00', + 'directory': '0e0afdd4c45a3e096fc5b7c85380aaede6d6b8b6', + 'id': '853dc104e6a43cba9a7a87542bc6d8e3b74f0bc9', + 'merge': False, + 'parents': ['96fc74333f84cfdf8d434c6c07254e215e2aad00'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'torvalds@linux-foundation.org', + 'fullname': 'Linus Torvalds ', + 'id': 8275, + 'name': 'Linus Torvalds'}, + 'committer': {'email': 'torvalds@linux-foundation.org', + 'fullname': 'Linus Torvalds ', + 'id': 8275, + 'name': 'Linus Torvalds'}, + 'committer_date': '2018-08-17T09:41:28-07:00', + 'date': '2018-08-17T09:41:28-07:00', + 'directory': '5fb6fad54ed3ca01280f1d947879d62798609cac', + 'id': '2645b9d1a49c2c2cf23895657bdf9a56e07a4da8', + 'merge': True, + 'parents': ['46e62a072a8254e9e3765fdc999aba325b7918df', + '4d97f7d53da7dc830dbf416a3d2a6778d267ae68'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'laurentiu.tudor@nxp.com', + 'fullname': 'Laurentiu Tudor ', + 'id': 14341520, + 'name': 'Laurentiu Tudor'}, + 'committer': {'email': 'leoyang.li@nxp.com', + 'fullname': 'Li Yang ', + 'id': 9171189, + 'name': 'Li Yang'}, + 'committer_date': '2018-10-01T17:47:43-05:00', + 'date': '2018-09-26T16:22:31+03:00', + 'directory': '6d2c05ee8502bf8b3fdf3ad7105f73d06d09623c', + 'id': '5a1eb8b9542884592a018829bb1ff20c9695d925', + 'merge': False, + 'parents': ['853dc104e6a43cba9a7a87542bc6d8e3b74f0bc9'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'snitzer@redhat.com', + 'fullname': 'Mike Snitzer ', + 'id': 6293, + 'name': 'Mike Snitzer'}, + 'committer': {'email': 'snitzer@redhat.com', + 'fullname': 'Mike Snitzer ', + 'id': 6293, + 'name': 'Mike Snitzer'}, + 'committer_date': '2018-07-31T17:33:21-04:00', + 'date': '2018-07-31T17:27:02-04:00', + 'directory': '1e469739cfedec594032343f889bea5dab3fffb2', + 'id': '7209049d40dc37791ce0f3738965296f30e26044', + 'merge': False, + 'parents': ['63c8ecb6261abcb79191a264778e8dae222e67cf'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'torvalds@linux-foundation.org', + 'fullname': 'Linus Torvalds ', + 'id': 8275, + 'name': 'Linus Torvalds'}, + 'committer': {'email': 'torvalds@linux-foundation.org', + 'fullname': 'Linus Torvalds ', + 'id': 8275, + 'name': 'Linus Torvalds'}, + 'committer_date': '2018-08-26T11:48:42-07:00', + 'date': '2018-08-26T11:48:42-07:00', + 'directory': '1f53d2cee40e82efe6a727208307af475327af9a', + 'id': 'aba16dc5cf9318b4e0fe92f8261779cd9f1d2d77', + 'merge': True, + 'parents': ['c4726e774ed27680c418e138234dfd2b8e1e89ac', + '1df895190233fcc30d46beca4550bcafb7b959a6'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'snitzer@redhat.com', + 'fullname': 'Mike Snitzer ', + 'id': 6293, + 'name': 'Mike Snitzer'}, + 'committer': {'email': 'snitzer@redhat.com', + 'fullname': 'Mike Snitzer ', + 'id': 6293, + 'name': 'Mike Snitzer'}, + 'committer_date': '2018-08-07T14:30:30-04:00', + 'date': '2018-08-02T16:08:52-04:00', + 'directory': '18fcd992ce788f48fd53964edaf3fa0d05ebb4c8', + 'id': 'fd2fa95416188a767a63979296fa3e169a9ef5ec', + 'merge': False, + 'parents': ['75294442d896f2767be34f75aca7cc2b0d01301f'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'alexandre.belloni@bootlin.com', + 'fullname': 'Alexandre Belloni ', + 'id': 18447214, + 'name': 'Alexandre Belloni'}, + 'committer': {'email': 'olof@lixom.net', + 'fullname': 'Olof Johansson ', + 'id': 6861, + 'name': 'Olof Johansson'}, + 'committer_date': '2018-09-25T13:57:25-07:00', + 'date': '2018-08-23T23:36:00+02:00', + 'directory': '584df8f3b1e15ed9103c26cd146be64b83e8fbef', + 'id': '64e9e22e68512da8df3c9a7430f07621e48db3c2', + 'merge': False, + 'parents': ['1a677ff4ce6acc3bdb2d01bb25eb687a6e28aa1d'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'arnd@arndb.de', + 'fullname': 'Arnd Bergmann ', + 'id': 8479, + 'name': 'Arnd Bergmann'}, + 'committer': {'email': 'tglx@linutronix.de', + 'fullname': 'Thomas Gleixner ', + 'id': 6035, + 'name': 'Thomas Gleixner'}, + 'committer_date': '2018-08-22T15:11:35+02:00', + 'date': '2018-08-21T22:33:00+02:00', + 'directory': '212ebd0eeb1ce42e71f1d6d592dc3d35a9ce53a9', + 'id': 'fd991a23c8f6ef30692f77409602ccf3614353b2', + 'merge': False, + 'parents': ['f19f5c49bbc3ffcc9126cc245fc1b24cc29f4a37'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'snitzer@redhat.com', + 'fullname': 'Mike Snitzer ', + 'id': 6293, + 'name': 'Mike Snitzer'}, + 'committer': {'email': 'snitzer@redhat.com', + 'fullname': 'Mike Snitzer ', + 'id': 6293, + 'name': 'Mike Snitzer'}, + 'committer_date': '2018-08-08T20:50:58-04:00', + 'date': '2018-08-08T20:50:58-04:00', + 'directory': '7827ac389f9c3559b7ccc27b9c5a819a8e616e82', + 'id': 'c9a5e6a968bd328753b694d19b952068c65dc5e7', + 'merge': False, + 'parents': ['3db2776d9fca45305e6c2065905d9a0e7b2c8212'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'idryomov@gmail.com', + 'fullname': 'Ilya Dryomov ', + 'id': 7263, + 'name': 'Ilya Dryomov'}, + 'committer': {'email': 'snitzer@redhat.com', + 'fullname': 'Mike Snitzer ', + 'id': 6293, + 'name': 'Mike Snitzer'}, + 'committer_date': '2018-08-09T12:14:32-04:00', + 'date': '2018-08-09T12:38:28+02:00', + 'directory': '69002c098fbe12b487d5e4850e54e5e8e362abdd', + 'id': '5b1fe7bec8a8d0cc547a22e7ddc2bd59acd67de4', + 'merge': False, + 'parents': ['c9a5e6a968bd328753b694d19b952068c65dc5e7'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'olof@lixom.net', + 'fullname': 'Olof Johansson ', + 'id': 6861, + 'name': 'Olof Johansson'}, + 'committer': {'email': 'olof@lixom.net', + 'fullname': 'Olof Johansson ', + 'id': 6861, + 'name': 'Olof Johansson'}, + 'committer_date': '2018-09-25T13:51:16-07:00', + 'date': '2018-09-25T13:51:16-07:00', + 'directory': '14f1e20b0794fffd0fd0dd642dc6b540fb2eab65', + 'id': '1a677ff4ce6acc3bdb2d01bb25eb687a6e28aa1d', + 'merge': True, + 'parents': ['9f71e7d5d97b8ed1262b777fd5b37667aa18d3fd', + '3ab97942d0213b6583a5408630a8cbbfbf54730f'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'miguel.ojeda.sandonis@gmail.com', + 'fullname': 'Miguel Ojeda ', + 'id': 15745, + 'name': 'Miguel Ojeda'}, + 'committer': {'email': 'miguel.ojeda.sandonis@gmail.com', + 'fullname': 'Miguel Ojeda ', + 'id': 15745, + 'name': 'Miguel Ojeda'}, + 'committer_date': '2018-09-30T13:50:05+02:00', + 'date': '2018-09-30T13:50:05+02:00', + 'directory': 'f4aa8ff806adbc842974b5cf0b69e8292aa79c01', + 'id': '03d179a840ce9e694db9d69bb643fdee04cfd28f', + 'merge': False, + 'parents': ['6bf4ca7fbc85d80446ac01c0d1d77db4d91a6d84'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-09-29T14:34:06-07:00', + 'date': '2018-09-29T14:34:06-07:00', + 'directory': '85ee529dd51c22fe323533df47c39d18e0d8d205', + 'id': 'e75417739b1de4f6eb99f3f080c67bfd6812d562', + 'merge': True, + 'parents': ['e1ce697db67421cc7e9a1cc1312149f3a4e08c93', + 'bdec8d7fa55e6f5314ed72e5a0b435d90ff90548'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'robh@kernel.org', + 'fullname': 'Rob Herring ', + 'id': 20596, + 'name': 'Rob Herring'}, + 'committer': {'email': 'robh@kernel.org', + 'fullname': 'Rob Herring ', + 'id': 20596, + 'name': 'Rob Herring'}, + 'committer_date': '2018-08-31T08:30:42-04:00', + 'date': '2018-08-28T15:10:48-05:00', + 'directory': '2c421170687e8f71f93d578a6c65edf0ea80ff13', + 'id': '0413bedabc886c3a56804d1c80a58e99077b1d91', + 'merge': False, + 'parents': ['f42b0e18f2e5cf34f73ef1b6327b49040b307a33'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-09-30T06:20:33-07:00', + 'date': '2018-09-30T06:20:33-07:00', + 'directory': 'f4f44b0388f05453644ca4846a8247899a760d99', + 'id': '9a10b063758c756a4d60d63acb890c27d03c9bef', + 'merge': True, + 'parents': ['9ba6873e1603988adf97dd5abe18936a2aeff1fd', + '03d179a840ce9e694db9d69bb643fdee04cfd28f'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-09-30T07:15:35-07:00', + 'date': '2018-09-30T07:15:35-07:00', + 'directory': '8985bd35269204c126b193d721cc260d740367fd', + 'id': '17b57b1883c1285f3d0dc2266e8f79286a7bef38', + 'merge': False, + 'parents': ['9a10b063758c756a4d60d63acb890c27d03c9bef'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-09-23T19:15:18+02:00', + 'date': '2018-09-23T19:15:18+02:00', + 'directory': '5d4207e67958bdbc23288cf30178692f5534e1a0', + 'id': '6bf4ca7fbc85d80446ac01c0d1d77db4d91a6d84', + 'merge': False, + 'parents': ['d02771fb166274e75ebda8aefc86ea2816416165'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'torvalds@linux-foundation.org', + 'fullname': 'Linus Torvalds ', + 'id': 8275, + 'name': 'Linus Torvalds'}, + 'committer': {'email': 'torvalds@linux-foundation.org', + 'fullname': 'Linus Torvalds ', + 'id': 8275, + 'name': 'Linus Torvalds'}, + 'committer_date': '2018-09-02T14:37:30-07:00', + 'date': '2018-09-02T14:37:30-07:00', + 'directory': '6516fbf96fe121538247b7525866553c1eb7665a', + 'id': '57361846b52bc686112da6ca5368d11210796804', + 'merge': False, + 'parents': ['fd6868d82b824b1bea42001ecc2411f2689d3634'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-09-30T06:19:38-07:00', + 'date': '2018-09-30T06:19:38-07:00', + 'directory': 'aed33e2f126698f0b60cc69b08b5067ea3f0ce41', + 'id': '9ba6873e1603988adf97dd5abe18936a2aeff1fd', + 'merge': True, + 'parents': ['291d0e5d81e101392379217b06251fe8c27f1f80', + 'f52afc93cd018fe6910133a05d44671192d1aeb0'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'axboe@kernel.dk', + 'fullname': 'Jens Axboe ', + 'id': 9741, + 'name': 'Jens Axboe'}, + 'committer': {'email': 'axboe@kernel.dk', + 'fullname': 'Jens Axboe ', + 'id': 9741, + 'name': 'Jens Axboe'}, + 'committer_date': '2018-09-28T09:41:40-06:00', + 'date': '2018-09-28T09:41:40-06:00', + 'directory': 'f045c6b55b668dfcdf02860ea3451c5a51694e6d', + 'id': '133424a207774d3d32a38d560c6469ed31c0472f', + 'merge': True, + 'parents': ['6c7678674014b4552caf0e5aa0ca34078a377482', + 'bb830add192e9d8338082c0fc2c209e23b43d865'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer': {'email': 'gregkh@linuxfoundation.org', + 'fullname': 'Greg Kroah-Hartman ', + 'id': 7500, + 'name': 'Greg Kroah-Hartman'}, + 'committer_date': '2018-09-29T14:52:14-07:00', + 'date': '2018-09-29T14:52:14-07:00', + 'directory': '25a790625bf5a763f96956710ce8e7c9589cab88', + 'id': '291d0e5d81e101392379217b06251fe8c27f1f80', + 'merge': True, + 'parents': ['e75417739b1de4f6eb99f3f080c67bfd6812d562', + '133424a207774d3d32a38d560c6469ed31c0472f'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'jack@suse.cz', + 'fullname': 'Jan Kara ', + 'id': 7420, + 'name': 'Jan Kara'}, + 'committer': {'email': 'dan.j.williams@intel.com', + 'fullname': 'Dan Williams ', + 'id': 6332, + 'name': 'Dan Williams'}, + 'committer_date': '2018-09-27T10:56:15-07:00', + 'date': '2018-09-27T13:23:32+02:00', + 'directory': '32137752634c7f3c0f4efecfc91d64551bed047c', + 'id': 'f52afc93cd018fe6910133a05d44671192d1aeb0', + 'merge': False, + 'parents': ['6bf4ca7fbc85d80446ac01c0d1d77db4d91a6d84'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'bootc@bootc.net', + 'fullname': 'Chris Boot ', + 'id': 4976, + 'name': 'Chris Boot'}, + 'committer': {'email': 'ulf.hansson@linaro.org', + 'fullname': 'Ulf Hansson ', + 'id': 21162, + 'name': 'Ulf Hansson'}, + 'committer_date': '2018-10-09T09:23:00+02:00', + 'date': '2018-10-08T17:07:30+02:00', + 'directory': '78a25d39a5515767dea363d066aafc0ee459f1f8', + 'id': '41591b38f5f8f78344954b68582b5f00e56ffe61', + 'merge': False, + 'parents': ['0238df646e6224016a45505d2c111a24669ebe21'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'torvalds@linux-foundation.org', + 'fullname': 'Linus Torvalds ', + 'id': 8275, + 'name': 'Linus Torvalds'}, + 'committer': {'email': 'torvalds@linux-foundation.org', + 'fullname': 'Linus Torvalds ', + 'id': 8275, + 'name': 'Linus Torvalds'}, + 'committer_date': '2018-09-02T10:44:28-07:00', + 'date': '2018-09-02T10:44:28-07:00', + 'directory': '0e454bfb536028eba241bc720d2af57316e0b950', + 'id': 'a3ea9911e225337472fe7cc9278966049a1571e5', + 'merge': True, + 'parents': ['899ba79553cf1699bdcd262950b48501b0285529', + 'a72b44a871c218e2a0580e68affa1d3528c0587a'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 'torvalds@linux-foundation.org', + 'fullname': 'Linus Torvalds ', + 'id': 8275, + 'name': 'Linus Torvalds'}, + 'committer': {'email': 'torvalds@linux-foundation.org', + 'fullname': 'Linus Torvalds ', + 'id': 8275, + 'name': 'Linus Torvalds'}, + 'committer_date': '2018-09-02T10:56:01-07:00', + 'date': '2018-09-02T10:56:01-07:00', + 'directory': '887ca27479a0ee0ac0802f5708e5e16915242c61', + 'id': 'fd6868d82b824b1bea42001ecc2411f2689d3634', + 'merge': True, + 'parents': ['a3ea9911e225337472fe7cc9278966049a1571e5', + '0413bedabc886c3a56804d1c80a58e99077b1d91'], + 'synthetic': False, + 'type': 'git'}, + {'author': {'email': 's.hauer@pengutronix.de', + 'fullname': 'Sascha Hauer ', + 'id': 10052, + 'name': 'Sascha Hauer'}, + 'committer': {'email': 'shawnguo@kernel.org', + 'fullname': 'Shawn Guo ', + 'id': 494655, + 'name': 'Shawn Guo'}, + 'committer_date': '2018-09-12T14:44:33+08:00', + 'date': '2018-09-12T08:23:01+02:00', + 'directory': '6955fbcbea81c598dd9734e8fc78462bfdd423cc', + 'id': 'eea96566c189c77e5272585984eb2729881a2f1d', + 'merge': False, + 'parents': ['57361846b52bc686112da6ca5368d11210796804'], + 'synthetic': False, + 'type': 'git'}] + +test_revisions = {rev['id']: rev for rev in _test_revisions} \ No newline at end of file diff --git a/swh/web/tests/algos/test_revisions_walker.py b/swh/web/tests/algos/test_revisions_walker.py new file mode 100644 --- /dev/null +++ b/swh/web/tests/algos/test_revisions_walker.py @@ -0,0 +1,100 @@ +# Copyright (C) 2018 The Software Heritage developers +# See the AUTHORS file at the top-level directory of this distribution +# License: GNU Affero General Public License version 3, or any later version +# See top-level LICENSE file for more information + +import heapq + +from collections import deque +from unittest.mock import patch + +from swh.web.algos.revisions_walker import get_revisions_walker +from swh.web.common.utils import parse_timestamp +from swh.web.tests.testcase import SWHWebTestCase + +from .data.revisions_walker_data import test_revisions + + +def _get_rev(rev_id): + return test_revisions[rev_id] + + +def _get_rev_log(rev_id, limit=1): + return [_get_rev(rev_id)] + + +def _get_timestamp(rev): + return parse_timestamp(rev['committer_date']).timestamp() + + +_rev_start = 'bab5c80b211035739997ebd361a679fa85b39465' +_max_revs = 20 + + +@patch('swh.web.algos.revisions_walker.service') +class RevisionsWalkerTest(SWHWebTestCase): + + def test_revisions_walker_committer_date(self, mock_service): + mock_service.lookup_revision_log.side_effect = _get_rev_log + revs_walker = get_revisions_walker('committer_date', _rev_start, + max_revs=_max_revs) + + revs_queue = [] + revs_committer_date = [] + rev_start = _get_rev(_rev_start) + commit_time = _get_timestamp(rev_start) + heapq.heappush(revs_queue, (-commit_time, _rev_start)) + while len(revs_committer_date) < _max_revs: + _, rev_id = heapq.heappop(revs_queue) + rev = _get_rev(rev_id) + revs_committer_date.append(rev) + for p_rev_id in rev['parents']: + commit_time = _get_timestamp(_get_rev(p_rev_id)) + heapq.heappush(revs_queue, (-commit_time, p_rev_id)) + + self.assertEqual(list(revs_walker), revs_committer_date[0:_max_revs]) + + def test_revisions_walker_bfs(self, mock_service): + mock_service.lookup_revision_log.side_effect = _get_rev_log + revs_walker = get_revisions_walker('bfs', _rev_start, + max_revs=_max_revs) + + revs_queue = deque([_get_rev(_rev_start)]) + revs_bfs = [] + while len(revs_bfs) < _max_revs: + rev = revs_queue.popleft() + revs_bfs.append(rev) + for p_rev_id in rev['parents']: + revs_queue.append(_get_rev(p_rev_id)) + + self.assertEqual(list(revs_walker), revs_bfs[0:_max_revs]) + + def test_revisions_walker_dfs(self, mock_service): + mock_service.lookup_revision_log.side_effect = _get_rev_log + revs_walker = get_revisions_walker('dfs', _rev_start, + max_revs=_max_revs) + + revs_stack = [_get_rev(_rev_start)] + revs_dfs = [] + while len(revs_dfs) < _max_revs: + rev = revs_stack.pop() + revs_dfs.append(rev) + for p_rev_id in reversed(rev['parents']): + revs_stack.append(_get_rev(p_rev_id)) + + self.assertEqual(list(revs_walker), revs_dfs[0:_max_revs]) + + def test_revisions_walker_dfs_post(self, mock_service): + mock_service.lookup_revision_log.side_effect = _get_rev_log + revs_walker = get_revisions_walker('dfs_post', _rev_start, + max_revs=_max_revs) + + revs_stack = [_get_rev(_rev_start)] + revs_dfs_post = [] + while len(revs_dfs_post) < _max_revs: + rev = revs_stack.pop() + revs_dfs_post.append(rev) + for p_rev_id in rev['parents']: + revs_stack.append(_get_rev(p_rev_id)) + + self.assertEqual(list(revs_walker), revs_dfs_post[0:_max_revs]) diff --git a/swh/web/tests/browse/test_utils.py b/swh/web/tests/browse/test_utils.py --- a/swh/web/tests/browse/test_utils.py +++ b/swh/web/tests/browse/test_utils.py @@ -242,177 +242,3 @@ '%s' % (revision_url, revision_id)) self.assertEqual(utils.gen_revision_link(revision_id, shorten_id=True), '%s' % (revision_url, revision_id[:7])) - - def test_prepare_revision_log_for_display_no_contex(self): - per_page = 10 - first_page_logs_data = revision_history_log_test[:per_page+1] - second_page_logs_data = revision_history_log_test[per_page:2*per_page+1] - third_page_logs_data = revision_history_log_test[2*per_page:3*per_page+1] - last_page_logs_data = revision_history_log_test[3*per_page:3*per_page+5] - - revision_log_display_data = utils.prepare_revision_log_for_display( - first_page_logs_data, per_page, None) - - self.assertEqual(revision_log_display_data['revision_log_data'], - utils._format_log_entries(first_page_logs_data, - per_page)) - - self.assertEqual(revision_log_display_data['prev_rev'], - first_page_logs_data[-1]['id']) - - self.assertEqual(revision_log_display_data['prev_revs_breadcrumb'], - first_page_logs_data[0]['id']) - - self.assertEqual(revision_log_display_data['next_rev'], None) - self.assertEqual(revision_log_display_data['next_revs_breadcrumb'], - None) - - old_prev_revs_bc = str(revision_log_display_data['prev_revs_breadcrumb']) - - revision_log_display_data = utils.prepare_revision_log_for_display( - second_page_logs_data, per_page, old_prev_revs_bc) - - self.assertEqual(revision_log_display_data['revision_log_data'], - utils._format_log_entries(second_page_logs_data, - per_page)) - - self.assertEqual(revision_log_display_data['prev_rev'], - second_page_logs_data[-1]['id']) - - self.assertEqual(revision_log_display_data['prev_revs_breadcrumb'], - old_prev_revs_bc + '/' + second_page_logs_data[0]['id']) - - self.assertEqual(revision_log_display_data['next_rev'], - old_prev_revs_bc) - self.assertEqual(revision_log_display_data['next_revs_breadcrumb'], - None) - - old_prev_revs_bc = str(revision_log_display_data['prev_revs_breadcrumb']) - - revision_log_display_data = utils.prepare_revision_log_for_display( - third_page_logs_data, per_page, old_prev_revs_bc) - - self.assertEqual(revision_log_display_data['revision_log_data'], - utils._format_log_entries(third_page_logs_data, per_page)) - - self.assertEqual(revision_log_display_data['prev_rev'], - third_page_logs_data[-1]['id']) - - self.assertEqual(revision_log_display_data['prev_revs_breadcrumb'], - old_prev_revs_bc + '/' + third_page_logs_data[0]['id']) - - self.assertEqual(revision_log_display_data['next_rev'], - old_prev_revs_bc.split('/')[-1]) - - self.assertEqual(revision_log_display_data['next_revs_breadcrumb'], - '/'.join(old_prev_revs_bc.split('/')[:-1])) - - old_prev_revs_bc = str(revision_log_display_data['prev_revs_breadcrumb']) - - revision_log_display_data = utils.prepare_revision_log_for_display( - last_page_logs_data, per_page, old_prev_revs_bc) - - self.assertEqual(revision_log_display_data['revision_log_data'], - utils._format_log_entries(last_page_logs_data, per_page)) - - self.assertEqual(revision_log_display_data['prev_rev'], - None) - - self.assertEqual(revision_log_display_data['prev_revs_breadcrumb'], - None) - - self.assertEqual(revision_log_display_data['next_rev'], old_prev_revs_bc.split('/')[-1]) - self.assertEqual(revision_log_display_data['next_revs_breadcrumb'], - '/'.join(old_prev_revs_bc.split('/')[:-1])) - - def test_prepare_revision_log_for_display_snapshot_context(self): - per_page = 10 - first_page_logs_data = revision_history_log_test[:per_page+1] - second_page_logs_data = revision_history_log_test[per_page:2*per_page+1] - third_page_logs_data = revision_history_log_test[2*per_page:3*per_page+1] - last_page_logs_data = revision_history_log_test[3*per_page:3*per_page+5] - - snapshot_context = { - 'origin_info': {'type': 'git', - 'url': 'https://github.com/git/git'}, - 'origin_type': 'git', - 'url_args': {}, - 'query_params': {} - } - - revision_log_display_data = utils.prepare_revision_log_for_display( - first_page_logs_data, per_page, None, snapshot_context=snapshot_context) - - self.assertEqual(revision_log_display_data['revision_log_data'], - utils._format_log_entries(first_page_logs_data, - per_page, snapshot_context=snapshot_context)) - - self.assertEqual(revision_log_display_data['prev_rev'], - first_page_logs_data[-1]['id']) - - self.assertEqual(revision_log_display_data['prev_revs_breadcrumb'], - first_page_logs_data[-1]['id']) - - self.assertEqual(revision_log_display_data['next_rev'], None) - self.assertEqual(revision_log_display_data['next_revs_breadcrumb'], - None) - - old_prev_revs_bc = str(revision_log_display_data['prev_revs_breadcrumb']) - - revision_log_display_data = utils.prepare_revision_log_for_display( - second_page_logs_data, per_page, old_prev_revs_bc, snapshot_context=snapshot_context) - - self.assertEqual(revision_log_display_data['revision_log_data'], - utils._format_log_entries(second_page_logs_data, - per_page, snapshot_context=snapshot_context)) - - self.assertEqual(revision_log_display_data['prev_rev'], - second_page_logs_data[-1]['id']) - - self.assertEqual(revision_log_display_data['prev_revs_breadcrumb'], - old_prev_revs_bc + '/' + second_page_logs_data[-1]['id']) - - self.assertEqual(revision_log_display_data['next_rev'], - old_prev_revs_bc) - self.assertEqual(revision_log_display_data['next_revs_breadcrumb'], - None) - - old_prev_revs_bc = str(revision_log_display_data['prev_revs_breadcrumb']) - - revision_log_display_data = utils.prepare_revision_log_for_display( - third_page_logs_data, per_page, old_prev_revs_bc, snapshot_context=snapshot_context) - - self.assertEqual(revision_log_display_data['revision_log_data'], - utils._format_log_entries(third_page_logs_data, per_page, - snapshot_context=snapshot_context)) - - self.assertEqual(revision_log_display_data['prev_rev'], - third_page_logs_data[-1]['id']) - - self.assertEqual(revision_log_display_data['prev_revs_breadcrumb'], - old_prev_revs_bc + '/' + third_page_logs_data[-1]['id']) - - self.assertEqual(revision_log_display_data['next_rev'], - old_prev_revs_bc.split('/')[-1]) - - self.assertEqual(revision_log_display_data['next_revs_breadcrumb'], - '/'.join(old_prev_revs_bc.split('/')[:-1])) - - old_prev_revs_bc = str(revision_log_display_data['prev_revs_breadcrumb']) - - revision_log_display_data = utils.prepare_revision_log_for_display( - last_page_logs_data, per_page, old_prev_revs_bc, snapshot_context=snapshot_context) - - self.assertEqual(revision_log_display_data['revision_log_data'], - utils._format_log_entries(last_page_logs_data, per_page, - snapshot_context=snapshot_context)) - - self.assertEqual(revision_log_display_data['prev_rev'], - None) - - self.assertEqual(revision_log_display_data['prev_revs_breadcrumb'], - None) - - self.assertEqual(revision_log_display_data['next_rev'], old_prev_revs_bc.split('/')[-1]) - self.assertEqual(revision_log_display_data['next_revs_breadcrumb'], - '/'.join(old_prev_revs_bc.split('/')[:-1])) diff --git a/swh/web/tests/browse/views/data/revision_test_data.py b/swh/web/tests/browse/views/data/revision_test_data.py --- a/swh/web/tests/browse/views/data/revision_test_data.py +++ b/swh/web/tests/browse/views/data/revision_test_data.py @@ -855,5 +855,51 @@ 'metadata': {}, 'parents': ['8aa8a7b63fed3c7909a6d4f15159c036a0561d64'], 'synthetic': False, - 'type': 'git'} + 'type': 'git'}, + {'author': { + 'email': 'tobias.koppers@googlemail.com', + 'fullname': 'Tobias Koppers ', + 'id': 141959, + 'name': 'Tobias Koppers' + }, + 'committer': { + 'email': 'noreply@github.com', + 'fullname': 'GitHub ', + 'id': 10932771, + 'name': 'GitHub' + }, + 'committer_date': '2017-04-21T19:00:50+02:00', + 'committer_url': '/api/1/person/10932771/', + 'date': '2017-04-21T19:00:50+02:00', + 'directory': '52cf6f28b1dbfe98f485ea78ae03942f55cd8fa0', + 'id': 'd7f30392ddda27613e0ea05cb60ec985b4f75e5c', + 'merge': True, + 'message': 'Merge pull request #4729 from simon04/provide-plugin-es2015\n\nProvidePlugin: add test case for ES2015 modules', + 'metadata': {}, + 'parents': ['88f37348e7de240d794713c0c38170f17a0a8c0e', + 'd0bbf967fb51c031e16c5dfe040afce9a4113b5b'], + 'synthetic': False, + 'type': 'git'}, + {'author': { + 'email': 'kevin@fossa.io', + 'fullname': 'Kevin Wang ', + 'id': 14855611, + 'name': 'Kevin Wang' + }, + 'committer': { + 'email': 'noreply@github.com', + 'fullname': 'GitHub ', + 'id': 10932771, + 'name': 'GitHub' + }, + 'committer_date': '2017-04-21T18:28:41-07:00', + 'date': '2017-04-21T18:28:41-07:00', + 'directory': '412c6f5c00ce7d02efcdd6f23160a36eb4e5a1f4', + 'id': 'ff211108d888908d41470cea6187133ccdb56e87', + 'merge': False, + 'message': 'Add license scan report and status', + 'metadata': {}, + 'parents': ['d7f30392ddda27613e0ea05cb60ec985b4f75e5c'], + 'synthetic': False, + 'type': 'git'}, ] \ No newline at end of file diff --git a/swh/web/tests/browse/views/test_revision.py b/swh/web/tests/browse/views/test_revision.py --- a/swh/web/tests/browse/views/test_revision.py +++ b/swh/web/tests/browse/views/test_revision.py @@ -10,7 +10,8 @@ from swh.web.common.exc import NotFoundExc from swh.web.common.utils import ( - reverse, format_utc_iso_date, get_swh_persistent_id + reverse, format_utc_iso_date, get_swh_persistent_id, + parse_timestamp ) from swh.web.tests.testcase import SWHWebTestCase @@ -132,12 +133,16 @@ self.assertContains(resp, swh_dir_id) self.assertContains(resp, swh_dir_id_url) - @patch('swh.web.browse.views.revision.service') + @patch('swh.web.algos.revisions_walker.service') def test_revision_log_browse(self, mock_service): per_page = 10 - mock_service.lookup_revision_log.return_value = \ - revision_history_log_test[:per_page+1] + revision_history_log_test_sorted = \ + sorted(revision_history_log_test, + key=lambda rev: -parse_timestamp(rev['committer_date']).timestamp()) + + mock_service.lookup_revision_log.return_value = revision_history_log_test + url = reverse('browse-revision-log', kwargs={'sha1_git': revision_id_test}, @@ -145,10 +150,9 @@ resp = self.client.get(url) - prev_rev = revision_history_log_test[per_page]['id'] next_page_url = reverse('browse-revision-log', - kwargs={'sha1_git': prev_rev}, - query_params={'revs_breadcrumb': revision_id_test, + kwargs={'sha1_git': revision_id_test}, + query_params={'offset': per_page, 'per_page': per_page}) self.assertEqual(resp.status_code, 200) @@ -159,31 +163,24 @@ self.assertContains(resp, '
  • Older
  • ' % escape(next_page_url)) - for log in revision_history_log_test[:per_page]: + for log in revision_history_log_test_sorted[:per_page]: author_url = reverse('browse-person', kwargs={'person_id': log['author']['id']}) revision_url = reverse('browse-revision', kwargs={'sha1_git': log['id']}) - directory_url = reverse('browse-directory', - kwargs={'sha1_git': log['directory']}) self.assertContains(resp, '%s' % (author_url, log['author']['name'])) self.assertContains(resp, '%s' % (revision_url, log['id'][:7])) - self.assertContains(resp, directory_url) - - mock_service.lookup_revision_log.return_value = \ - revision_history_log_test[per_page:2*per_page+1] resp = self.client.get(next_page_url) - prev_prev_rev = revision_history_log_test[2*per_page]['id'] prev_page_url = reverse('browse-revision-log', kwargs={'sha1_git': revision_id_test}, query_params={'per_page': per_page}) next_page_url = reverse('browse-revision-log', - kwargs={'sha1_git': prev_prev_rev}, - query_params={'revs_breadcrumb': revision_id_test + '/' + prev_rev, + kwargs={'sha1_git': revision_id_test}, + query_params={'offset': 2 * per_page, 'per_page': per_page}) self.assertEqual(resp.status_code, 200) @@ -195,19 +192,15 @@ self.assertContains(resp, '
  • Older
  • ' % escape(next_page_url)) - mock_service.lookup_revision_log.return_value = \ - revision_history_log_test[2*per_page:3*per_page+1] - resp = self.client.get(next_page_url) - prev_prev_prev_rev = revision_history_log_test[3*per_page]['id'] prev_page_url = reverse('browse-revision-log', - kwargs={'sha1_git': prev_rev}, - query_params={'revs_breadcrumb': revision_id_test, + kwargs={'sha1_git': revision_id_test}, + query_params={'offset': per_page, 'per_page': per_page}) next_page_url = reverse('browse-revision-log', - kwargs={'sha1_git': prev_prev_prev_rev}, - query_params={'revs_breadcrumb': revision_id_test + '/' + prev_rev + '/' + prev_prev_rev, + kwargs={'sha1_git': revision_id_test}, + query_params={'offset': 3 * per_page, 'per_page': per_page}) self.assertEqual(resp.status_code, 200) @@ -219,27 +212,10 @@ self.assertContains(resp, '
  • Older
  • ' % escape(next_page_url)) - mock_service.lookup_revision_log.return_value = \ - revision_history_log_test[3*per_page:3*per_page+per_page//2] - - resp = self.client.get(next_page_url) - - prev_page_url = reverse('browse-revision-log', - kwargs={'sha1_git': prev_prev_rev}, - query_params={'revs_breadcrumb': revision_id_test + '/' + prev_rev, - 'per_page': per_page}) - - self.assertEqual(resp.status_code, 200) - self.assertTemplateUsed('browse/revision-log.html') - self.assertContains(resp, '', - count=per_page//2) - self.assertContains(resp, '
  • Older
  • ') - self.assertContains(resp, '
  • Newer
  • ' % - escape(prev_page_url)) - @patch('swh.web.browse.utils.service') + @patch('swh.web.algos.revisions_walker.service') @patch('swh.web.browse.views.revision.service') - def test_revision_request_errors(self, mock_service, mock_utils_service): + def test_revision_request_errors(self, mock_service, mock_rev_walk_service, mock_utils_service): mock_service.lookup_revision.side_effect = \ NotFoundExc('Revision not found') url = reverse('browse-revision', @@ -249,7 +225,7 @@ self.assertTemplateUsed('error.html') self.assertContains(resp, 'Revision not found', status_code=404) - mock_service.lookup_revision_log.side_effect = \ + mock_rev_walk_service.lookup_revision_log.side_effect = \ NotFoundExc('Revision not found') url = reverse('browse-revision-log', kwargs={'sha1_git': revision_id_test})