diff --git a/swh/web/ui/service.py b/swh/web/ui/service.py --- a/swh/web/ui/service.py +++ b/swh/web/ui/service.py @@ -253,9 +253,9 @@ 'Only sha1_git is supported.') return sha1_git_bin - sha1_bin_list = map(to_sha1_bin, sha1_git_list) + sha1_bin_list = (to_sha1_bin(x) for x in sha1_git_list) revisions = backend.revision_get_multiple(sha1_bin_list) - return map(converters.from_revision, revisions) + return (converters.from_revision(x) for x in revisions) def lookup_revision_message(rev_sha1_git): diff --git a/swh/web/ui/templates/revision-log.html b/swh/web/ui/templates/revision-log.html --- a/swh/web/ui/templates/revision-log.html +++ b/swh/web/ui/templates/revision-log.html @@ -39,6 +39,13 @@ {% endif %} + {% if revision['history_context_url'] is not none %} +
+
Contextual Revision Log
+

{{ revision['history_context_url'] }}

+
+ {% endif %} + {% if revision['directory_url'] is not none %}
directory
diff --git a/swh/web/ui/templates/revision.html b/swh/web/ui/templates/revision.html --- a/swh/web/ui/templates/revision.html +++ b/swh/web/ui/templates/revision.html @@ -22,6 +22,13 @@
{% endif %} + {% if revision['history_context_url'] is not none %} +
+
Contextual Revision Log
+

{{ revision['history_context_url'] }}

