diff --git a/reviewboard/diffviewer/renderers.py b/reviewboard/diffviewer/renderers.py
index b35ed9a9d0a992c9c711c5a9bfc629a000e1a761..53ffa4d2f2595ce0ec46ea870e8f87109298ee97 100644
--- a/reviewboard/diffviewer/renderers.py
+++ b/reviewboard/diffviewer/renderers.py
@@ -214,6 +214,12 @@ class DiffRenderer(object):
                     else:
                         self.diff_file['chunks'].remove(chunk)
 
+        equal_lines = 0
+
+        for chunk in self.diff_file['chunks']:
+            if chunk['change'] == 'equal':
+                equal_lines += chunk['numlines']
+
         context.update({
             'collapseall': self.collapse_all,
             'file': self.diff_file,
@@ -221,6 +227,7 @@ class DiffRenderer(object):
                            not self.diff_file['interfilediff'] and
                            not self.diff_file['filediff'].parent_diff,
             'lines_of_context': self.lines_of_context or (0, 0),
+            'equal_lines': equal_lines,
             'standalone': self.chunk_index is not None,
         })
 
diff --git a/reviewboard/settings.py b/reviewboard/settings.py
index 2e5f2c3aa23f44c15e4971bd0b3ee1015f68625c..e22cec46e535a3043fbf0107ebadbc89dc65d2ea 100644
--- a/reviewboard/settings.py
+++ b/reviewboard/settings.py
@@ -282,6 +282,10 @@ LOGIN_URL = SITE_ROOT + 'account/login/'
 PIPELINE_JS = {
     '3rdparty': {
         'source_filenames': (
+            'lib/js/flot/jquery.flot.min.js',
+            'lib/js/flot/jquery.flot.pie.min.js',
+            'lib/js/flot/jquery.flot.selection.min.js',
+            'lib/js/flot/jquery.flot.time.min.js',
             'lib/js/underscore-1.4.4.min.js',
             'lib/js/backbone-1.0.0.min.js',
             'lib/js/jquery.cookie-1.3.1.js',
@@ -470,10 +474,6 @@ PIPELINE_JS = {
     },
     'admin': {
         'source_filenames': (
-            'lib/js/flot/jquery.flot.min.js',
-            'lib/js/flot/jquery.flot.pie.min.js',
-            'lib/js/flot/jquery.flot.selection.min.js',
-            'lib/js/flot/jquery.flot.time.min.js',
             'lib/js/jquery.masonry.js',
             'rb/js/admin.js',
         ),
diff --git a/reviewboard/static/rb/css/diffviewer.less b/reviewboard/static/rb/css/diffviewer.less
index 308596c58439070ba5631e495221b119427476ba..c9e8818039096c5a503daf624d99b1beaacd79be 100644
--- a/reviewboard/static/rb/css/diffviewer.less
+++ b/reviewboard/static/rb/css/diffviewer.less
@@ -22,6 +22,7 @@
 @diff-delete-linenum-border-color:
   desaturate(darken(@diff-delete-linenum-color, 7%), 55%);
 @diff-delete-selected-color: darken(@diff-delete-linenum-color, 4%);
+@diff-delete-dot-color: desaturate(darken(@diff-delete-color, 30%), 35%);
 
 // Insert lines
 @diff-insert-color: #dfffd7;
@@ -30,6 +31,7 @@
 @diff-insert-linenum-border-color:
   desaturate(darken(@diff-insert-linenum-color, 7%), 35%);
 @diff-insert-selected-color: darken(@diff-insert-linenum-color, 5%);
+@diff-insert-dot-color: desaturate(darken(@diff-insert-color, 35%), 35%);
 
 // Replace lines
 @diff-replace-color: #fdfecc;
@@ -38,6 +40,7 @@
 @diff-replace-linenum-border-color:
   desaturate(darken(@diff-replace-linenum-color, 10%), 30%);
 @diff-replace-selected-color: darken(@diff-replace-linenum-color, 10%);
+@diff-replace-dot-color: darken(@diff-replace-color, 50%);
 
 // Revisions selector
 @revisions-border-color: #999999;
@@ -51,6 +54,19 @@
 @paginate-text-color: #f4f379;
 
 
+.diff-changes-icon-insert {
+  color: @diff-insert-dot-color;
+}
+
+.diff-changes-icon-replace {
+  color: @diff-replace-dot-color;
+}
+
+.diff-changes-icon-delete {
+  color: @diff-delete-dot-color;
+}
+
+
 .diff-container {
   .border-radius(@box-border-radius);
   border: 1px @diff-border-color solid;
@@ -445,11 +461,111 @@
   }
 }
 
-ol.index li {
-  padding: 2px 0;
+#diff_index {
+  border: 1px #BBB solid;
+  margin: 1em 1em 2em 1em;
+
+  table {
+    padding: 0;
+    border-collapse: collapse;
+    width: 100%;
+
+    tr {
+      min-height: 2.5em;
+
+      &:first-child td {
+        border-top: 0;
+      }
+
+      &.loading .diff-file-icon {
+        background-image: url("../images/spinner.gif");
+        background-position: center center;
+        background-repeat: no-repeat;
+        padding: 12px;
+      }
+
+      &.renamed-file .diff-file-info .diff-file-extra {
+        display: block;
+        padding: 1em 0 0 0;
+      }
+    }
+
+    td {
+      background: #FDFDFD;
+      padding: 0.6em;
+      margin: 0;
+      border-top: 1px #DDD solid;
+      vertical-align: top;
+
+      &.diff-chunks-cell {
+        color: #888;
+        text-align: right;
+        width: 70%;
+      }
+
+      &.diff-file-icon {
+        padding: 2px;
+
+        .rb-icon {
+          margin: 2px;
+          width: 16px;
+          height: 16px;
+        }
+      }
+
+      &.diff-file-info {
+        padding-left: 0.3em;
+        white-space: nowrap;
+        width: 30%;
+
+        .diff-file-rename {
+          color: #888;
+          display: block;
+          font-size: 90%;
+          font-style: italic;
+          padding: 1em 0 0 0;
+        }
+      }
+
+      a {
+        color: blue;
+        text-decoration: none;
+
+        &:hover {
+          text-decoration: underline;
+        }
+
+        &.dimmed {
+          color: #ABABAB;
+        }
+      }
+
+      .diff-chunks {
+        max-height: 2.5em;
+        overflow: hidden;
+        text-align: right;
+
+        a {
+          display: inline-block;
+          margin: 3px 2px;
+          width: 8px;
+          height: 8px;
+          .border-radius(50%);
+
+          &.insert {
+            background-color: @diff-insert-dot-color;
+          }
+
+          &.delete {
+            background-color: @diff-delete-dot-color;
+          }
 
-  a.dimmed {
-    color: #ABABAB;
+          &.replace {
+            background-color: @diff-replace-dot-color;
+          }
+        }
+      }
+    }
   }
 }
 
@@ -461,6 +577,10 @@ ol.index li {
   border-spacing: 8px;
   border-top: 1px #c2c1b0 solid;
   margin-top: 20px;
+
+  .main h1 {
+    margin: 1em;
+  }
 }
 
 .revision-selector {
diff --git a/reviewboard/static/rb/js/pages/views/diffViewerPageView.js b/reviewboard/static/rb/js/pages/views/diffViewerPageView.js
index ad17991cd1d53cd634f5eca39eb5693396437e23..b94ca48dc6c5497eb620eaa1c002d79e775b1a5b 100644
--- a/reviewboard/static/rb/js/pages/views/diffViewerPageView.js
+++ b/reviewboard/static/rb/js/pages/views/diffViewerPageView.js
@@ -1,4 +1,199 @@
 /*
+ * Displays the file index for the diffs on a page.
+ *
+ * The file page lists the names of the files, as well as a little graph
+ * icon showing the relative size and complexity of a file, a list of chunks
+ * (and their types), and the number of lines added and removed.
+ */
+var DiffFileIndexView = Backbone.View.extend({
+    chunkTemplate: _.template(
+        '<a href="#<%= chunkID %>" class="<%= className %>"> </a>'
+    ),
+
+    events: {
+        'click a': '_onAnchorClicked'
+    },
+
+    /*
+     * Initializes the view.
+     */
+    initialize: function() {
+        this._$items = null;
+        this._iconInsertColor = null;
+        this._iconReplaceColor = null;
+        this._iconDeleteColor = null;
+    },
+
+    /*
+     * Renders the view to the page.
+     *
+     * This will grab the list of items and precompute the colors used in
+     * the complexity icons.
+     */
+    render: function() {
+        var $iconColor = $('<div/>').appendTo(document.body);
+
+        this._$items = this.$('tr');
+
+        $iconColor[0].className = 'diff-changes-icon-insert';
+        this._iconInsertColor = $iconColor.css('color');
+
+        $iconColor[0].className = 'diff-changes-icon-replace';
+        this._iconReplaceColor = $iconColor.css('color');
+
+        $iconColor[0].className = 'diff-changes-icon-delete';
+        this._iconDeleteColor = $iconColor.css('color');
+
+        $iconColor.remove();
+
+        return this;
+    },
+
+    /*
+     * Adds a loaded diff to the index.
+     *
+     * The reserved entry for the diff will be populated with a link to the
+     * diff, and information about the diff.
+     */
+    addDiff: function(index, diffReviewableView) {
+        var $item = $(this._$items[index])
+            .removeClass('loading');
+
+        if (diffReviewableView.$el.hasClass('diff-error')) {
+            this._renderDiffError($item);
+        } else {
+            this._renderDiffEntry($item, diffReviewableView);
+        }
+    },
+
+    /*
+     * Renders a diff loading error.
+     *
+     * An error icon will be displayed in place of the typical complexity
+     * icon.
+     */
+    _renderDiffError: function($item) {
+        $('<div class="rb-icon rb-icon-warning"/>')
+            .appendTo($item.find('.diff-file-icon'));
+    },
+
+    /*
+     * Renders the display of a loaded diff.
+     */
+    _renderDiffEntry: function($item, diffReviewableView) {
+        var $table = diffReviewableView.$el,
+            fileDeleted = $item.hasClass('deleted-file'),
+            fileAdded = $item.hasClass('new-file'),
+            linesEqual = $table.data('lines-equal'),
+            numDeletes = 0,
+            numInserts = 0,
+            numReplaces = 0,
+            chunksList = [];
+
+        if (fileAdded) {
+            numInserts = 1;
+        } else if (fileDeleted) {
+            numDeletes = 1;
+        } else {
+            _.each($table.children('tbody'), function(chunk) {
+                var numRows = chunk.rows.length,
+                    $chunk = $(chunk);
+
+                if ($chunk.hasClass('delete')) {
+                    numDeletes += numRows;
+                } else if ($chunk.hasClass('insert')) {
+                    numInserts += numRows;
+                } else if ($chunk.hasClass('replace')) {
+                    numReplaces += numRows;
+                } else {
+                    return;
+                }
+
+                chunksList.push(this.chunkTemplate({
+                    chunkID: chunk.id.substr(5),
+                    className: chunk.className
+                }));
+            }, this);
+
+            /* Add clickable blocks for each diff chunk. */
+            $item.find('.diff-chunks').html(chunksList.join(''));
+        }
+
+        /* Render the complexity icon. */
+        this._renderComplexityIcon($item, numInserts, numDeletes, numReplaces,
+                                   linesEqual + numDeletes + numInserts +
+                                   numReplaces);
+
+        this.listenTo(diffReviewableView, 'chunkDimmed chunkUndimmed',
+                      function(chunkID) {
+            this.$('a[href="#' + chunkID + '"]').toggleClass('dimmed');
+        });
+    },
+
+    /*
+     * Renders the icon showing the general complexity of the diff.
+     *
+     * This icon is a pie graph showing the percentage of adds vs deletes
+     * vs replaces. The size of the white inner radius is a relative indicator
+     * of how large the change is for the file. Smaller inner radiuses indicate
+     * much larger changes, whereas larger radiuses represent smaller changes.
+     *
+     * Think of the inner radius as the unchanged lines.
+     */
+    _renderComplexityIcon: function($item, numInserts, numDeletes, numReplaces,
+                                    totalLines) {
+        function clampValue(val) {
+            return val === 0 ? 0 : Math.max(val, minValue);
+        }
+
+        var numTotal = numInserts + numDeletes + numReplaces,
+            minValue = numTotal * 0.15;
+
+        $('<div/>')
+            .width(20)
+            .height(20)
+            .appendTo($item.find('.diff-file-icon'))
+            .plot(
+                [
+                    {
+                        color: this._iconInsertColor,
+                        data: clampValue(numInserts)
+                    },
+                    {
+                        color: this._iconDeleteColor,
+                        data: clampValue(numDeletes)
+                    },
+                    {
+                        color: this._iconReplaceColor,
+                        data: clampValue(numReplaces)
+                    }
+                ],
+                {
+                    series: {
+                        pie: {
+                            show: true,
+                            innerRadius: 0.5 *
+                                         ((totalLines - numTotal) / totalLines),
+                            radius: 0.8
+                        }
+                    }
+                }
+            );
+    },
+
+    /*
+     * Handler for when an anchor is clicked.
+     *
+     * Gets the name of the target and emits anchorClicked.
+     */
+    _onAnchorClicked: function(e) {
+        e.preventDefault();
+
+        this.trigger('anchorClicked', e.target.href.split('#')[1]);
+    }
+});
+
+/*
  * Manages the diff viewer page.
  *
  * This provides functionality for the diff viewer page for managing the
@@ -25,7 +220,6 @@ RB.DiffViewerPageView = RB.ReviewablePageView.extend({
     },
 
     events: _.extend({
-        'click .index a': '_onIndexClicked',
         'click .toggle-whitespace-only-chunks': '_toggleWhitespaceOnlyChunks',
         'click .toggle-show-whitespace': '_toggleShowExtraWhitespace'
     }, RB.ReviewablePageView.prototype.events),
@@ -41,8 +235,8 @@ RB.DiffViewerPageView = RB.ReviewablePageView.extend({
         this._selectedAnchorIndex = -1;
         this._$anchors = $();
         this._$controls = null;
-        this._$indexes = null;
         this._diffReviewableViews = [];
+        this._diffFileIndexView = null;
 
         /* Check to see if there's an anchor we need to scroll to. */
         url = document.location.toString();
@@ -50,6 +244,15 @@ RB.DiffViewerPageView = RB.ReviewablePageView.extend({
     },
 
     /*
+     * Removes the view from the page.
+     */
+    remove: function() {
+        _.super(this).remove.call(this);
+
+        this._diffFileIndexView.remove();
+    },
+
+    /*
      * Renders the page and begins loading all diffs.
      */
     render: function() {
@@ -60,7 +263,14 @@ RB.DiffViewerPageView = RB.ReviewablePageView.extend({
         $reviewRequest = this.$('.review-request');
 
         this._$controls = $reviewRequest.find('ul.controls');
-        this._$indexes = $reviewRequest.find('ol.index');
+
+        this._diffFileIndexView = new DiffFileIndexView({
+            el: $('#diff_index')
+        });
+        this._diffFileIndexView.render();
+
+        this.listenTo(this._diffFileIndexView, 'anchorClicked',
+                      this.selectAnchorByName);
 
         $('#diffs').bindClass(RB.UserSession.instance,
                               'diffsShowExtraWhitespace', 'ewhl');
@@ -118,24 +328,18 @@ RB.DiffViewerPageView = RB.ReviewablePageView.extend({
      * pulled from the server.
      */
     _renderFileDiff: function(diffReviewable) {
-        var fileDiffID = diffReviewable.get('fileDiffID'),
-            tableID = 'file' + fileDiffID,
-            $table = $('#' + tableID),
-            diffReviewableView = new RB.DiffReviewableView({
-                el: $table,
+        var diffReviewableView = new RB.DiffReviewableView({
+                el: $('#file' + diffReviewable.get('fileDiffID')),
                 model: diffReviewable
             }),
             $anchor;
 
+        this._diffFileIndexView.addDiff(this._diffReviewableViews.length,
+                                        diffReviewableView);
+
         this._diffReviewableViews.push(diffReviewableView);
         diffReviewableView.render();
 
-        this.listenTo(diffReviewableView, 'chunkDimmed chunkUndimmed',
-                      function(chunkID) {
-            this._$indexes.find('a[href="#' + chunkID + '"]')
-                .toggleClass('dimmed');
-        });
-
         this.listenTo(diffReviewableView, 'fileClicked', function() {
             this.selectAnchorByName(diffReviewable.get('fileIndex'));
         });
@@ -145,7 +349,7 @@ RB.DiffViewerPageView = RB.ReviewablePageView.extend({
         });
 
         /* We must rebuild this every time. */
-        this._updateAnchors($table);
+        this._updateAnchors(diffReviewableView.$el);
 
         this.listenTo(diffReviewableView, 'chunkExpansionChanged', function() {
             /* The selection rectangle may not update -- bug #1353. */
@@ -317,17 +521,6 @@ RB.DiffViewerPageView = RB.ReviewablePageView.extend({
     },
 
     /*
-     * Handler for when a file/chunk index is clicked.
-     *
-     * Navigates to the proper file or chunk header for this anchor.
-     */
-    _onIndexClicked: function(e) {
-        this.selectAnchorByName(e.target.href.split('#')[1]);
-
-        return false;
-    },
-
-    /*
      * Toggles the display of diff chunks that only contain whitespace changes.
      */
     _toggleWhitespaceOnlyChunks: function() {
diff --git a/reviewboard/templates/diffviewer/changeindex.html b/reviewboard/templates/diffviewer/changeindex.html
index e18ae6acdb4cb1b3480e13d741ddb69315b12b6e..04ed50c94dc98f33da66cc4c2171bd60c5b6fb0c 100644
--- a/reviewboard/templates/diffviewer/changeindex.html
+++ b/reviewboard/templates/diffviewer/changeindex.html
@@ -1,22 +1,33 @@
 {% load i18n %}
 {% load staticfiles %}
-<ol class="index" start="{{page_start_index}}">
+<div id="diff_index">
+ <table>
 {% for file in files %}
- <li id="change_file_{{file.index}}"><a href="#{{file.index}}">{{file.dest_filename}}</a>:
-  <img src="{% static "rb/images/spinner.gif" %}"
-       width="10" height="10" alt="{% trans "Loading..." %}" />
- </li>
-{% endfor %}
-</ol>
-{% if is_paginated %}
- {% blocktrans %}This diff has been split across {{ pages }} pages:{% endblocktrans %}
- {% if has_previous %}<span class="paginate-previous"><a href="?page={{ previous_page }}" title="{% trans "Previous Page" %}">&lt;</a></span>{% endif %}
- {% for num in page_numbers %}
-  {% if num == page %}
-   <span class="paginate-current" title="{% trans "Current Page" %}">{{ num }}</span>
-  {% else %}
-   <span class="paginate-link"><a href="?page={{ num }}" title="{% blocktrans %}Page {{ num }} {% endblocktrans %}">{{ num }}</a></span>
-  {% endif %}
- {% endfor %}
- {% if has_next %}<span class="paginate-next"><a href="?page={{ next_page }}" title="{% trans "Next Page" %}">&gt;</a></span>{% endif %}
+  <tr class="loading {% spaceless %}
+{%  if file.newfile %}new-file{% endif %}
+{%  if file.binary %}binary-file{% endif %}
+{%  if file.deleted %}deleted-file{% endif %}
+{%  if file.dest_filename != file.depot_filename %}renamed-file{% endif %}
+{% endspaceless %}">
+   <td class="diff-file-icon"></td>
+   <td class="diff-file-info">
+    <a href="#{{file.index}}">{{file.dest_filename}}</a>
+{% if file.dest_filename != file.depot_filename %}
+    <span class="diff-file-rename">
+     Was {{file.depot_filename}}
+    </span>
+{% endif %}
+   </td>
+   <td class="diff-chunks-cell">
+{% if file.binary %}
+    {% trans "Binary file" %}
+{% elif file.deleted %}
+    {% trans "Deleted" %}
+{% else %}
+    <div class="diff-chunks"></div></td>
 {% endif %}
+   </td>
+  </tr>
+{% endfor %}
+ </table>
+</div>
diff --git a/reviewboard/templates/diffviewer/changeindex_entry.html b/reviewboard/templates/diffviewer/changeindex_entry.html
deleted file mode 100644
index 3a4ee60c990ff3a94267cb5356b273c1af74dc5e..0000000000000000000000000000000000000000
--- a/reviewboard/templates/diffviewer/changeindex_entry.html
+++ /dev/null
@@ -1,30 +0,0 @@
-{% load i18n %}
-<a href="#{{file.index}}">{{file.dest_filename|force_escape|escapejs}}</a>
-{% if file.dest_filename != file.depot_filename %}
- (was {{file.depot_filename}})
-{% endif %}
-:
-{% if error %}
-{%  trans "Diff currently unavailable." %}
-{% elif file.binary %}
-{%  trans "binary file" %}
-{% elif file.deleted %}
-{%  trans "deleted" %}
-{% else %}
-{%  blocktrans count file.num_changes as counter %}
- 1  change
-{%  plural %}
- {{counter}} changes
-{%  endblocktrans %}
-{%  if file.chunks|length != 0 %}
- [
-{%   if file.chunks|length == 1 %}
-    <a href="#{{file.index}}.{{file.chunks.0.index}}"> {% if file.chunks.0.change == "insert" %}new content{% elif file.chunks.0.change == "delete" %}deleted content{% endif %}</a>
-{%   else %}
-{%    for chunk_index in file.changed_chunk_indexes %}
-     <a href="#{{file.index}}.{{chunk_index}}">{{forloop.counter}}</a>
-{%    endfor %}
-{%   endif %}
- ]
-{%  endif %}
-{% endif %}
diff --git a/reviewboard/templates/diffviewer/diff_file_fragment.html b/reviewboard/templates/diffviewer/diff_file_fragment.html
index 2638e902c5f88dc380b8f6c661f9c6e2042dbbcb..e5522b59eac512373426d10c26bd1a8ec7928c80 100644
--- a/reviewboard/templates/diffviewer/diff_file_fragment.html
+++ b/reviewboard/templates/diffviewer/diff_file_fragment.html
@@ -46,7 +46,13 @@
 
 {% if file.changed_chunk_indexes or file.binary or file.deleted or file.moved %}
 {%  if not standalone %}
-<table class="sidebyside{% if is_new_file %} newfile{% endif %}" id="file{{file.filediff.id}}"{% if not file.interfilediff %} data-lines-added="{{file.filediff.insert_count}}" data-lines-removed="{{file.filediff.delete_count}}"{% endif %}>
+<table id="file{{file.filediff.id}}" class="{% spaceless %}
+  sidebyside
+  {% if is_new_file %}newfile{% endif %}
+  {% if file.binary %}diff-binary{% endif %}
+  {% if file.deleted %}diff-deleted{% endif %}
+  {% endspaceless %}"
+       data-lines-equal="{{equal_lines}}">
  <colgroup>
 {% if not is_new_file %}
   <col class="line" />
@@ -145,20 +151,5 @@
 {%     endif %}{# file deleted, binary and whitespace_only #}
 {%  if not standalone %}
 </table>
-<script type="text/javascript">
-  $(document).ready(function() {
-    /* Add to the change index. */
-    $("#change_file_{{file.index}}").html(
-      {% include_as_string "diffviewer/changeindex_entry.html" %});
-  });
-</script>
-{%  endif %}{# not standalone #}
-{% else %}{# No changed chunks and not a binary file #}
-{%  if not standalone %}
-<script type="text/javascript">
-  $(document).ready(function() {
-    $("#change_file_{{file.index}}").remove();
-  });
-</script>
 {%  endif %}{# not standalone #}
 {% endif %}{# No changed chunks and not a binary file #}
diff --git a/reviewboard/templates/diffviewer/diff_fragment_error.html b/reviewboard/templates/diffviewer/diff_fragment_error.html
index 2a11abfe661b789268613dd635f47ab908241697..582f8d4b20c934bf92bea6220a071eb39be7d66c 100644
--- a/reviewboard/templates/diffviewer/diff_fragment_error.html
+++ b/reviewboard/templates/diffviewer/diff_fragment_error.html
@@ -2,7 +2,7 @@
 {% load i18n %}
 
 <table class="sidebyside diff-error{% if file.newfile %} newfile{% endif %}"
-       id="file_container_{{file.filediff.id}}">
+       id="file{{file.filediff.id}}">
  <thead>
   <tr class="filename-row">
 {% if comment %}
@@ -54,10 +54,3 @@
 {% endif %}
  </tbody>
 </table>
-{% if file %}
-<script type="text/javascript">
-  /* Add to the change index. */
-  $("#change_file_{{file.index}}").html(
-    {% include_as_string "diffviewer/changeindex_entry.html" %});
-</script>
-{% endif %}
diff --git a/reviewboard/templates/diffviewer/view_diff.html b/reviewboard/templates/diffviewer/view_diff.html
index 834e438aced122ed10c2dcbe70f7725cee490f7e..b88f02ff20830918ab116d43a0f14e4df77341c8 100644
--- a/reviewboard/templates/diffviewer/view_diff.html
+++ b/reviewboard/templates/diffviewer/view_diff.html
@@ -160,7 +160,6 @@
  </table>
 {% endif %}
 
- <p><label>{% trans "Files Changed:" %}</label></p>
 {% include "diffviewer/changeindex.html" %}
   </div>
  </div>
@@ -207,7 +206,19 @@
 {%  endif %}
 </div>
 {% endfor %}
-</div>
+
+{% if is_paginated %}
+ {% blocktrans %}This diff has been split across {{ pages }} pages:{% endblocktrans %}
+ {% if has_previous %}<span class="paginate-previous"><a href="?page={{ previous_page }}" title="{% trans "Previous Page" %}">&lt;</a></span>{% endif %}
+ {% for num in page_numbers %}
+  {% if num == page %}
+   <span class="paginate-current" title="{% trans "Current Page" %}">{{ num }}</span>
+  {% else %}
+   <span class="paginate-link"><a href="?page={{ num }}" title="{% blocktrans %}Page {{ num }} {% endblocktrans %}">{{ num }}</a></span>
+  {% endif %}
+ {% endfor %}
+ {% if has_next %}<span class="paginate-next"><a href="?page={{ next_page }}" title="{% trans "Next Page" %}">&gt;</a></span>{% endif %}
+{% endif %}
 </div>
 
 {% endif %}{# !error #}
