diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -10,7 +10,7 @@
     'conpherence.pkg.css' => '3c8a0668',
     'conpherence.pkg.js' => '020aebcf',
     'core.pkg.css' => 'f2319e1f',
-    'core.pkg.js' => 'a7c9c5f4',
+    'core.pkg.js' => '95f3ae17',
     'differential.pkg.css' => '9a93fe9e',
     'differential.pkg.js' => 'e268fcc6',
     'diffusion.pkg.css' => '42c75c37',
@@ -471,7 +471,7 @@
     'rsrc/js/core/behavior-linked-container.js' => '74446546',
     'rsrc/js/core/behavior-more.js' => '506aa3f4',
     'rsrc/js/core/behavior-object-selector.js' => 'a4af0b4a',
-    'rsrc/js/core/behavior-oncopy.js' => '12e8deee',
+    'rsrc/js/core/behavior-oncopy.js' => '2a482382',
     'rsrc/js/core/behavior-phabricator-nav.js' => 'f166c949',
     'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '2f80333f',
     'rsrc/js/core/behavior-read-only-warning.js' => 'b9109f8f',
@@ -634,7 +634,7 @@
     'javelin-behavior-phabricator-nav' => 'f166c949',
     'javelin-behavior-phabricator-notification-example' => '29819b75',
     'javelin-behavior-phabricator-object-selector' => 'a4af0b4a',
-    'javelin-behavior-phabricator-oncopy' => '12e8deee',
+    'javelin-behavior-phabricator-oncopy' => '2a482382',
     'javelin-behavior-phabricator-remarkup-assist' => '2f80333f',
     'javelin-behavior-phabricator-reveal-content' => 'b105a3a6',
     'javelin-behavior-phabricator-search-typeahead' => '1cb7d027',
@@ -998,10 +998,6 @@
       'javelin-workflow',
       'phuix-icon-view',
     ),
-    '12e8deee' => array(
-      'javelin-behavior',
-      'javelin-dom',
-    ),
     '1325b731' => array(
       'javelin-behavior',
       'javelin-uri',
@@ -1111,6 +1107,10 @@
       'javelin-stratcom',
       'javelin-behavior',
     ),
+    '2a482382' => array(
+      'javelin-behavior',
+      'javelin-dom',
+    ),
     '2a8b62d9' => array(
       'multirow-row-manager',
       'javelin-install',
diff --git a/src/applications/differential/render/DifferentialChangesetRenderer.php b/src/applications/differential/render/DifferentialChangesetRenderer.php
--- a/src/applications/differential/render/DifferentialChangesetRenderer.php
+++ b/src/applications/differential/render/DifferentialChangesetRenderer.php
@@ -363,11 +363,6 @@
 
     $result = $notice.$props.$undershield.$content;
 
-    // TODO: Let the user customize their tab width / display style.
-    // TODO: We should possibly post-process "\r" as well.
-    // TODO: Both these steps should happen earlier.
-    $result = str_replace("\t", '  ', $result);
-
     return phutil_safe_html($result);
   }
 
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
@@ -69,6 +69,18 @@
     $depths = $this->getDepths();
     $mask = $this->getMask();
 
+    foreach ($old_lines as $k => $map) {
+      if (!$map['line']) {
+        continue;
+      }
+
+      $raw = $map['text'];
+      $copy = str_replace('o', "\xF0\x9F\x90\x91", $raw);
+      if ($copy !== $raw) {
+        $old_lines[$k]['raw'] = $copy;
+      }
+    }
+
     for ($ii = $range_start; $ii < $range_start + $range_len; $ii++) {
       if (empty($mask[$ii])) {
         // If we aren't going to show this line, we've just entered a gap.
@@ -142,9 +154,13 @@
       $o_num = null;
       $o_classes = '';
       $o_text = null;
+      $o_raw = null;
       if (isset($old_lines[$ii])) {
-        $o_num  = $old_lines[$ii]['line'];
+        $o_num = $old_lines[$ii]['line'];
         $o_text = isset($old_render[$ii]) ? $old_render[$ii] : null;
+        if (isset($old_lines[$ii]['raw'])) {
+          $o_raw = $old_lines[$ii]['raw'];
+        }
         if ($old_lines[$ii]['type']) {
           if ($old_lines[$ii]['type'] == '\\') {
             $o_text = $old_lines[$ii]['text'];
@@ -302,6 +318,7 @@
           array(
             'class' => $o_classes,
             'data-copy-mode' => 'copy-l',
+            'data-copy-text' => $o_raw,
           ),
           $o_text),
         $new_number,
diff --git a/webroot/rsrc/js/core/behavior-oncopy.js b/webroot/rsrc/js/core/behavior-oncopy.js
--- a/webroot/rsrc/js/core/behavior-oncopy.js
+++ b/webroot/rsrc/js/core/behavior-oncopy.js
@@ -271,6 +271,13 @@
       // Otherwise, fall through and extract this node's text normally.
     }
 
+    if (node.getAttribute) {
+      var copy_text = node.getAttribute('data-copy-text');
+      if (copy_text) {
+        return copy_text;
+      }
+    }
+
     if (!node.childNodes || !node.childNodes.length) {
       return node.textContent;
     }