MozReview: Review Page Redesign: Move commits table above the main content area (Bug 1309964). r?glob,smacleod draft
authorDavid Walsh <dwalsh@mozilla.com>
Tue, 29 Nov 2016 10:21:03 -0600
changeset 9981 7fde75b31579e5311cc291d7e1db6976c53947e6
parent 9936 d207da6d30423fb0d13db42116323911bc614c6f
child 9982 75e6bd4195a3cace92e9ef3ba542f3c2409cb9d3
push id1403
push userbmo:dwalsh@mozilla.com
push dateTue, 29 Nov 2016 16:23:11 +0000
reviewersglob, smacleod
bugs1309964
MozReview: Review Page Redesign: Move commits table above the main content area (Bug 1309964). r?glob,smacleod MozReview-Commit-ID: KM0AyJQa31D
pylib/mozreview/mozreview/extension.py
pylib/mozreview/mozreview/fields.py
pylib/mozreview/mozreview/hooks.py
pylib/mozreview/mozreview/static/mozreview/css/commits.less
pylib/mozreview/mozreview/static/mozreview/css/mozilla-theme-common.less
pylib/mozreview/mozreview/static/mozreview/css/mozilla-theme-reviews.less
pylib/mozreview/mozreview/static/mozreview/css/mozilla-theme.less
pylib/mozreview/mozreview/static/mozreview/css/review.less
pylib/mozreview/mozreview/static/mozreview/js/review.js
pylib/mozreview/mozreview/templates/mozreview/commits-requests.html
pylib/mozreview/mozreview/templates/mozreview/commits.html
--- a/pylib/mozreview/mozreview/extension.py
+++ b/pylib/mozreview/mozreview/extension.py
@@ -53,16 +53,17 @@ from mozreview.fields import (
     ImportCommitField,
     PullCommitField,
     TryField,
 )
 from mozreview.file_diff_reviewer.resources import (
     file_diff_reviewer_resource,
 )
 from mozreview.hooks import (
+    CommitContextTemplateHook,
     MozReviewApprovalHook,
 )
 from mozreview.hostingservice.bmo_bugtracker import (
     BMOBugTracker,
 )
 from mozreview.hostingservice.hmo_repository import (
     HMORepository,
 )
@@ -310,19 +311,19 @@ class MozReviewExtension(Extension):
                      'mozreview/user-data.html')
         TemplateHook(self, 'base-after-content',
                      'mozreview/repository.html')
         TemplateHook(self, 'base-after-content',
                      'mozreview/user_review_flag.html',
                      apply_to=review_request_url_names)
 
         ReviewRequestFieldsHook(self, 'main', [CommitsListField])
-        # This forces the Commits field to be the top item.
-        main_fieldset.field_classes.insert(0,
-                                           main_fieldset.field_classes.pop())
+        CommitContextTemplateHook(self, 'mozreview-pre-review-request-box',
+                                  'mozreview/commits.html',
+                                  apply_to=review_request_url_names)
 
         # The above hack forced Commits at the top, but the rest of these
         # fields are fine below the Description.
         ReviewRequestFieldsHook(self, 'main', [CombinedReviewersField])
         ReviewRequestFieldsHook(self, 'main', [TryField])
         ReviewRequestFieldsHook(self, 'main', [BaseCommitField])
         ReviewRequestFieldsHook(self, 'main', [FileDiffReviewerField])
 