+
+ {% endif %} + {% if revision['directory_url'] is not none %}
directory
diff --git a/swh/web/ui/tests/test_utils.py b/swh/web/ui/tests/test_utils.py --- a/swh/web/ui/tests/test_utils.py +++ b/swh/web/ui/tests/test_utils.py @@ -476,6 +476,19 @@ call('api_entity_by_uuid', uuid='uuid-parent')]) + @nottest + def _url_for_context_test(self, fn, **data): + if fn == 'api_revision': + if 'context' in data and data['context'] is not None: + return '/api/revision/%s/prev/%s/' % (data['sha1_git'], data['context']) # noqa + else: + return '/api/revision/%s/' % data['sha1_git'] + elif fn == 'api_revision_log': + if 'prev_sha1s' in data: + return '/api/revision/%s/prev/%s/log/' % (data['sha1_git'], data['prev_sha1s']) # noqa + else: + return '/api/revision/%s/log/' % data['sha1_git'] + @patch('swh.web.ui.utils.flask') @istest def enrich_revision_without_children_or_parent(self, mock_flask): @@ -500,8 +513,7 @@ 'committer': {'id': '2'}, }) - # then - self.assertEqual(actual_revision, { + expected_revision = { 'id': 'rev-id', 'directory': '123', 'url': '/api/revision/rev-id/', @@ -511,70 +523,205 @@ 'author_url': '/api/person/1/', 'committer': {'id': '2'}, 'committer_url': '/api/person/2/' - }) + } + + # then + self.assertEqual(actual_revision, expected_revision) - mock_flask.url_for.assert_has_calls([call('api_revision', - sha1_git='rev-id'), - call('api_revision_log', - sha1_git='rev-id'), - call('api_person', - person_id='1'), - call('api_person', - person_id='2'), - call('api_directory', - sha1_git='123')]) + mock_flask.url_for.assert_has_calls( + [call('api_revision', + sha1_git='rev-id'), + call('api_revision_log', + sha1_git='rev-id'), + call('api_person', + person_id='1'), + call('api_person', + person_id='2'), + call('api_directory', + sha1_git='123')]) @patch('swh.web.ui.utils.flask') @istest def enrich_revision_with_children_and_parent_no_dir(self, mock_flask): # given - def url_for_test(fn, **data): - if fn == 'api_revision': - return '/api/revision/' + data['sha1_git'] + '/' - elif fn == 'api_revision_log': - return '/api/revision/' + data['sha1_git'] + '/log/' - else: - return '/api/revision/' + data['sha1_git_root'] + '/history/' + data['sha1_git'] + '/' # noqa + mock_flask.url_for.side_effect = self._url_for_context_test - mock_flask.url_for.side_effect = url_for_test + # when + actual_revision = utils.enrich_revision({ + 'id': 'rev-id', + 'parents': ['123'], + 'children': ['456'], + }, context='prev-rev') + + expected_revision = { + 'id': 'rev-id', + 'url': '/api/revision/rev-id/', + 'history_url': '/api/revision/rev-id/log/', + 'history_context_url': '/api/revision/rev-id/prev/prev-rev/log/', + 'parents': ['123'], + 'parent_urls': ['/api/revision/123/prev/prev-rev/rev-id/'], + 'children': ['456'], + 'children_urls': ['/api/revision/456/', + '/api/revision/prev-rev/'], + } + + # then + self.assertEqual(actual_revision, expected_revision) + + mock_flask.url_for.assert_has_calls( + [call('api_revision', + sha1_git='prev-rev'), + call('api_revision', + sha1_git='rev-id'), + call('api_revision_log', + sha1_git='rev-id'), + call('api_revision_log', + sha1_git='rev-id', + prev_sha1s='prev-rev'), + call('api_revision', + sha1_git='123', + context='prev-rev/rev-id'), + call('api_revision', + sha1_git='456')]) + + @patch('swh.web.ui.utils.flask') + @istest + def enrich_revision_no_context(self, mock_flask): + # given + mock_flask.url_for.side_effect = self._url_for_context_test # when actual_revision = utils.enrich_revision({ 'id': 'rev-id', 'parents': ['123'], 'children': ['456'], - }, context='sha1_git_root') + }) + + expected_revision = { + 'id': 'rev-id', + 'url': '/api/revision/rev-id/', + 'history_url': '/api/revision/rev-id/log/', + 'parents': ['123'], + 'parent_urls': ['/api/revision/123/prev/rev-id/'], + 'children': ['456'], + 'children_urls': ['/api/revision/456/'] + } + + # then + self.assertEqual(actual_revision, expected_revision) + + mock_flask.url_for.assert_has_calls( + [call('api_revision', + sha1_git='rev-id'), + call('api_revision_log', + sha1_git='rev-id'), + call('api_revision', + sha1_git='123', + context='rev-id'), + call('api_revision', + sha1_git='456')]) + + @patch('swh.web.ui.utils.flask') + @istest + def enrich_revision_context_empty_prev_list(self, mock_flask): + # given + mock_flask.url_for.side_effect = self._url_for_context_test + + # when + expected_revision = { + 'id': 'rev-id', + 'url': '/api/revision/rev-id/', + 'history_url': '/api/revision/rev-id/log/', + 'history_context_url': ('/api/revision/rev-id/' + 'prev/prev-rev/log/'), + 'parents': ['123'], + 'parent_urls': ['/api/revision/123/prev/prev-rev/rev-id/'], + 'children': ['456'], + 'children_urls': ['/api/revision/456/', '/api/revision/prev-rev/'], + } + + actual_revision = utils.enrich_revision({ + 'id': 'rev-id', + 'url': '/api/revision/rev-id/', + 'parents': ['123'], + 'children': ['456']}, context='prev-rev') # then - self.assertEqual(actual_revision, { + self.assertEqual(actual_revision, expected_revision) + mock_flask.url_for.assert_has_calls( + [call('api_revision', + sha1_git='prev-rev'), + call('api_revision', + sha1_git='rev-id'), + call('api_revision_log', + sha1_git='rev-id'), + call('api_revision_log', + sha1_git='rev-id', + prev_sha1s='prev-rev'), + call('api_revision', + sha1_git='123', + context='prev-rev/rev-id'), + call('api_revision', + sha1_git='456')]) + + @patch('swh.web.ui.utils.flask') + @istest + def enrich_revision_context_some_prev_list(self, mock_flask): + # given + mock_flask.url_for.side_effect = self._url_for_context_test + + # when + expected_revision = { 'id': 'rev-id', 'url': '/api/revision/rev-id/', 'history_url': '/api/revision/rev-id/log/', + 'history_context_url': ('/api/revision/rev-id/' + 'prev/prev1-rev/prev0-rev/log/'), 'parents': ['123'], - 'parent_urls': ['/api/revision/sha1_git_root/history/123/'], + 'parent_urls': ['/api/revision/123/prev/' + 'prev1-rev/prev0-rev/rev-id/'], 'children': ['456'], - 'children_urls': ['/api/revision/sha1_git_root/history/456/'], - }) + 'children_urls': ['/api/revision/456/', + '/api/revision/prev0-rev/prev/prev1-rev/'], + } + + actual_revision = utils.enrich_revision({ + 'id': 'rev-id', + 'parents': ['123'], + 'children': ['456']}, context='prev1-rev/prev0-rev') + # then + self.assertEqual(actual_revision, expected_revision) mock_flask.url_for.assert_has_calls( [call('api_revision', + sha1_git='prev0-rev', + context='prev1-rev'), + call('api_revision', sha1_git='rev-id'), call('api_revision_log', sha1_git='rev-id'), - call('api_revision_history', - sha1_git_root='sha1_git_root', - sha1_git='123'), - call('api_revision_history', - sha1_git_root='sha1_git_root', + call('api_revision_log', + sha1_git='rev-id', + prev_sha1s='prev1-rev/prev0-rev'), + call('api_revision', + sha1_git='123', + context='prev1-rev/prev0-rev/rev-id'), + call('api_revision', sha1_git='456')]) @nottest def _url_for_rev_message_test(self, fn, **data): if fn == 'api_revision': - return '/api/revision/' + data['sha1_git'] + '/' + if 'context' in data and data['context'] is not None: + return '/api/revision/%s/prev/%s/' % (data['sha1_git'], data['context']) # noqa + else: + return '/api/revision/%s/' % data['sha1_git'] elif fn == 'api_revision_log': - return '/api/revision/' + data['sha1_git'] + '/log/' + if 'prev_sha1s' in data and data['prev_sha1s'] is not None: + return '/api/revision/%s/prev/%s/log/' % (data['sha1_git'], data['prev_sha1s']) # noqa + else: + return '/api/revision/%s/log/' % data['sha1_git'] elif fn == 'api_revision_raw_message': return '/api/revision/' + data['sha1_git'] + '/raw/' else: @@ -587,35 +734,43 @@ mock_flask.url_for.side_effect = self._url_for_rev_message_test # when - actual_revision = utils.enrich_revision({ + expected_revision = { 'id': 'rev-id', + 'url': '/api/revision/rev-id/', + 'history_url': '/api/revision/rev-id/log/', + 'history_context_url': ('/api/revision/rev-id/' + 'prev/prev-rev/log/'), 'message': None, 'parents': ['123'], + 'parent_urls': ['/api/revision/123/prev/prev-rev/rev-id/'], 'children': ['456'], - }, context='sha1_git_root') + 'children_urls': ['/api/revision/456/', '/api/revision/prev-rev/'], + } - # then - self.assertEqual(actual_revision, { + actual_revision = utils.enrich_revision({ 'id': 'rev-id', - 'url': '/api/revision/rev-id/', 'message': None, - 'history_url': '/api/revision/rev-id/log/', 'parents': ['123'], - 'parent_urls': ['/api/revision/sha1_git_root/history/123/'], 'children': ['456'], - 'children_urls': ['/api/revision/sha1_git_root/history/456/'], - }) + }, context='prev-rev') + + # then + self.assertEqual(actual_revision, expected_revision) mock_flask.url_for.assert_has_calls( [call('api_revision', + sha1_git='prev-rev'), + call('api_revision', sha1_git='rev-id'), call('api_revision_log', sha1_git='rev-id'), - call('api_revision_history', - sha1_git_root='sha1_git_root', - sha1_git='123'), - call('api_revision_history', - sha1_git_root='sha1_git_root', + call('api_revision_log', + sha1_git='rev-id', + prev_sha1s='prev-rev'), + call('api_revision', + sha1_git='123', + context='prev-rev/rev-id'), + call('api_revision', sha1_git='456')]) @patch('swh.web.ui.utils.flask') @@ -631,30 +786,38 @@ 'message_decoding_failed': True, 'parents': ['123'], 'children': ['456'], - }, context='sha1_git_root') + }, context='prev-rev') - # then - self.assertEqual(actual_revision, { + expected_revision = { 'id': 'rev-id', 'url': '/api/revision/rev-id/', + 'history_url': '/api/revision/rev-id/log/', + 'history_context_url': ('/api/revision/rev-id/' + 'prev/prev-rev/log/'), 'message': None, 'message_decoding_failed': True, 'message_url': '/api/revision/rev-id/raw/', - 'history_url': '/api/revision/rev-id/log/', 'parents': ['123'], - 'parent_urls': ['/api/revision/sha1_git_root/history/123/'], + 'parent_urls': ['/api/revision/123/prev/prev-rev/rev-id/'], 'children': ['456'], - 'children_urls': ['/api/revision/sha1_git_root/history/456/'], - }) + 'children_urls': ['/api/revision/456/', '/api/revision/prev-rev/'], + } + + # then + self.assertEqual(actual_revision, expected_revision) mock_flask.url_for.assert_has_calls( [call('api_revision', + sha1_git='prev-rev'), + call('api_revision', sha1_git='rev-id'), call('api_revision_log', sha1_git='rev-id'), - call('api_revision_history', - sha1_git_root='sha1_git_root', - sha1_git='123'), - call('api_revision_history', - sha1_git_root='sha1_git_root', + call('api_revision_log', + sha1_git='rev-id', + prev_sha1s='prev-rev'), + call('api_revision', + sha1_git='123', + context='prev-rev/rev-id'), + call('api_revision', sha1_git='456')]) diff --git a/swh/web/ui/tests/views/test_api.py b/swh/web/ui/tests/views/test_api.py --- a/swh/web/ui/tests/views/test_api.py +++ b/swh/web/ui/tests/views/test_api.py @@ -679,8 +679,8 @@ '8734ef7e7c357ce2af928115c6c6a42b7e2a44e7' ], 'parent_urls': [ - '/api/1/revision/18d8be353ed3480476f032475e7c233eff7371d5' - '/history/8734ef7e7c357ce2af928115c6c6a42b7e2a44e7/' + '/api/1/revision/8734ef7e7c357ce2af928115c6c6a42b7e2a44e7' + '/prev/18d8be353ed3480476f032475e7c233eff7371d5/' ], 'type': 'tar', 'synthetic': True, @@ -1416,8 +1416,8 @@ '7834ef7e7c357ce2af928115c6c6a42b7e2a4345' ], 'parent_urls': [ - '/api/1/revision/18d8be353ed3480476f032475e7c233eff7371d5' - '/history/7834ef7e7c357ce2af928115c6c6a42b7e2a4345/' + '/api/1/revision/7834ef7e7c357ce2af928115c6c6a42b7e2a4345' + '/prev/18d8be353ed3480476f032475e7c233eff7371d5/' ], 'type': 'tar', 'synthetic': True, @@ -1521,8 +1521,8 @@ 'committer_date_offset': 0, 'parents': ['adc83b19e793491b1c6ea0fd8b46cd9f32e592fc'], 'parent_urls': [ - '/api/1/revision/7834ef7e7c357ce2af928115c6c6a42b7e2a44e6' - '/history/adc83b19e793491b1c6ea0fd8b46cd9f32e592fc/' + '/api/1/revision/adc83b19e793491b1c6ea0fd8b46cd9f32e592fc' + '/prev/7834ef7e7c357ce2af928115c6c6a42b7e2a44e6/' ], 'type': 'tar', 'synthetic': True, @@ -1545,8 +1545,8 @@ 'committer_date_offset': 0, 'parents': ['7834ef7e7c357ce2af928115c6c6a42b7e2a4345'], 'parent_urls': [ - '/api/1/revision/18d8be353ed3480476f032475e7c233eff7371d5' - '/history/7834ef7e7c357ce2af928115c6c6a42b7e2a4345/' + '/api/1/revision/7834ef7e7c357ce2af928115c6c6a42b7e2a4345' + '/prev/18d8be353ed3480476f032475e7c233eff7371d5/' ], 'type': 'tar', 'synthetic': True, @@ -1596,8 +1596,8 @@ '7834ef7e7c357ce2af928115c6c6a42b7e2a4345' ], 'parent_urls': [ - '/api/1/revision/18d8be353ed3480476f032475e7c233eff7371d5' - '/history/7834ef7e7c357ce2af928115c6c6a42b7e2a4345/' + '/api/1/revision/7834ef7e7c357ce2af928115c6c6a42b7e2a4345' + '/prev/18d8be353ed3480476f032475e7c233eff7371d5/' ], 'type': 'tar', 'synthetic': True, @@ -1680,10 +1680,10 @@ 'directory': '272' } - mock_service.lookup_revision_with_context.return_value = stub_revision + mock_service.lookup_revision.return_value = stub_revision # then - rv = self.app.get('/api/1/revision/666/history/883/') + rv = self.app.get('/api/1/revision/883/prev/999/') self.assertEquals(rv.status_code, 200) self.assertEquals(rv.mimetype, 'application/json') @@ -1693,17 +1693,17 @@ 'id': '883', 'url': '/api/1/revision/883/', 'history_url': '/api/1/revision/883/log/', + 'history_context_url': '/api/1/revision/883/prev/999/log/', 'children': ['777', '999'], - 'children_urls': ['/api/1/revision/666/history/777/', - '/api/1/revision/666/history/999/'], + 'children_urls': ['/api/1/revision/777/', + '/api/1/revision/999/'], 'parents': [], 'parent_urls': [], 'directory': '272', 'directory_url': '/api/1/directory/272/' }) - mock_service.lookup_revision_with_context.assert_called_once_with( - '666', '883', 100) + mock_service.lookup_revision.assert_called_once_with('883') @patch('swh.web.ui.views.api._revision_directory_by') @istest diff --git a/swh/web/ui/tests/views/test_browse.py b/swh/web/ui/tests/views/test_browse.py --- a/swh/web/ui/tests/views/test_browse.py +++ b/swh/web/ui/tests/views/test_browse.py @@ -760,7 +760,7 @@ 'Not found!') self.assertIsNone(self.get_context_variable('revision')) - mock_api.api_revision.assert_called_once_with('1') + mock_api.api_revision.assert_called_once_with('1', None) @patch('swh.web.ui.views.browse.api') @istest @@ -780,7 +780,7 @@ 'wrong input!') self.assertIsNone(self.get_context_variable('revision')) - mock_api.api_revision.assert_called_once_with('426') + mock_api.api_revision.assert_called_once_with('426', None) @patch('swh.web.ui.views.browse.api') @istest @@ -842,7 +842,7 @@ expected_revision) self.assertIsNone(self.get_context_variable('message')) - mock_api.api_revision.assert_called_once_with('426') + mock_api.api_revision.assert_called_once_with('426', None) @patch('swh.web.ui.views.browse.api') @istest @@ -875,7 +875,7 @@ 'Not found!') self.assertEqual(self.get_context_variable('revisions'), []) - mock_api.api_revision_log.assert_called_once_with('sha1') + mock_api.api_revision_log.assert_called_once_with('sha1', None) @patch('swh.web.ui.views.browse.api') @istest @@ -895,7 +895,7 @@ 'wrong input!') self.assertEqual(self.get_context_variable('revisions'), []) - mock_api.api_revision_log.assert_called_once_with('426') + mock_api.api_revision_log.assert_called_once_with('426', None) @patch('swh.web.ui.views.browse.api') @istest @@ -935,7 +935,7 @@ isinstance(self.get_context_variable('revisions'), map)) self.assertIsNone(self.get_context_variable('message')) - mock_api.api_revision_log.assert_called_once_with('426') + mock_api.api_revision_log.assert_called_once_with('426', None) @patch('swh.web.ui.views.browse.api') @istest @@ -976,7 +976,7 @@ 'wrong input!') self.assertEqual(self.get_context_variable('revisions'), []) - mock_api.api_revision_log.assert_called_once_with('abcd') + mock_api.api_revision_log.assert_called_once_with('abcd', None) @patch('swh.web.ui.views.browse.api') @istest diff --git a/swh/web/ui/utils.py b/swh/web/ui/utils.py --- a/swh/web/ui/utils.py +++ b/swh/web/ui/utils.py @@ -198,16 +198,87 @@ return entity +def _get_revision_contexts(rev_id, context): + """Helper for enrich_revision: retrieve for the revision id and potentially + the navigation breadcrumbs the context to pass to parents and children of + of the revision. + + Args: + rev_id: the revision's sha1 id + context: the current navigation context + + Returns: + The context for parents, children and the url of the direct child as a + tuple in that order. + """ + context_for_parents = None + context_for_children = None + url_direct_child = None + + if not context: + context_for_parents = rev_id + else: + context_for_parents = '%s/%s' % (context, rev_id) + prev_for_children = context.split('/')[:-1] + if len(prev_for_children) > 0: + context_for_children = '/'.join(prev_for_children) + # This commit is not the first commit in the path + if context_for_children: + url_direct_child = flask.url_for( + 'api_revision', + sha1_git=context.split('/')[-1], + context=context_for_children) + # This commit is the first commit in the path + else: + url_direct_child = flask.url_for( + 'api_revision', + sha1_git=context.split('/')[-1]) + + return (context_for_parents, context_for_children, url_direct_child) + + +def _make_child_url(rev_children, context): + """Helper for enrich_revision: retrieve the list of urls corresponding + to the children of the current revision according to the navigation + breadcrumbs. + + Args: + rev_children: a list of revision id + context: the '/'-separated navigation breadcrumbs + + Returns: + the list of the children urls according to the context + """ + children = [] + for child in rev_children: + if context and child != context.split('/')[-1]: + children.append(flask.url_for('api_revision', sha1_git=child)) + elif not context: + children.append(flask.url_for('api_revision', sha1_git=child)) + return children + + def enrich_revision(revision, context=None): """Enrich revision with links where it makes sense (directory, parents). + Keep track of the navigation breadcrumbs if they are specified. + Args: + revision: the revision as a dict + context: the navigation breadcrumbs as a /-separated string of revision + sha1_git """ - if not context: - context = revision['id'] + + ctx_parents, ctx_children, url_direct_child = _get_revision_contexts( + revision['id'], context) revision['url'] = flask.url_for('api_revision', sha1_git=revision['id']) revision['history_url'] = flask.url_for('api_revision_log', sha1_git=revision['id']) + if context: + revision['history_context_url'] = flask.url_for( + 'api_revision_log', + sha1_git=revision['id'], + prev_sha1s=context) if 'author' in revision: author = revision['author'] @@ -227,21 +298,21 @@ if 'parents' in revision: parents = [] for parent in revision['parents']: - parents.append(flask.url_for('api_revision_history', - sha1_git_root=context, - sha1_git=parent)) - + parents.append(flask.url_for('api_revision', + sha1_git=parent, + context=ctx_parents)) revision['parent_urls'] = parents if 'children' in revision: - children = [] - for child in revision['children']: - children.append(flask.url_for('api_revision_history', - sha1_git_root=context, - sha1_git=child)) - + children = _make_child_url(revision['children'], context) + if url_direct_child: + children.append(url_direct_child) revision['children_urls'] = children + else: + if url_direct_child: + revision['children_urls'] = [url_direct_child] + if 'message_decoding_failed' in revision: revision['message_url'] = flask.url_for( 'api_revision_raw_message', diff --git a/swh/web/ui/views/api.py b/swh/web/ui/views/api.py --- a/swh/web/ui/views/api.py +++ b/swh/web/ui/views/api.py @@ -507,7 +507,8 @@ @app.route('/api/1/revision/') @app.route('/api/1/revision//') -def api_revision(sha1_git): +@app.route('/api/1/revision//prev//') +def api_revision(sha1_git, context=None): """Return information about revision with id sha1_git. Args: @@ -522,14 +523,15 @@ Example: GET /api/1/revision/baf18f9fc50a0b6fef50460a76c33b2ddc57486e - """ + def _enrich_revision(revision, context=context): + return utils.enrich_revision(revision, context) + return _api_lookup( sha1_git, - lookup_fn=service.lookup_revision, - error_msg_if_not_found='Revision with sha1_git %s not' - ' found.' % sha1_git, - enrich_fn=utils.enrich_revision) + service.lookup_revision, + 'Revision with sha1_git %s not found.' % sha1_git, + _enrich_revision) @app.route('/api/1/revision//raw/') diff --git a/swh/web/ui/views/browse.py b/swh/web/ui/views/browse.py --- a/swh/web/ui/views/browse.py +++ b/swh/web/ui/views/browse.py @@ -298,17 +298,32 @@ @app.route('/browse/revision/') @app.route('/browse/revision//') +@app.route('/browse/revision//prev//') @set_renderers(HTMLRenderer) -def browse_revision(sha1_git): - """Browse revision with sha1_git. +def browse_revision(sha1_git, prev_sha1s=None): + """Browse the revision with git SHA1 sha1_git_cur, while optionally keeping + the context from which we came as a list of previous (i.e. later) + revisions' sha1s. + Args: + sha1_git: the requested revision's sha1_git. + prev_sha1s: an optional string of /-separated sha1s representing our + context, ordered by descending revision date. + + Returns: + Information about revision of git SHA1 sha1_git_cur, with relevant URLS + pointing to the context augmented with sha1_git_cur. + + Example: + GET /browse/revision/ """ + env = {'sha1_git': sha1_git, 'message': None, 'revision': None} try: - rev = api.api_revision(sha1_git) + rev = api.api_revision(sha1_git, prev_sha1s) env['revision'] = utils.prepare_data_for_view(rev) except (NotFoundExc, BadInputExc) as e: env['message'] = str(e) @@ -325,10 +340,17 @@ @app.route('/browse/revision//log/') +@app.route('/browse/revision//prev//log/') @set_renderers(HTMLRenderer) -def browse_revision_log(sha1_git): - """Browse revision with sha1_git's log. +def browse_revision_log(sha1_git, prev_sha1s=None): + """Browse revision with sha1_git's log. If the navigation path through the + commit tree is specified, we intersect the earliest revision's log with the + revisions the user browsed through - ie the path taken to the specified + revision. + Args: + sha1_git: the current revision's SHA1_git checksum + prev_sha1s: optionally, the path through which we want log information """ env = {'sha1_git': sha1_git, 'sha1_url': '/browse/revision/%s/' % sha1_git, @@ -336,7 +358,7 @@ 'revisions': []} try: - revisions = api.api_revision_log(sha1_git) + revisions = api.api_revision_log(sha1_git, prev_sha1s) env['revisions'] = map(utils.prepare_data_for_view, revisions) except (NotFoundExc, BadInputExc) as e: env['message'] = str(e) @@ -392,6 +414,38 @@ return render_template('revision-log.html', **env) +@app.route('/browse/revision//prev//') +@set_renderers(HTMLRenderer) +def browse_with_rev_context(sha1_git_cur, sha1s): + """Browse the revision with git SHA1 sha1_git_cur, while keeping the context + from which we came as a list of previous (i.e. later) revisions' sha1s. + + Args: + sha1_git_cur: the requested revision's sha1_git. + sha1s: a string of /-separated sha1s representing our context, ordered + by descending revision date. + + Returns: + Information about revision of git SHA1 sha1_git_cur, with relevant URLS + pointing to the context augmented with sha1_git_cur. + + Example: + GET /browse/revision/ + """ + env = {'sha1_git': sha1_git_cur, + 'message': None, + 'revision': None} + + try: + revision = api.api_revision( + sha1_git_cur, sha1s) + env['revision'] = utils.prepare_data_for_view(revision) + except (BadInputExc, NotFoundExc) as e: + env['message'] = str(e) + + return render_template('revision.html', **env) + + @app.route('/browse/revision//history//') @set_renderers(HTMLRenderer) def browse_revision_history(sha1_git_root, sha1_git):