diff --git a/src/applications/differential/field/specification/DifferentialRevisionIDFieldSpecification.php b/src/applications/differential/field/specification/DifferentialRevisionIDFieldSpecification.php
index 8cefeceb4..3f4bd54e4 100644
--- a/src/applications/differential/field/specification/DifferentialRevisionIDFieldSpecification.php
+++ b/src/applications/differential/field/specification/DifferentialRevisionIDFieldSpecification.php
@@ -1,112 +1,128 @@
 <?php
 
 /*
  * Copyright 2012 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 final class DifferentialRevisionIDFieldSpecification
   extends DifferentialFieldSpecification {
 
   private $id;
 
   protected function didSetRevision() {
     $this->id = $this->getRevision()->getID();
   }
 
   public function shouldAppearOnCommitMessage() {
     return true;
   }
 
   public function shouldAppearOnCommitMessageTemplate() {
     return false;
   }
 
   public function getCommitMessageKey() {
     return 'revisionID';
   }
 
   public function setValueFromParsedCommitMessage($value) {
     $this->id = $value;
     return $this;
   }
 
   public function renderLabelForCommitMessage() {
     return 'Differential Revision';
   }
 
   public function renderValueForCommitMessage($is_edit) {
     if (!$this->id) {
       return null;
     }
     return PhabricatorEnv::getProductionURI('/D'.$this->id);
   }
 
   public function parseValueFromCommitMessage($value) {
     $rev = trim($value);
 
     if (!strlen($rev)) {
       return null;
     }
 
     if (is_numeric($rev)) {
       // TODO: Eventually, remove support for bare revision numbers.
       return (int)$rev;
     }
 
     $rev = self::parseRevisionIDFromURI($rev);
     if ($rev !== null) {
       return $rev;
     }
 
     $example_uri = PhabricatorEnv::getProductionURI('/D123');
     throw new DifferentialFieldParseException(
       "Commit references invalid 'Differential Revision'. Expected a ".
       "Phabricator URI like '{$example_uri}', got '{$value}'.");
   }
 
   public static function parseRevisionIDFromURI($uri) {
     $path = id(new PhutilURI($uri))->getPath();
 
     $matches = null;
     if (preg_match('#^/D(\d+)$#', $path, $matches)) {
       $id = (int)$matches[1];
       // Make sure the URI is the same as our URI. Basically, we want to ignore
       // commits from other Phabricator installs.
       if ($uri == PhabricatorEnv::getProductionURI('/D'.$id)) {
         return $id;
       }
     }
 
     return null;
   }
 
   public function shouldAppearOnRevisionList() {
     return true;
   }
 
   public function renderHeaderForRevisionList() {
     return 'ID';
   }
 
   public function renderValueForRevisionList(DifferentialRevision $revision) {
     return 'D'.$revision->getID();
   }
 
   public function renderValueForMail($phase) {
-    $uri = PhabricatorEnv::getProductionURI('/D'.$this->id);
-    return "REVISION DETAIL\n  {$uri}";
+    $body = array();
+    $body[] = 'REVISION DETAIL';
+    $body[] = '  '.PhabricatorEnv::getProductionURI('/D'.$this->id);
+
+    if ($phase == DifferentialMailPhase::UPDATE) {
+      $diffs = id(new DifferentialDiff())->loadAllWhere(
+        'revisionID = %d ORDER BY id DESC LIMIT 2',
+        $this->id);
+      if (count($diffs) == 2) {
+        list($new, $old) = array_values(mpull($diffs, 'getID'));
+        $body[] = null;
+        $body[] = 'CHANGE SINCE LAST DIFF';
+        $body[] = '  '.PhabricatorEnv::getProductionURI(
+          "/D{$this->id}?vs={$old}&id={$new}#differential-review-toc");
+      }
+    }
+
+    return implode("\n", $body);
   }
 
 }
diff --git a/src/applications/differential/mail/DifferentialMail.php b/src/applications/differential/mail/DifferentialMail.php
index 4b607ae22..7014e2743 100644
--- a/src/applications/differential/mail/DifferentialMail.php
+++ b/src/applications/differential/mail/DifferentialMail.php
@@ -1,426 +1,426 @@
 <?php
 
 /*
  * Copyright 2012 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
  *   http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 abstract class DifferentialMail {
 
   protected $to = array();
   protected $cc = array();
 
   protected $actorHandle;
 
   protected $revision;
   protected $comment;
   protected $changesets;
   protected $inlineComments;
   protected $isFirstMailAboutRevision;
   protected $isFirstMailToRecipients;
   protected $heraldTranscriptURI;
   protected $heraldRulesHeader;
   protected $replyHandler;
   protected $parentMessageID;
 
   protected function renderSubject() {
     $revision = $this->getRevision();
     $title = $revision->getTitle();
     $id = $revision->getID();
     return "D{$id}: {$title}";
   }
 
   abstract protected function renderVaryPrefix();
   abstract protected function renderBody();
 
   public function setActorHandle($actor_handle) {
     $this->actorHandle = $actor_handle;
     return $this;
   }
 
   public function getActorHandle() {
     return $this->actorHandle;
   }
 
   protected function getActorName() {
     $handle = $this->getActorHandle();
     if ($handle) {
       return $handle->getName();
     }
     return '???';
   }
 
   public function setParentMessageID($parent_message_id) {
     $this->parentMessageID = $parent_message_id;
     return $this;
   }
 
   public function setXHeraldRulesHeader($header) {
     $this->heraldRulesHeader = $header;
     return $this;
   }
 
   public function send() {
     $to_phids = $this->getToPHIDs();
     if (!$to_phids) {
       throw new Exception('No "To:" users provided!');
     }
 
     $cc_phids     = $this->getCCPHIDs();
     $body         = $this->buildBody();
     $attachments  = $this->buildAttachments();
 
     $template = new PhabricatorMetaMTAMail();
     $actor_handle = $this->getActorHandle();
     $reply_handler = $this->getReplyHandler();
 
     if ($actor_handle) {
       $template->setFrom($actor_handle->getPHID());
     }
 
     $template
       ->setSubject($this->renderSubject())
       ->setSubjectPrefix($this->getSubjectPrefix())
       ->setVarySubjectPrefix($this->renderVaryPrefix())
       ->setBody($body)
       ->setIsHTML($this->shouldMarkMailAsHTML())
       ->setParentMessageID($this->parentMessageID)
       ->addHeader('Thread-Topic', $this->getThreadTopic());
 
     $template->setAttachments($attachments);
 
     $template->setThreadID(
       $this->getThreadID(),
       $this->isFirstMailAboutRevision());
 
     if ($this->heraldRulesHeader) {
       $template->addHeader('X-Herald-Rules', $this->heraldRulesHeader);
     }
 
     $revision = $this->revision;
     if ($revision) {
       if ($revision->getAuthorPHID()) {
         $template->addHeader(
           'X-Differential-Author',
           '<'.$revision->getAuthorPHID().'>');
       }
       if ($revision->getReviewers()) {
         $template->addHeader(
           'X-Differential-Reviewers',
           '<'.implode('>, <', $revision->getReviewers()).'>');
       }
       if ($revision->getCCPHIDs()) {
         $template->addHeader(
           'X-Differential-CCs',
           '<'.implode('>, <', $revision->getCCPHIDs()).'>');
 
         // Determine explicit CCs (those added by humans) and put them in a
         // header so users can differentiate between Herald CCs and human CCs.
 
         $relation_subscribed = DifferentialRevision::RELATION_SUBSCRIBED;
         $raw = $revision->getRawRelations($relation_subscribed);
 
         $reason_phids = ipull($raw, 'reasonPHID');
         $reason_handles = id(new PhabricatorObjectHandleData($reason_phids))
           ->loadHandles();
 
         $explicit_cc = array();
         foreach ($raw as $relation) {
           if (!$relation['reasonPHID']) {
             continue;
           }
           $type = $reason_handles[$relation['reasonPHID']]->getType();
           if ($type == PhabricatorPHIDConstants::PHID_TYPE_USER) {
             $explicit_cc[] = $relation['objectPHID'];
           }
         }
 
         if ($explicit_cc) {
           $template->addHeader(
             'X-Differential-Explicit-CCs',
             '<'.implode('>, <', $explicit_cc).'>');
         }
       }
     }
 
     $template->setIsBulk(true);
     $template->setRelatedPHID($this->getRevision()->getPHID());
 
     $mailtags = $this->getMailTags();
     if ($mailtags) {
       $template->setMailTags($mailtags);
     }
 
     $phids = array();
     foreach ($to_phids as $phid) {
       $phids[$phid] = true;
     }
     foreach ($cc_phids as $phid) {
       $phids[$phid] = true;
     }
     $phids = array_keys($phids);
 
     $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
 
     $event = new PhabricatorEvent(
       PhabricatorEventType::TYPE_DIFFERENTIAL_WILLSENDMAIL,
       array(
         'mail' => $template,
       )
     );
     PhutilEventEngine::dispatchEvent($event);
 
     $template = $event->getValue('mail');
 
     $mails = $reply_handler->multiplexMail(
       $template,
       array_select_keys($handles, $to_phids),
       array_select_keys($handles, $cc_phids));
 
     foreach ($mails as $mail) {
       $mail->saveAndSend();
     }
   }
 
   protected function getMailTags() {
     return array();
   }
 
   protected function getSubjectPrefix() {
     return PhabricatorEnv::getEnvConfig('metamta.differential.subject-prefix');
   }
 
   protected function shouldMarkMailAsHTML() {
     return false;
   }
 
   protected function buildBody() {
 
     $body = $this->renderBody();
 
     $reply_handler = $this->getReplyHandler();
     $reply_instructions = $reply_handler->getReplyHandlerInstructions();
     if ($reply_instructions) {
       $body .=
         "\nREPLY HANDLER ACTIONS\n".
         "  {$reply_instructions}\n";
     }
 
     if ($this->getHeraldTranscriptURI() && $this->isFirstMailToRecipients()) {
       $manage_uri = PhabricatorEnv::getProductionURI(
         '/herald/view/differential/');
 
       $xscript_uri = $this->getHeraldTranscriptURI();
       $body .= <<<EOTEXT
 
 MANAGE HERALD DIFFERENTIAL RULES
   {$manage_uri}
 
 WHY DID I GET THIS EMAIL?
   {$xscript_uri}
 
 EOTEXT;
     }
 
     return $body;
   }
 
   /**
    * You can override this method in a subclass and return array of attachments
    * to be sent with the email.  Each attachment is an instance of
    * PhabricatorMetaMTAAttachment.
    */
   protected function buildAttachments() {
     return array();
   }
 
   public function getReplyHandler() {
     if (!$this->replyHandler) {
       $this->replyHandler =
         self::newReplyHandlerForRevision($this->getRevision());
     }
 
     return $this->replyHandler;
   }
 
   public static function newReplyHandlerForRevision(
     DifferentialRevision $revision) {
 
     $reply_handler = PhabricatorEnv::newObjectFromConfig(
       'metamta.differential.reply-handler');
     $reply_handler->setMailReceiver($revision);
 
     return $reply_handler;
   }
 
 
   protected function formatText($text) {
-    $text = explode("\n", $text);
+    $text = explode("\n", rtrim($text));
     foreach ($text as &$line) {
       $line = rtrim('  '.$line);
     }
     unset($line);
     return implode("\n", $text);
   }
 
   public function setToPHIDs(array $to) {
     $this->to = $this->filterContactPHIDs($to);
     return $this;
   }
 
   public function setCCPHIDs(array $cc) {
     $this->cc = $this->filterContactPHIDs($cc);
     return $this;
   }
 
   protected function filterContactPHIDs(array $phids) {
     return $phids;
 
     // TODO: actually do this?
 
     // Differential revisions use Subscriptions for CCs, so any arbitrary
     // PHID can end up CC'd to them. Only try to actually send email PHIDs
     // which have ToolsHandle types that are marked emailable. If we don't
     // filter here, sending the email will fail.
 /*
     $handles = array();
     prep(new ToolsHandleData($phids, $handles));
     foreach ($handles as $phid => $handle) {
       if (!$handle->isEmailable()) {
         unset($handles[$phid]);
       }
     }
     return array_keys($handles);
 */
   }
 
   protected function getToPHIDs() {
     return $this->to;
   }
 
   protected function getCCPHIDs() {
     return $this->cc;
   }
 
   public function setRevision($revision) {
     $this->revision = $revision;
     return $this;
   }
 
   public function getRevision() {
     return $this->revision;
   }
 
   protected function getThreadID() {
     $phid = $this->getRevision()->getPHID();
     return "differential-rev-{$phid}-req";
   }
 
   protected function getThreadTopic() {
     $phid = $this->getRevision()->getPHID();
     return "Differential Revision {$phid}";
   }
 
   public function setComment($comment) {
     $this->comment = $comment;
     return $this;
   }
 
   public function getComment() {
     return $this->comment;
   }
 
   public function setChangesets($changesets) {
     $this->changesets = $changesets;
     return $this;
   }
 
   public function getChangesets() {
     return $this->changesets;
   }
 
   public function setInlineComments(array $inline_comments) {
     assert_instances_of($inline_comments, 'PhabricatorInlineCommentInterface');
     $this->inlineComments = $inline_comments;
     return $this;
   }
 
   public function getInlineComments() {
     return $this->inlineComments;
   }
 
   protected function renderAuxFields($phase) {
     $selector = DifferentialFieldSelector::newSelector();
     $aux_fields = $selector->sortFieldsForMail(
       $selector->getFieldSpecifications());
 
     $body = array();
     foreach ($aux_fields as $field) {
       $field->setRevision($this->getRevision());
       $text = $field->renderValueForMail($phase);
       if ($text !== null) {
         $body[] = $text;
         $body[] = null;
       }
     }
 
     return implode("\n", $body);
   }
 
   public function renderRevisionDetailLink() {
     $uri = $this->getRevisionURI();
     return "REVISION DETAIL\n  {$uri}";
   }
 
   public function getRevisionURI() {
     return PhabricatorEnv::getProductionURI('/D'.$this->getRevision()->getID());
   }
 
   public function setIsFirstMailToRecipients($first) {
     $this->isFirstMailToRecipients = $first;
     return $this;
   }
 
   public function isFirstMailToRecipients() {
     return $this->isFirstMailToRecipients;
   }
 
   public function setIsFirstMailAboutRevision($first) {
     $this->isFirstMailAboutRevision = $first;
     return $this;
   }
 
   public function isFirstMailAboutRevision() {
     return $this->isFirstMailAboutRevision;
   }
 
   public function setHeraldTranscriptURI($herald_transcript_uri) {
     $this->heraldTranscriptURI = $herald_transcript_uri;
     return $this;
   }
 
   public function getHeraldTranscriptURI() {
     return $this->heraldTranscriptURI;
   }
 
   protected function renderHandleList(array $handles, array $phids) {
     assert_instances_of($handles, 'PhabricatorObjectHandle');
     $names = array();
     foreach ($phids as $phid) {
       $names[] = $handles[$phid]->getName();
     }
     return implode(', ', $names);
   }
 
 }