diff --git a/src/applications/differential/render/DifferentialChangesetHTMLRenderer.php b/src/applications/differential/render/DifferentialChangesetHTMLRenderer.php
--- a/src/applications/differential/render/DifferentialChangesetHTMLRenderer.php
+++ b/src/applications/differential/render/DifferentialChangesetHTMLRenderer.php
@@ -439,4 +439,92 @@
       ->setAllowReply($allow_reply);
   }
 
+
+  /**
+   * Build links which users can click to show more context in a changeset.
+   *
+   * @param int Beginning of the line range to build links for.
+   * @param int Length of the line range to build links for.
+   * @param int Total number of lines in the changeset.
+   * @return markup Rendered links.
+   */
+  protected function renderShowContextLinks($top, $len, $changeset_length) {
+    $block_size = 20;
+    $end = ($top + $len) - $block_size;
+
+    // If this is a large block, such that the "top" and "bottom" ranges are
+    // non-overlapping, we'll provide options to show the top, bottom or entire
+    // block. For smaller blocks, we only provide an option to show the entire
+    // block, since it would be silly to show the bottom 20 lines of a 25-line
+    // block.
+    $is_large_block = ($len > ($block_size * 2));
+
+    $links = array();
+
+    if ($is_large_block) {
+      $is_first_block = ($top == 0);
+      if ($is_first_block) {
+        $text = pht('Show First %d Line(s)', $block_size);
+      } else {
+        $text = pht("\xE2\x96\xB2 Show %d Line(s)", $block_size);
+      }
+
+      $links[] = $this->renderShowContextLink(
+        false,
+        "{$top}-{$len}/{$top}-20",
+        $text);
+    }
+
+    $links[] = $this->renderShowContextLink(
+      true,
+      "{$top}-{$len}/{$top}-{$len}",
+      pht('Show All %d Line(s)', $len));
+
+    if ($is_large_block) {
+      $is_last_block = (($top + $len) >= $changeset_length);
+      if ($is_last_block) {
+        $text = pht('Show Last %d Line(s)', $block_size);
+      } else {
+        $text = pht("\xE2\x96\xBC Show %d Line(s)", $block_size);
+      }
+
+      $links[] = $this->renderShowContextLink(
+        false,
+        "{$top}-{$len}/{$end}-20",
+        $text);
+    }
+
+    return phutil_implode_html(" \xE2\x80\xA2 ", $links);
+  }
+
+
+  /**
+   * Build a link that shows more context in a changeset.
+   *
+   * See @{method:renderShowContextLinks}.
+   *
+   * @param bool Does this link show all context when clicked?
+   * @param string Range specification for lines to show.
+   * @param string Text of the link.
+   * @return markup Rendered link.
+   */
+  private function renderShowContextLink($is_all, $range, $text) {
+    $reference = $this->getRenderingReference();
+
+    return javelin_tag(
+      'a',
+      array(
+        'href' => '#',
+        'mustcapture' => true,
+        'sigil' => 'show-more',
+        'meta' => array(
+          'type' => ($is_all ? 'all' : null),
+          'ref' => $reference,
+          'range' => $range,
+        ),
+      ),
+      $text);
+  }
+
+
 }
diff --git a/src/applications/differential/render/DifferentialChangesetOneUpRenderer.php b/src/applications/differential/render/DifferentialChangesetOneUpRenderer.php
--- a/src/applications/differential/render/DifferentialChangesetOneUpRenderer.php
+++ b/src/applications/differential/render/DifferentialChangesetOneUpRenderer.php
@@ -69,9 +69,28 @@
           break;
         case 'no-context':
           $out[] = hsprintf(
-            '<tr><td class="show-more" colspan="3">%s</th></tr>',
+            '<tr><td class="show-more" colspan="3">%s</td></tr>',
             pht('Context not available.'));
           break;
+        case 'context':
+          $top = $p['top'];
+          $len = $p['len'];
+
+          $links = $this->renderShowContextLinks($top, $len, $rows);
+
+          $out[] = javelin_tag(
+            'tr',
+            array(
+              'sigil' => 'context-target',
+            ),
+            phutil_tag(
+              'td',
+              array(
+                'class' => 'show-more',
+                'colspan' => 3,
+              ),
+              $links));
+          break;
         default:
           $out[] = hsprintf('<tr><th /><th /><td>%s</td></tr>', $type);
           break;
diff --git a/src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php b/src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php
--- a/src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php
+++ b/src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php
@@ -84,6 +84,9 @@
         // increments $ii by the entire size of the gap and then continues
         // the loop.
         $gap = array_pop($gaps);
+
+        // TODO: Move this to renderShowContextLinks() once that is stable.
+
         $top = $gap[0];
         $len = $gap[1];
 
diff --git a/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php b/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php
--- a/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php
+++ b/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php
@@ -941,6 +941,32 @@
           'allowed domains will be able to register an account: %3$s',
         ),
       ),
+
+      'Show First %d Line(s)' => array(
+        'Show First Line',
+        'Show First %d Lines',
+      ),
+
+      "\xE2\x96\xB2 Show %d Line(s)" => array(
+        "\xE2\x96\xB2 Show %d Line(s)",
+        "\xE2\x96\xB2 Show %d Line(s)",
+      ),
+
+      'Show All %d Line(s)' => array(
+        'Show Line',
+        'Show All %d Lines',
+      ),
+
+      "\xE2\x96\xBC Show %d Line(s)" => array(
+        "\xE2\x96\xBC Show Line",
+        "\xE2\x96\xBC Show %d Lines",
+      ),
+
+      'Show Last %d Line(s)' => array(
+        'Show Last Line',
+        'Show Last %d Lines',
+      ),
+
     );
   }