--- a/pylib/mozreview/mozreview/fields.py
+++ b/pylib/mozreview/mozreview/fields.py
@@ -83,65 +83,28 @@ class CombinedReviewersField(BaseReviewR
 
 class CommitsListField(CommitDataBackedField):
     """The commits list field for review requests.
 
     This field is injected in the details of a review request that
     is a "push" based review request.
     """
     field_id = COMMITS_KEY
-    label = _("Commits")
+    label = ""
 
     can_record_change_entry = True
 
     def has_value_changed(self, old_value, new_value):
         # Just to be safe, we de-serialize the json and compare values
         if old_value is not None and new_value is not None:
             return json.loads(old_value) != json.loads(new_value)
         return old_value != new_value
 
     def should_render(self, value):
-        return is_pushed(self.review_request_details)
-
-    def as_html(self):
-        user = self.request.user
-        parent = get_parent_rr(self.review_request_details.get_review_request())
-        parent_details = parent.get_draft(user) or parent
-
-        # If a user can view the parent draft they should also have
-        # permission to view every child. We check if the child is
-        # accessible anyways in case it has been restricted for other
-        # reasons.
-        children_details = [
-            child for child in gen_child_rrs(parent_details, user=user)
-            if child.is_accessible_by(user)]
-
-        autoland_requests = AutolandRequest.objects.filter(
-            review_request_id=parent.id).order_by('-autoland_id')
-
-        repo_urls = set()
-        latest_autoland_requests = []
-
-
-        # We would like to fetch the latest AutolandRequest for each
-        # different repository.
-        for request in autoland_requests:
-            if request.repository_url in repo_urls:
-                continue
-
-            repo_urls.add(request.repository_url)
-            latest_autoland_requests.append(request)
-
-        return get_template('mozreview/commits.html').render(Context({
-            'review_request_details': self.review_request_details,
-            'parent_details': parent_details,
-            'children_details': children_details,
-            'latest_autoland_requests': latest_autoland_requests,
-            'user': user
-        }))
+        return False
 
     def render_change_entry_html(self, info):
         old_value = json.loads(info.get('old', ['[]'])[0])
         old_commits = [c for c, r in old_value]
 
         new_value = json.loads(info.get('new', ['[]'])[0])
         new_commits = [c for c, r in new_value]
 
--- a/pylib/mozreview/mozreview/hooks.py
+++ b/pylib/mozreview/mozreview/hooks.py
@@ -1,32 +1,101 @@
 from __future__ import unicode_literals
 
 import logging
 
-from reviewboard.extensions.hooks import ReviewRequestApprovalHook
+from django.template.loader import Context
+from django.utils.translation import ugettext as _
 
+from reviewboard.extensions.hooks import (
+    ReviewRequestApprovalHook,
+    ReviewRequestFieldsHook,
+    TemplateHook
+)
+
+from mozreview.autoland.models import AutolandEventLogEntry, AutolandRequest
 from mozreview.extra_data import (
     COMMIT_ID_KEY,
     fetch_commit_data,
     gen_child_rrs,
+    get_parent_rr,
     is_parent,
     is_pushed,
 )
 from mozreview.models import (
     get_profile,
 )
 from mozreview.review_helpers import (
     has_valid_shipit,
     has_l3_shipit,
 )
 
 
 logger = logging.getLogger(__name__)
 
+class CommitContextTemplateHook(TemplateHook):
+    """Gathers all information required for commits table
+
+    This hook allows us to generate a detailed, custom commits table.
+    Information provided includes the parent and child review requests,
+    as well as autoland information.
+    """
+
+    def get_extra_context(self, request, context):
+        """Fetches relevant review request information, returns context"""
+        review_request_details = context['review_request_details']
+        commit_data = fetch_commit_data(review_request_details)
+
+        user = request.user
+        parent = get_parent_rr(review_request_details.get_review_request(), commit_data=commit_data)
+        parent_details = parent.get_draft(user) or parent
+
+        # If a user can view the parent draft they should also have
+        # permission to view every child. We check if the child is
+        # accessible anyways in case it has been restricted for other
+        # reasons.
+        children_details = [
+            child for child in gen_child_rrs(parent_details, user=user)
+            if child.is_accessible_by(user)]
+        n_children = len(children_details)
+        current_child_num = prev_child = next_child = None
+
+        if not is_parent(review_request_details, commit_data=commit_data):
+            cur_index = children_details.index(review_request_details)
+            current_child_num = cur_index + 1
+            next_child = (children_details[cur_index + 1]
+                          if cur_index + 1 < n_children else None)
+            prev_child = (children_details[cur_index - 1]
+                          if cur_index - 1 >= 0 else None)
+
+        latest_autoland_requests = []
+        repo_urls = set()
+        autoland_requests = AutolandRequest.objects.filter(
+            review_request_id=parent.id).order_by('-autoland_id')
+
+        # We would like to fetch the latest AutolandRequest for each
+        # different repository.
+        for request in autoland_requests:
+            if request.repository_url in repo_urls:
+                continue
+
+            repo_urls.add(request.repository_url)
+            latest_autoland_requests.append(request)
+
+        return {
+            'review_request_details': review_request_details,
+            'parent_details': parent_details,
+            'children_details': children_details,
+            'num_children': n_children,
+            'current_child_num': current_child_num,
+            'next_child': next_child,
+            'prev_child': prev_child,
+            'latest_autoland_requests': latest_autoland_requests,
+            'user': user,
+        }
 
 class MozReviewApprovalHook(ReviewRequestApprovalHook):
     """Calculates landing approval for review requests.
 
     This hook allows us to control the `approved` and `approval_failure`
     fields on review request model instances, and Web API results
     associated with them. By calculating landing approval and returning
     it here we have a nice way to distribute this decision throughout
--- a/pylib/mozreview/mozreview/static/mozreview/css/commits.less
+++ b/pylib/mozreview/mozreview/static/mozreview/css/commits.less
@@ -14,16 +14,17 @@
 @issue-opened-link-color: darken(@issue-opened-bg, 60%);
 
 /*******************************************************/
 
 @import (reference) 'mozilla-theme-common.less';
 
 @approval-bg: #DFFFD7;
 @approval-border-color: #478A06;
+@table-large-font-size: 18px;
 
 #mozreview-request-series{
   margin-top:5px;
 }
 
 .help-icon {
   display: inline-block;
   text-align: center;
@@ -31,24 +32,34 @@
   border-radius: 50%;
   height: 15px;
   width: 15px;
   border: 1px solid #3E3E3E;
   cursor: default;
   margin-top: -1px;
 }
 
+#mozreview-review-header {
+    text-transform: uppercase;
+    color: #777;
+    display: block;
+    font-weight: normal;
+}
+
 #mozreview-child-requests {
   width: 100%;
   table-layout: fixed;
   border-spacing: 0px;
   margin-top:10px;
   min-width: 653px;
-  border-top: 1px solid #C1C1C1;
-  border-right: 1px solid #C1C1C1;
+  border-top: 1px solid #EEE;
+  border-right: 1px solid #EEE;
+  margin-bottom: 20px;
+  border: 1px solid #CCC;
+  font-size: 110%;
 
   tr, td {
     text-align: left;
   }
 
   .truncate_text {
     white-space: nowrap;
     overflow: hidden;
@@ -57,17 +68,17 @@
   }
 
   .commits {
     width: 60%;
 
     ul {
       font-size: 90%;
       list-style-type: none;
-      margin: 0;
+      margin: 0 0 0 44px;
       padding: 6px 0;
 
       li {
         display: inline-block;
         padding-right: 10px;
         margin-right: 10px;
       }
 
@@ -108,56 +119,70 @@
     &.review-cleared{
       color: grey;
     }
     &.review-cleared::after {
       content: " (r? cleared)";
     }
   }
 
-  .status {
-    width: 90px;
-    white-space: nowrap;
+  tbody > tr, tbody > tr:hover {
+    background: #F9F9F9;
   }
 
-  tbody > tr {
+  #mozreview-child-requests-nav-row {
     background: #fff;
+    font-size: 90%;
+
+    td {
+      padding: 7px;
+      position: relative;
+      text-align: center;
+    }
+
+    a[rel="next"] {
+      position: absolute;
+      right: 10px;
+    }
+
+    a[rel="previous"] {
+      position: absolute;
+      left: 10px;
+    }
   }
 
   .mozreview_commit_summary {
     font-weight: bold;
     text-decoration: none;
+    font-size: @table-large-font-size;
 
     &:hover, &:active, &:focus {
       text-decoration: underline;
     }
   }
 
   .diffstat-insert {
     color: green;
   }
 
   .diffstat-delete {
-    color: #a02222;
-  }
-
-  & > tbody > tr[current="true"],
-  & > tbody > tr:hover {
-    background-color: #eee;
+    color: #A02222;
   }
 
  td,
  th {
    padding: 5px;
-   border-left: 1px solid #C1C1C1;
-   border-bottom: 1px solid #C1C1C1;
+   border-left: 1px solid #EEE;
+   border-bottom: 1px solid #EEE;
  }
 
  & > thead > tr {
-   background: @moz-blue-dark;
+   background: #EEE;
+   line-height: 1.5;
+   font-size: @table-large-font-size;
  }
 
  & > tbody td > a.commit_sha {
    font-family: monospace;
  }
 
  .reviewers {
    .inline-editor-form {
@@ -168,48 +193,22 @@
    }
    .buttons {
      white-space: nowrap;
    }
  }
 
  /* This was copied from the rb dashboard less file.*/
   .status {
-    text-align: center;
-  }
-
-  .no-approval {
-    cursor: default;
-  }
-
-  .approval {
-    cursor: default;
-    background-color: @approval-bg;
-  }
-
-  .approval-issues {
-    background: #fff4B0;
-    position: relative;
+    font-size: @table-large-font-size;
   }
-
-  .issue-count {
-    text-decoration: none;
-    color: inherit;
-    display: block;
-    padding: 10px 0;
-   }
+}
 
-  .shipit-count {
-    background-image: url("../shipit_bg.png");
-    background-position: top left;
-    background-repeat: repeat-x;
-    background-color: #6bc810;
-    border: 1px solid @approval-border-color;
-    color: #205003;
-  }
+#mozreview-all-commits {
+  font-weight: bold;
 }
 
 #error-container {
   &:not([haserror="true"]) {
     display: none;
   }
 
   border: 1px solid red;
--- a/pylib/mozreview/mozreview/static/mozreview/css/mozilla-theme-common.less
+++ b/pylib/mozreview/mozreview/static/mozreview/css/mozilla-theme-common.less
@@ -3,8 +3,17 @@
 
 @light-white: rgba(255, 255, 255, 0.6);
 
 .border-radius(...) {
   -moz-border-radius: @arguments;
   -webkit-border-radius: @arguments;
   border-radius: @arguments;
 }
+
+.review-status-icon(...) {
+  width: auto;
+  text-align: center;
+  background: #EEE;
+  font-weight: bold;
+  padding: 3px 10px;
+  display: inline-block;
+}
--- a/pylib/mozreview/mozreview/static/mozreview/css/mozilla-theme-reviews.less
+++ b/pylib/mozreview/mozreview/static/mozreview/css/mozilla-theme-reviews.less
@@ -1,31 +1,106 @@
 @import (reference) 'mozilla-theme-common.less';
 
+/* Maximum with for reviews content */
+#review_request .box-container, #reviews, #mozreview-review-header, #mozreview-child-requests {
+  max-width: 1200px;
+  margin-left: auto;
+  margin-right: auto;
+}
+
 /* Review Requests */
 .review-request {
   .main {
-    background: #fff;
+    background: #FFF;
     border: 0;
     border-radius: 0;
   }
 
   .content .field-container > pre {
     font-family: inherit;
     border: 0;
     padding: 0;
     font-size: 90%;
   }
+
+  .header {
+    display: none;
+  }
 }
 
-.reviewable-page #mozreview-child-requests > thead > tr > .status,
-.reviewable-page #mozreview-child-requests > tbody > tr > .status {
-  min-width: 86px;
+#mozreview-review-content {
+  text-align: center;
+
+  h1 {
+    font-size: 28px;
+    font-weight: normal;
+    display: block;
+    line-height: auto;
+    margin-bottom: 10px;
+  }
+
+  .review-request-meta {
+    color: #777;
+    text-align: center;
+    line-height: 1.6;
+    font-size: 110%;
+  }
+
+  #review-request-inputs {
+    margin-top: 20px;
+
+    input {
+      .border-radius(3px);
+      padding: 2px 6px;
+      background: #F9F9F9;
+      border: 1px solid #ddd;
+      font-family: monospace;
+      font-size: 16px !important;
+      width: 300px;
+      margin-right: 20px;
+    }
+  }
 }
 
+.status {
+  .review-status-icon();
+  font-size: 20px;
+  margin-bottom: 10px;
+
+  &.no-approval {
+    cursor: default;
+  }
+
+  &.approval {
+    cursor: default;
+    background-color: @approval-bg;
+  }
+
+  &.approval-issues {
+    background: #fff4B0;
+  }
+
+  .issue-count {
+    text-decoration: none;
+    color: #000;
+  }
+
+  .shipit-count {
+    background-image: url("../shipit_bg.png");
+    background-position: top left;
+    background-repeat: repeat-x;
+    background-color: #6bc810;
+    border: 1px solid @approval-border-color;
+    color: #205003;
+  }
+}
+
+
+
 /* Review Banners */
 .banner {
   border-bottom: 0;
 }
 
 /* Review Comments */
 .box, .box-inner {
   .border-radius(0);
--- a/pylib/mozreview/mozreview/static/mozreview/css/mozilla-theme.less
+++ b/pylib/mozreview/mozreview/static/mozreview/css/mozilla-theme.less
@@ -1,13 +1,14 @@
 @import (reference) 'mozilla-theme-common.less';
 
 body {
   background: @moz-blue-light;
-  font-size: 13px;
+  font-family: Arial, Helvetica, Sans-Serif;
+  font-size: 14px;
   padding-top: 8px;
 }
 
 /* Logo and Title */
 #headerbar {
   height: 18px;
 
   #title a {
@@ -260,16 +261,20 @@ body.datagrid-page {
   background: @moz-blue-dark;
   border: 0;
 }
 
 .actions-container {
   .border-radius(0);
 }
 
+.review-request-header {
+  line-height: 20px;
+}
+
 .review-request-header,
 .actions.page-tabs li {
   border-bottom: 0;
 }
 
 .actions.page-tabs li.active {
   border: 0;
   background: #fff;
--- a/pylib/mozreview/mozreview/static/mozreview/css/review.less
+++ b/pylib/mozreview/mozreview/static/mozreview/css/review.less
@@ -198,65 +198,33 @@ label[for="id_shipit"] {
     }
     .box::before, .box::after {
       display: none;
     }
   }
 }
 
 // Tooltips for landable and "r?" cells
-#mozreview-child-requests .help-tooltip,
-#mozreview-child-requests .status {
+#mozreview-child-requests .commits {
   position: relative;
 
   &:hover .review-tooltip {
     display: block;
   }
 }
 
 .review-tooltip {
   display: none;
   max-width: 400px;
   position: absolute;
-  top: -6px;
-  right: 90px;
+  bottom: 4px;
+  right: 4px;
   font-size: 12px;
   border: 1px #b09700 solid;
   background: #fff49c;
   max-width: 400px;
 }
 
 .review-tooltip-text {
   position: relative;
   padding: 10px;
   font-weight: normal;
 }
-
-/* shared with before and after */
-.review-tooltip-text:before, .review-tooltip-text:after {
-  content: ' ';
-  height: 0;
-  position: absolute;
-  width: 0;
-  border: 8px solid transparent; /* arrow size */
-}
-
-/* top-stacked, smaller arrow */
-.review-tooltip-text:before {
-  border-left-color: #fff49c;  /* arrow color */
-
-  /* positioning */
-  position: absolute;
-  top: 8px;
-  right: -15px;
-  z-index: 2;
-}
-
-/* arrow which acts as a background shadow */
-.review-tooltip-text:after {
-  border-left-color: #b09700;  /* arrow color */
-
-  /* positioning */
-  position: absolute;
-  top: 8px;
-  right: -16px;
-  z-index: 1;
-}
--- a/pylib/mozreview/mozreview/static/mozreview/js/review.js
+++ b/pylib/mozreview/mozreview/static/mozreview/js/review.js
@@ -14,22 +14,29 @@
   // Change string of "Review" button to be a verb so people better
   // understand what clicking it does.
   $("#review-link").text("Finish Review...");
 
   if (MozReview.isParent) {
     $('#review_request_extra').prepend(MRParents.parentWarning);
   }
 
+  // Show all commits when link is clicked
+  $('#mozreview-all-commits').on('click', function(e) {
+    e.preventDefault();
+    $('#mozreview-child-requests tr[hidden]').removeAttr('hidden');
+    $(this).hide();
+  });
+
   var reviewRequest = RB.PageManager.getPage().reviewRequest;
   RB.apiCall({
     type: 'GET',
     prefix: reviewRequest.get('sitePrefix'),
     noActivityIndicator: true,
-    url: '/api/review-requests/'+reviewRequest.get('id')+'/reviews/' +
+    url: '/api/review-requests/' + reviewRequest.get('id') + '/reviews/' +
          '?max-results=200',
     success: function(data) {
       _.forEach(data.reviews, function(item) {
         var flag = item.extra_data['p2rb.review_flag'];
         var flagDesc = '';
         var reviewText = $('#review'+item.id+' .body');
         switch(flag){
           case ' ':
@@ -44,26 +51,26 @@
         $(reviewText).prepend(
           $('<h4 class="body_top">'+flagDesc+'</h4>')
         );
       });
     }
   });
 
   // Tooltips for landable and "r?" cells
-  $('#mozreview-child-requests .help-tooltip, #mozreview-child-requests tbody .status').each(function() {
+  $('#mozreview-child-requests tbody .status').each(function() {
      var $element = $(this);
      var text = $element.attr('title');
 
-     if(!text) return;
+     if (!text) return;
 
      $element.attr('title', '');
 
      // Draw the tooltip title and text
-     var $tip = $('<div></div>').attr('class', 'review-tooltip').appendTo($element);
+     var $tip = $('<div></div>').attr('class', 'review-tooltip').appendTo($element.parent());
      $('<div></div>').attr('class', 'review-tooltip-text').text(text).appendTo($tip);
   });
 
   // Add a link to the parent on submitted children.
   if (!MozReview.isParent) {
     $('#submitted-banner').append(
       $('<a></a>')
         .addClass('reopen')
--- a/pylib/mozreview/mozreview/templates/mozreview/commits-requests.html
+++ b/pylib/mozreview/mozreview/templates/mozreview/commits-requests.html
@@ -10,44 +10,80 @@ by both the commits template and the com
 <table id="mozreview-child-requests">
   <thead>
   <tr>
     <th class="commits">{% trans "Commit" %}</th>
     {% comment "TODO: show this column when the commit author will be available" %}
     <th class="submitter">{% trans "Submitter" %}</th>
     {% endcomment %}
     <th class="reviewers">{% trans "Reviewers" %}</th>
-    <th class="status">
-      {% trans "Landable" %}
-      <div class="help-icon help-tooltip" title="{% trans "Hover over each commit's status to view landable status" %}"><span>?</span></div>
-    </th>
   </tr>
   </thead>
+
+  {% if parent_details.get_review_request.id != review_request_details.get_review_request.id and num_children > 1 %}
+  <tr id="mozreview-child-requests-nav-row">
+    <td colspan="2">
+      {% if prev_child %}
+        <a href="{{prev_child.get_review_request.get_absolute_url}}" rel="previous">&lt;&lt; Previous Commit</a>
+      {% endif %}
+      Viewing commit {{current_child_num}} of {{num_children}}.
+      <a href="javascript:;" id="mozreview-all-commits">View All Commits</a>
+      {% if next_child %}
+        <a href="{{next_child.get_review_request.get_absolute_url}}" rel="next">Next Commit &gt;&gt;</a>
+      {% endif %}
+    </td>
+  </tr>
+  {% endif %}
+
+
   {% for child_details in children_details %}
-  <tr {% if child_details.get_review_request.id = review_request_details.get_review_request.id %}current="true"{% endif %}>
+  <tr
+    {% if child_details.get_review_request.id == review_request_details.get_review_request.id %}
+      current="true"
+    {% else %}
+      {% if parent_details.get_review_request.id != review_request_details.get_review_request.id %}
+        hidden
+      {% endif %}
+    {% endif %}>
 
   <td class="commits">
     <div class="truncate_text">
-        <a class="mozreview_commit_summary" title="See diff for commit {{child_details|commit_id|slice:":12"}}"
-        href="{{child_details.get_review_request.get_absolute_url}}diff/#index_header">
-          {{ child_details.summary }}
-        </a>
-        <ul>
-          <li>
-            <a href="{{child_details.get_review_request.repository.path}}/raw-rev/{{child_details|commit_id|slice:":12"}}">
-              {% trans "View Raw" %}
-            </a>
-          </li><li>
-            <a href="{{child_details.get_review_request.get_absolute_url}}" title="{{ child_details.summary}}">
-                {% trans "View Reviews" %}
-            </a>
-        </li><li class="diffstat" title="{% trans "Lines inserted / deleted" %}">
-            {{ child_details.get_review_request|diffstat_text:user }}
-          </li>
-        </ul>
+      {% if child_details.get_review_request.issue_open_count > 0 %}
+        <div class="status approval-issues" title="{{child_details.get_review_request.issue_open_count}} open issue{{ child_details.get_review_request.issue_open_count|pluralize }}">
+          <a class="issue-count" href="{{child_details.get_review_request.get_absolute_url}}#issue-summary">
+            ! {{child_details.get_review_request.issue_open_count}}
+          </a>
+        </div>
+      {% elif child_details.get_review_request.approved %}
+        <div class="status approval" title="Approved For Landing - You have at least one valid ship it!">
+          r+
+        </div>
+      {% else %}
+        <div class="status no-approval" title="{{child_details.get_review_request.approval_failure}}">
+          r?
+        </div>
+      {% endif %}
+
+      <a class="mozreview_commit_summary" title="See diff for commit {{child_details|commit_id|slice:":12"}}"
+      href="{{child_details.get_review_request.get_absolute_url}}diff/#index_header">
+        {{ child_details.summary }}
+      </a>
+      <ul>
+        <li>
+          <a href="{{child_details.get_review_request.repository.path}}/raw-rev/{{child_details|commit_id|slice:":12"}}">
+            {% trans "View Raw" %}
+          </a>
+        </li><li>
+          <a href="{{child_details.get_review_request.get_absolute_url}}" title="{{ child_details.summary}}">
+              {% trans "View Reviews" %}
+          </a>
+      </li><li class="diffstat" title="{% trans "Lines inserted / deleted" %}">
+          {{ child_details.get_review_request|diffstat_text:user }}
+        </li>
+      </ul>
     </div>
   </td>
 
     {% comment "TODO: show this column when the commit author will be available" %}
       <td>{{ child_details.submitter }}</td>
     {% endcomment %}
 
     <td class="reviewers">
@@ -58,28 +94,11 @@ by both the commits template and the com
           {% if child_details|isDraft %}
             <span class="reviewer-name">{{ reviewer }}</span>
           {% else %}
             <span class="reviewer-name {% if status.ship_it %}reviewer-ship-it{% endif %} {{status.review_flag|review_flag_class}}">{{ reviewer }}</span>
           {% endif %}
         {% endfor %}
       </span>
     </td>
-
-    {% if child_details.get_review_request.issue_open_count > 0 %}
-      <td class="status approval-issues" title="{{child_details.get_review_request.issue_open_count}} open issues">
-        <a class="issue-count" href="{{child_details.get_review_request.get_absolute_url}}#issue-summary">
-          ! {{child_details.get_review_request.issue_open_count}}
-        </a>
-      </td>
-    {% elif child_details.get_review_request.approved %}
-      <td class="status approval" title="Approved For Landing - You have at least one valid ship it!">
-        r+
-      </td>
-    {% else %}
-      <td class="status no-approval" title="{{child_details.get_review_request.approval_failure}}">
-        r?
-      </td>
-    {% endif %}
-
   </tr>
   {% endfor %}
 </table>
--- a/pylib/mozreview/mozreview/templates/mozreview/commits.html
+++ b/pylib/mozreview/mozreview/templates/mozreview/commits.html
@@ -1,36 +1,54 @@
 {% comment %}
 This is the template for the "Commits" list in a push-based review request.
 {% endcomment %}
 
 {% load i18n %}
 {% load djblets_utils %}
 {% load mozreview %}
+{% load reviewtags %}
 
 <div id="error-container">
   <h1>Well, this is embarassing...</h1>
   <p>Something's gone wrong in either retrieving or manipulating these review requests. Sorry about that. <a href="https://bugzilla.mozilla.org/enter_bug.cgi?product=MozReview&component=General" target="_blank">Please consider filing a bug,</a> and including the following information:</p>
   <p id="error-info"></p>
   <pre id="error-stack"></pre>
   <a href="#" id="error-stack-toggle">Stack</a>
   <a href="#" id="error-close">Close</a>
 </div>
 
 <div id="mozreview-data"
   data-parent-review-id="{{ parent_details.get_review_request.id }}"
   data-selected-review-id="{{ review_request_details.get_review_request.id }}"
 ></div>
 
 <div id="mozreview-request-series">
-  <div id="mozreview-parent-request">
-    <a href="{{parent_details.get_review_request.get_absolute_url}}">Review Summary</a>
-    <a href="{{parent_details.get_review_request.get_absolute_url}}diff/#index_header">Squashed Diff</a>
+  <div id="mozreview-review-header">
+      {% if child_details.get_review_request.id == review_request_details.get_review_request.id %}
+        Review Summary
+      {% else %}
+        <a href="{{parent_details.get_review_request.get_absolute_url}}">Review Summary</a>
+      {% endif %}
+      &bull;
+      {% if review_request_details.get_review_request.bugs_closed %}
+        {% for bug in review_request_details.get_bug_list %}
+          <a href="{{bug|bug_url:review_request_details}}">Bug #{{bug}}</a>
+          {% if not forloop.last %}, {% endif %}
+        {% endfor %}
+        &bull;
+      {% endif %}
+      {{parent_details.get_review_request.repository}}
+      {% if parent_details.get_review_request.id == review_request_details.get_review_request.id %}
+        &bull; <a href="{{parent_details.get_review_request.get_absolute_url}}diff/#index_header">Squashed Diff</a>
+      {% endif %}
   </div>
+
   {% include 'mozreview/commits-requests.html' %}
+
   {% if latest_autoland_requests %}
   <div id="ci-actions">
     {% for autoland_request in latest_autoland_requests %}
       {% if not forloop.first %}
       <div class="action-separator"></div>
       {% endif %}
 
       {% if autoland_request.last_known_status == 'P' %}