mozreview: allow reviewing a commit message by injecting it as a FileDiff (bug 1248008) r=smacleod draft
authorPiotr Zalewa <pzalewa@mozilla.com>
Wed, 31 Aug 2016 19:03:36 +0200
changeset 10420 fc01e3bb606a0503bc20e965421b9e9e8399b6a9
parent 10419 76de7a741f4cabff73ffc0421358ef3f79a775f6
push id1535
push userbmo:pzalewa@mozilla.com
push dateFri, 17 Feb 2017 12:08:20 +0000
reviewerssmacleod
bugs1248008
mozreview: allow reviewing a commit message by injecting it as a FileDiff (bug 1248008) r=smacleod Currently there is no way to comment on a commit message, reviewers are using bogus diff comments. This patch is injecting the contents of the commit message as a FileDiff. We're using a standard FileDiff. It appears only in ReviewBoard. In every other aspect it works as a file from repository. As files need to be unique it is named "commit-message" with a suffix (4 characters sliced from mercurial hashtag). The suffix is then removed from UI using a CSS hack and JavaScript. There is a `mr:diff-context-loaded` event added to ReviewBoard fork and a corresponding listener in MozReview to change the filename in `#diff_index` list. Please note that the changes doesn't affect existing ReviewRequests. No commit message FileDiff is added in that case. MozReview-Commit-ID: JsBXhGyzUmz
hgext/reviewboard/tests/test-commit-message-as-file.t
hgext/reviewboard/tests/test-commit-message-file-old-structure.t
hgext/reviewboard/tests/test-commits-added.t
hgext/reviewboard/tests/test-commits-deleted-no-obsolescence.t
hgext/reviewboard/tests/test-commits-deleted-obsolescence.t
hgext/reviewboard/tests/test-operation-prevention.t
hgext/reviewboard/tests/test-push-http.t
hgext/reviewboard/tests/test-review-request-approval.t
hgext/reviewboard/tests/test-review-request-closed-discarded.t
hgext/reviewboard/tests/test-review-request-closed-submitted.t
hgext/reviewboard/tests/test-review-request-delegation.t
hgext/reviewboard/tests/test-review-request-delete-draft.t
hgext/reviewboard/tests/test-review-request-summary.t
hgext/reviewboard/tests/test-specify-reviewers.t
hgext/reviewboard/tests/test-unicode.t
pylib/mozreview/mozreview/extension.py
pylib/mozreview/mozreview/extra_data.py
pylib/mozreview/mozreview/resources/batch_review_request.py
pylib/mozreview/mozreview/static/mozreview/css/viewdiff.less
pylib/mozreview/mozreview/static/mozreview/js/init_filediffreviewer.js
pylib/mozreview/mozreview/templates/mozreview/diff-data.html
pylib/mozreview/mozreview/templatetags/mozreview.py
new file mode 100644
--- /dev/null
+++ b/hgext/reviewboard/tests/test-commit-message-as-file.t
@@ -0,0 +1,192 @@
+#require mozreviewdocker
+  $ . $TESTDIR/hgext/reviewboard/tests/helpers.sh
+  $ commonenv
+
+  $ bugzilla create-bug TestProduct TestComponent 'Initial Bug'
+
+  $ cd client
+  $ echo foo > foo.h
+  $ hg commit -A -m 'root commit'
+  adding foo.h
+  $ hg phase --public -r .
+  $ echo foo1 > foo.h
+  $ hg commit -m 'Bug 1 - Foo 1'
+  $ hg push
+  pushing to ssh://$DOCKER_HOSTNAME:$HGPORT6/test-repo
+  (adding commit id to 1 changesets)
+  saved backup bundle to $TESTTMP/client/.hg/strip-backup/8d901902ff4b-c4c7c89c-addcommitid.hg (glob)
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 2 changesets with 2 changes to 1 files
+  remote: recorded push in pushlog
+  submitting 1 changesets for review
+  
+  changeset:  1:4a9cf7e91820
+  summary:    Bug 1 - Foo 1
+  review:     http://$DOCKER_HOSTNAME:$HGPORT1/r/2 (draft)
+  
+  review id:  bz://1/mynick
+  review url: http://$DOCKER_HOSTNAME:$HGPORT1/r/1 (draft)
+  
+  (review requests lack reviewers; visit review url to assign reviewers)
+  
+  publish these review requests now (Yn)?  y
+  (published review request 1)
+
+Commit message file should be created as a first in the set.
+
+  $ reviewboard dumpreview 2
+  id: 2
+  status: pending
+  public: true
+  bugs:
+  - '1'
+  commit: null
+  submitter: default+5
+  summary: Bug 1 - Foo 1
+  description:
+  - Bug 1 - Foo 1
+  - ''
+  - 'MozReview-Commit-ID: 124Bxg'
+  target_people: []
+  extra_data:
+    calculated_trophies: true
+  commit_extra_data:
+    p2rb: true
+    p2rb.author: test
+    p2rb.commit_id: 4a9cf7e9182050f2125ae6c10b21cfc78f6b25ef
+    p2rb.commit_message_filediff_ids: '{"1": 2}'
+    p2rb.commit_message_filename: commit-message-df673
+    p2rb.first_public_ancestor: df67364c205763de5ad1d2c33fa78f87f6618289
+    p2rb.identifier: bz://1/mynick
+    p2rb.is_squashed: false
+  diffs:
+  - id: 3
+    revision: 1
+    base_commit_id: df67364c205763de5ad1d2c33fa78f87f6618289
+    name: diff
+    extra: {}
+    patch:
+    - diff --git a/commit-message-df673 b/commit-message-df673
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-df673
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Foo 1
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
+    - diff --git a/foo.h b/foo.h
+    - '--- a/foo.h'
+    - +++ b/foo.h
+    - '@@ -1,1 +1,1 @@'
+    - -foo
+    - +foo1
+    - ''
+  approved: false
+  approval_failure: A suitable reviewer has not given a "Ship It!"
+
+Amended commit message should result with a changed commit msg filediff content
+
+  $ hg commit --amend -m 'Bug 1 - Foo 2 - amended'
+  saved backup bundle to $TESTTMP/client/.hg/strip-backup/4a9cf7e91820-f76b5126-amend-backup.hg (glob)
+  $ hg push
+  pushing to ssh://$DOCKER_HOSTNAME:$HGPORT6/test-repo
+  (adding commit id to 1 changesets)
+  saved backup bundle to $TESTTMP/client/.hg/strip-backup/2358ed0ad9b5-42479a45-addcommitid.hg (glob)
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 1 changesets with 0 changes to 1 files (+1 heads)
+  remote: recorded push in pushlog
+  submitting 1 changesets for review
+  
+  changeset:  1:ecfec372b48c
+  summary:    Bug 1 - Foo 2 - amended
+  review:     http://$DOCKER_HOSTNAME:$HGPORT1/r/2 (draft)
+  
+  review id:  bz://1/mynick
+  review url: http://$DOCKER_HOSTNAME:$HGPORT1/r/1 (draft)
+  
+  (review requests lack reviewers; visit review url to assign reviewers)
+  
+  publish these review requests now (Yn)?  y
+  (published review request 1)
+
+  $ reviewboard dumpreview 2
+  id: 2
+  status: pending
+  public: true
+  bugs:
+  - '1'
+  commit: null
+  submitter: default+5
+  summary: Bug 1 - Foo 2 - amended
+  description:
+  - Bug 1 - Foo 2 - amended
+  - ''
+  - 'MozReview-Commit-ID: APOgLo'
+  target_people: []
+  extra_data:
+    calculated_trophies: true
+  commit_extra_data:
+    p2rb: true
+    p2rb.author: test
+    p2rb.commit_id: ecfec372b48ce7b49eccf655e422acb4283283e0
+    p2rb.commit_message_filediff_ids: '{"1": 2, "2": 5}'
+    p2rb.commit_message_filename: commit-message-df673
+    p2rb.first_public_ancestor: df67364c205763de5ad1d2c33fa78f87f6618289
+    p2rb.identifier: bz://1/mynick
+    p2rb.is_squashed: false
+  diffs:
+  - id: 3
+    revision: 1
+    base_commit_id: df67364c205763de5ad1d2c33fa78f87f6618289
+    name: diff
+    extra: {}
+    patch:
+    - diff --git a/commit-message-df673 b/commit-message-df673
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-df673
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Foo 1
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
+    - diff --git a/foo.h b/foo.h
+    - '--- a/foo.h'
+    - +++ b/foo.h
+    - '@@ -1,1 +1,1 @@'
+    - -foo
+    - +foo1
+    - ''
+  - id: 5
+    revision: 2
+    base_commit_id: df67364c205763de5ad1d2c33fa78f87f6618289
+    name: diff
+    extra: {}
+    patch:
+    - diff --git a/commit-message-df673 b/commit-message-df673
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-df673
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Foo 2 - amended
+    - +
+    - '+MozReview-Commit-ID: APOgLo'
+    - diff --git a/foo.h b/foo.h
+    - '--- a/foo.h'
+    - +++ b/foo.h
+    - '@@ -1,1 +1,1 @@'
+    - -foo
+    - +foo1
+    - ''
+  approved: false
+  approval_failure: A suitable reviewer has not given a "Ship It!"
+
+Cleanup
+
+  $ mozreview stop
+  stopped 9 containers
new file mode 100644
--- /dev/null
+++ b/hgext/reviewboard/tests/test-commit-message-file-old-structure.t
@@ -0,0 +1,253 @@
+#require mozreviewdocker
+  $ . $TESTDIR/hgext/reviewboard/tests/helpers.sh
+  $ commonenv
+
+  $ bugzilla create-bug TestProduct TestComponent 'Initial Bug'
+
+  $ cd client
+  $ echo foo > foo.h
+  $ hg commit -A -m 'root commit'
+  adding foo.h
+  $ hg phase --public -r .
+  $ echo foo1 > foo.h
+  $ hg commit -m 'Bug 1 - Foo 1'
+  $ hg push
+  pushing to ssh://$DOCKER_HOSTNAME:$HGPORT6/test-repo
+  (adding commit id to 1 changesets)
+  saved backup bundle to $TESTTMP/client/.hg/strip-backup/8d901902ff4b-c4c7c89c-addcommitid.hg (glob)
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 2 changesets with 2 changes to 1 files
+  remote: recorded push in pushlog
+  submitting 1 changesets for review
+  
+  changeset:  1:4a9cf7e91820
+  summary:    Bug 1 - Foo 1
+  review:     http://$DOCKER_HOSTNAME:$HGPORT1/r/2 (draft)
+  
+  review id:  bz://1/mynick
+  review url: http://$DOCKER_HOSTNAME:$HGPORT1/r/1 (draft)
+  
+  (review requests lack reviewers; visit review url to assign reviewers)
+  
+  publish these review requests now (Yn)?  y
+  (published review request 1)
+
+System is recognizing if a ReviewRequest has been created before implementation
+of the commit-message FileDiff feature by fields added to Repository and
+CommitData. To make above ReviewRequest look like an old one we need to
+remove commit message entries from database.
+  $ mozreview sql rbweb <<EOF
+  > DELETE FROM diffviewer_filediff WHERE id=2;
+  > DELETE FROM diffviewer_diffset WHERE id=2;
+  > -- remove has_commit_msg_filediff from parent review_request
+  > UPDATE "mozreview_commitdata" SET
+  > extra_data='{"p2rb.first_public_ancestor": "df67364c205763de5ad1d2c33fa78f87f6618289", "p2rb.is_squashed": true, "p2rb.base_commit": "df67364c205763de5ad1d2c33fa78f87f6618289", "p2rb.identifier": "bz://1/mynick", "p2rb.discard_on_publish_rids": "[]", "p2rb.unpublished_rids": "[]", "p2rb.commits": "[[\"4a9cf7e9182050f2125ae6c10b21cfc78f6b25ef\", 2]]", "p2rb": true}',
+  > draft_extra_data='{"p2rb.first_public_ancestor": "df67364c205763de5ad1d2c33fa78f87f6618289", "p2rb.is_squashed": true, "p2rb.base_commit": "df67364c205763de5ad1d2c33fa78f87f6618289", "p2rb.identifier": "bz://1/mynick", "p2rb.discard_on_publish_rids": "[]", "p2rb.has_temp_diffset": true, "p2rb.unpublished_rids": "[]", "p2rb.commits": "[[\"4a9cf7e9182050f2125ae6c10b21cfc78f6b25ef\", 2]]", "p2rb": true}' WHERE review_request_id=1;
+  > -- remove commit_message_filediff_ids and .commit_message_filename
+  > UPDATE "mozreview_commitdata" SET
+  > extra_data='{"p2rb.first_public_ancestor": "df67364c205763de5ad1d2c33fa78f87f6618289", "p2rb.is_squashed": false, "p2rb.author": "test", "p2rb.identifier": "bz://1/mynick", "p2rb.commit_id": "4a9cf7e9182050f2125ae6c10b21cfc78f6b25ef", "p2rb": true}',
+  > draft_extra_data = '{"p2rb.first_public_ancestor": "df67364c205763de5ad1d2c33fa78f87f6618289", "p2rb.is_squashed": false, "p2rb.author": "test", "p2rb.commit_id": "4a9cf7e9182050f2125ae6c10b21cfc78f6b25ef", "p2rb.identifier": "bz://1/mynick", "p2rb": true}'
+  > WHERE review_request_id=2;
+  > -- remove temp_diffset_id info from repository
+  > UPDATE "scmtools_repository" SET extra_data='{}';
+  > EOF
+
+  $ reviewboard dumpreview 2
+  id: 2
+  status: pending
+  public: true
+  bugs:
+  - '1'
+  commit: null
+  submitter: default+5
+  summary: Bug 1 - Foo 1
+  description:
+  - Bug 1 - Foo 1
+  - ''
+  - 'MozReview-Commit-ID: 124Bxg'
+  target_people: []
+  extra_data:
+    calculated_trophies: true
+  commit_extra_data:
+    p2rb: true
+    p2rb.author: test
+    p2rb.commit_id: 4a9cf7e9182050f2125ae6c10b21cfc78f6b25ef
+    p2rb.first_public_ancestor: df67364c205763de5ad1d2c33fa78f87f6618289
+    p2rb.identifier: bz://1/mynick
+    p2rb.is_squashed: false
+  diffs:
+  - id: 3
+    revision: 1
+    base_commit_id: df67364c205763de5ad1d2c33fa78f87f6618289
+    name: diff
+    extra: {}
+    patch:
+    - diff --git a/foo.h b/foo.h
+    - '--- a/foo.h'
+    - +++ b/foo.h
+    - '@@ -1,1 +1,1 @@'
+    - -foo
+    - +foo1
+    - ''
+  approved: false
+  approval_failure: A suitable reviewer has not given a "Ship It!"
+
+Ammending commit should not create a commit message FileDiff
+  $ echo foo2 > foo.h
+  $ hg commit --amend -m 'Bug 1 - Foo 2'
+  saved backup bundle to $TESTTMP/client/.hg/strip-backup/4a9cf7e91820-a2ac3612-amend-backup.hg (glob)
+  $ hg push -y
+  pushing to ssh://$DOCKER_HOSTNAME:$HGPORT6/test-repo
+  (adding commit id to 1 changesets)
+  saved backup bundle to $TESTTMP/client/.hg/strip-backup/ecf3ba3ae44d-df5ba11c-addcommitid.hg (glob)
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 1 changesets with 1 changes to 1 files (+1 heads)
+  remote: recorded push in pushlog
+  submitting 1 changesets for review
+  
+  changeset:  1:9d4207e39e13
+  summary:    Bug 1 - Foo 2
+  review:     http://$DOCKER_HOSTNAME:$HGPORT1/r/2 (draft)
+  
+  review id:  bz://1/mynick
+  review url: http://$DOCKER_HOSTNAME:$HGPORT1/r/1 (draft)
+  
+  (review requests lack reviewers; visit review url to assign reviewers)
+  
+  publish these review requests now (Yn)?  y
+  (published review request 1)
+
+  $ reviewboard dumpreview 2
+  id: 2
+  status: pending
+  public: true
+  bugs:
+  - '1'
+  commit: null
+  submitter: default+5
+  summary: Bug 1 - Foo 2
+  description:
+  - Bug 1 - Foo 2
+  - ''
+  - 'MozReview-Commit-ID: APOgLo'
+  target_people: []
+  extra_data:
+    calculated_trophies: true
+  commit_extra_data:
+    p2rb: true
+    p2rb.author: test
+    p2rb.commit_id: 9d4207e39e136eb5965ae29c2014e6334861b4db
+    p2rb.first_public_ancestor: df67364c205763de5ad1d2c33fa78f87f6618289
+    p2rb.identifier: bz://1/mynick
+    p2rb.is_squashed: false
+  diffs:
+  - id: 3
+    revision: 1
+    base_commit_id: df67364c205763de5ad1d2c33fa78f87f6618289
+    name: diff
+    extra: {}
+    patch:
+    - diff --git a/foo.h b/foo.h
+    - '--- a/foo.h'
+    - +++ b/foo.h
+    - '@@ -1,1 +1,1 @@'
+    - -foo
+    - +foo1
+    - ''
+  - id: 5
+    revision: 2
+    base_commit_id: df67364c205763de5ad1d2c33fa78f87f6618289
+    name: diff
+    extra: {}
+    patch:
+    - diff --git a/foo.h b/foo.h
+    - '--- a/foo.h'
+    - +++ b/foo.h
+    - '@@ -1,1 +1,1 @@'
+    - -foo
+    - +foo2
+    - ''
+  approved: false
+  approval_failure: A suitable reviewer has not given a "Ship It!"
+
+Entirely new review request within the same parent also has no commit message
+FileDiff.
+  $ echo bar > bar.txt
+  $ hg commit -A -m 'Bug 1 - Bar'
+  adding bar.txt
+  $ hg push -y
+  pushing to ssh://$DOCKER_HOSTNAME:$HGPORT6/test-repo
+  searching for changes
+  remote: adding changesets
+  remote: adding manifests
+  remote: adding file changes
+  remote: added 1 changesets with 1 changes to 1 files
+  remote: recorded push in pushlog
+  submitting 2 changesets for review
+  
+  changeset:  1:9d4207e39e13
+  summary:    Bug 1 - Foo 2
+  review:     http://$DOCKER_HOSTNAME:$HGPORT1/r/2
+  
+  changeset:  2:fda2e193ec23
+  summary:    Bug 1 - Bar
+  review:     http://$DOCKER_HOSTNAME:$HGPORT1/r/3 (draft)
+  
+  review id:  bz://1/mynick
+  review url: http://$DOCKER_HOSTNAME:$HGPORT1/r/1 (draft)
+  
+  (review requests lack reviewers; visit review url to assign reviewers)
+  
+  publish these review requests now (Yn)?  y
+  (published review request 1)
+
+  $ reviewboard dumpreview 3
+  id: 3
+  status: pending
+  public: true
+  bugs:
+  - '1'
+  commit: null
+  submitter: default+5
+  summary: Bug 1 - Bar
+  description:
+  - Bug 1 - Bar
+  - ''
+  - 'MozReview-Commit-ID: F63vXs'
+  target_people: []
+  extra_data:
+    calculated_trophies: true
+  commit_extra_data:
+    p2rb: true
+    p2rb.author: test
+    p2rb.commit_id: fda2e193ec23a353558ab5b384277e8e9c21fe83
+    p2rb.first_public_ancestor: df67364c205763de5ad1d2c33fa78f87f6618289
+    p2rb.identifier: bz://1/mynick
+    p2rb.is_squashed: false
+  diffs:
+  - id: 7
+    revision: 1
+    base_commit_id: 9d4207e39e136eb5965ae29c2014e6334861b4db
+    name: diff
+    extra: {}
+    patch:
+    - diff --git a/bar.txt b/bar.txt
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/bar.txt
+    - '@@ -0,0 +1,1 @@'
+    - +bar
+    - ''
+  approved: false
+  approval_failure: A suitable reviewer has not given a "Ship It!"
+
+Cleanup
+
+  $ mozreview stop
+  stopped 9 containers
--- a/hgext/reviewboard/tests/test-commits-added.t
+++ b/hgext/reviewboard/tests/test-commits-added.t
@@ -92,16 +92,17 @@ The parent review request should be upda
   summary: ''
   description: ''
   target_people: []
   extra_data: {}
   commit_extra_data:
     p2rb: true
     p2rb.discard_on_publish_rids: '[]'
     p2rb.first_public_ancestor: 7c5bdf0cec4a90edb36300f8f3679857f46db829
+    p2rb.has_commit_message_filediff: true
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: true
     p2rb.unpublished_rids: '[2, 3, 4]'
   diffs: []
   approved: false
   approval_failure: The review request is not public.
   draft:
     bugs:
@@ -114,21 +115,22 @@ The parent review request should be upda
       p2rb.reviewer_map: '{"3": [], "2": []}'
     commit_extra_data:
       p2rb: true
       p2rb.base_commit: 7c5bdf0cec4a90edb36300f8f3679857f46db829
       p2rb.commits: '[["98467d80785ec84dd871f213c167ed704a6d974d", 2], ["3a446ae4382006c43cdfa5aa33c494f582736f35",
         3], ["1ec9946fd47ff9b5cb07e9d9c8b4d393b688e01b", 4]]'
       p2rb.discard_on_publish_rids: '[]'
       p2rb.first_public_ancestor: 7c5bdf0cec4a90edb36300f8f3679857f46db829
+      p2rb.has_commit_message_filediff: true
       p2rb.identifier: bz://1/mynick
       p2rb.is_squashed: true
       p2rb.unpublished_rids: '[]'
     diffs:
-    - id: 4
+    - id: 5
       revision: 1
       base_commit_id: 7c5bdf0cec4a90edb36300f8f3679857f46db829
       name: diff
       extra: {}
       patch:
       - diff --git a/foo b/foo
       - '--- a/foo'
       - +++ b/foo
--- a/hgext/reviewboard/tests/test-commits-deleted-no-obsolescence.t
+++ b/hgext/reviewboard/tests/test-commits-deleted-no-obsolescence.t
@@ -126,16 +126,17 @@ Review request 6 should be added to the 
   commit_extra_data:
     p2rb: true
     p2rb.base_commit: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     p2rb.commits: '[["0b3e14fe3ff19019110705e72dcf563c0ef551f6", 2], ["bce658a3f6d6aa04bf5c449e0e779e839de4690e",
       3], ["713878e22d952d478e88bfdef897fdfc73060351", 4], ["4d0f846364eb509a1b6ae3294f05439101f6e7d3",
       5], ["4e50148c492dde95397cd666f2d4e4ad4fd2176f", 6]]'
     p2rb.discard_on_publish_rids: '[6]'
     p2rb.first_public_ancestor: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
+    p2rb.has_commit_message_filediff: true
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: true
     p2rb.unpublished_rids: '[]'
   diffs:
   - id: 1
     revision: 1
     base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     name: diff
@@ -187,21 +188,22 @@ Review request 6 should be added to the 
     commit_extra_data:
       p2rb: true
       p2rb.base_commit: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
       p2rb.commits: '[["0b3e14fe3ff19019110705e72dcf563c0ef551f6", 2], ["bce658a3f6d6aa04bf5c449e0e779e839de4690e",
         3], ["713878e22d952d478e88bfdef897fdfc73060351", 4], ["4d0f846364eb509a1b6ae3294f05439101f6e7d3",
         5]]'
       p2rb.discard_on_publish_rids: '[]'
       p2rb.first_public_ancestor: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
+      p2rb.has_commit_message_filediff: true
       p2rb.identifier: bz://1/mynick
       p2rb.is_squashed: true
       p2rb.unpublished_rids: '[]'
     diffs:
-    - id: 7
+    - id: 8
       revision: 2
       base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
       name: diff
       extra: {}
       patch:
       - diff --git a/foo1 b/foo1
       - new file mode 100644
       - '--- /dev/null'
@@ -247,26 +249,36 @@ Review 6 should be marked as discarded
   - 'MozReview-Commit-ID: JmjAjw'
   target_people: []
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: 4e50148c492dde95397cd666f2d4e4ad4fd2176f
+    p2rb.commit_message_filediff_ids: '{"1": 14}'
+    p2rb.commit_message_filename: commit-message-4d0f8
     p2rb.first_public_ancestor: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 6
+  - id: 7
     revision: 1
     base_commit_id: 4d0f846364eb509a1b6ae3294f05439101f6e7d3
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-4d0f8 b/commit-message-4d0f8
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-4d0f8
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Foo 5
+    - +
+    - '+MozReview-Commit-ID: JmjAjw'
     - diff --git a/foo5 b/foo5
     - new file mode 100644
     - '--- /dev/null'
     - +++ b/foo5
     - '@@ -0,0 +1,1 @@'
     - +foo5
     - ''
   approved: false
@@ -322,26 +334,36 @@ The review request corresponding to the 
   - 'MozReview-Commit-ID: 124Bxg'
   target_people: []
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: 0b3e14fe3ff19019110705e72dcf563c0ef551f6
+    p2rb.commit_message_filediff_ids: '{"1": 6}'
+    p2rb.commit_message_filename: commit-message-93d94
     p2rb.first_public_ancestor: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 2
+  - id: 3
     revision: 1
     base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-93d94 b/commit-message-93d94
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-93d94
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Foo 1
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
     - diff --git a/foo1 b/foo1
     - new file mode 100644
     - '--- /dev/null'
     - +++ b/foo1
     - '@@ -0,0 +1,1 @@'
     - +foo1
     - ''
   approved: false
@@ -366,16 +388,17 @@ Review request 2 should be marked as dis
   commit_extra_data:
     p2rb: true
     p2rb.base_commit: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     p2rb.commits: '[["0b3e14fe3ff19019110705e72dcf563c0ef551f6", 2], ["bce658a3f6d6aa04bf5c449e0e779e839de4690e",
       3], ["713878e22d952d478e88bfdef897fdfc73060351", 4], ["4d0f846364eb509a1b6ae3294f05439101f6e7d3",
       5]]'
     p2rb.discard_on_publish_rids: '[2]'
     p2rb.first_public_ancestor: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
+    p2rb.has_commit_message_filediff: true
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: true
     p2rb.unpublished_rids: '[]'
   diffs:
   - id: 1
     revision: 1
     base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     name: diff
@@ -407,17 +430,17 @@ Review request 2 should be marked as dis
     - +foo4
     - diff --git a/foo5 b/foo5
     - new file mode 100644
     - '--- /dev/null'
     - +++ b/foo5
     - '@@ -0,0 +1,1 @@'
     - +foo5
     - ''
-  - id: 7
+  - id: 8
     revision: 2
     base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     name: diff
     extra: {}
     patch:
     - diff --git a/foo1 b/foo1
     - new file mode 100644
     - '--- /dev/null'
@@ -457,21 +480,22 @@ Review request 2 should be marked as dis
       p2rb.reviewer_map: '{"3": [], "2": [], "5": [], "4": []}'
     commit_extra_data:
       p2rb: true
       p2rb.base_commit: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
       p2rb.commits: '[["eeb6d49dcb0950d771959358f662cf2e5ddc9dc1", 3], ["607f375f35c0866a8e08bc1d6aaecc6ad259ed6e",
         4], ["81ee86efd38ff60717aeeeff153292e84e58be0b", 5]]'
       p2rb.discard_on_publish_rids: '[]'
       p2rb.first_public_ancestor: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
+      p2rb.has_commit_message_filediff: true
       p2rb.identifier: bz://1/mynick
       p2rb.is_squashed: true
       p2rb.unpublished_rids: '[]'
     diffs:
-    - id: 8
+    - id: 9
       revision: 3
       base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
       name: diff
       extra: {}
       patch:
       - diff --git a/foo2 b/foo2
       - new file mode 100644
       - '--- /dev/null'
@@ -544,16 +568,17 @@ The parent review should have been updat
     p2rb.reviewer_map: '{"3": [], "2": [], "5": [], "4": []}'
   commit_extra_data:
     p2rb: true
     p2rb.base_commit: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     p2rb.commits: '[["eeb6d49dcb0950d771959358f662cf2e5ddc9dc1", 3], ["607f375f35c0866a8e08bc1d6aaecc6ad259ed6e",
       4], ["81ee86efd38ff60717aeeeff153292e84e58be0b", 5]]'
     p2rb.discard_on_publish_rids: '[4]'
     p2rb.first_public_ancestor: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
+    p2rb.has_commit_message_filediff: true
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: true
     p2rb.unpublished_rids: '[]'
   diffs:
   - id: 1
     revision: 1
     base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     name: diff
@@ -585,17 +610,17 @@ The parent review should have been updat
     - +foo4
     - diff --git a/foo5 b/foo5
     - new file mode 100644
     - '--- /dev/null'
     - +++ b/foo5
     - '@@ -0,0 +1,1 @@'
     - +foo5
     - ''
-  - id: 7
+  - id: 8
     revision: 2
     base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     name: diff
     extra: {}
     patch:
     - diff --git a/foo1 b/foo1
     - new file mode 100644
     - '--- /dev/null'
@@ -616,17 +641,17 @@ The parent review should have been updat
     - +foo3
     - diff --git a/foo4 b/foo4
     - new file mode 100644
     - '--- /dev/null'
     - +++ b/foo4
     - '@@ -0,0 +1,1 @@'
     - +foo4
     - ''
-  - id: 8
+  - id: 9
     revision: 3
     base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     name: diff
     extra: {}
     patch:
     - diff --git a/foo2 b/foo2
     - new file mode 100644
     - '--- /dev/null'
@@ -660,21 +685,22 @@ The parent review should have been updat
       p2rb.reviewer_map: '{"3": [], "5": [], "4": []}'
     commit_extra_data:
       p2rb: true
       p2rb.base_commit: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
       p2rb.commits: '[["eeb6d49dcb0950d771959358f662cf2e5ddc9dc1", 3], ["a27a94c54524d4331dec2f92f647067bfd6dfbd4",
         5]]'
       p2rb.discard_on_publish_rids: '[]'
       p2rb.first_public_ancestor: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
+      p2rb.has_commit_message_filediff: true
       p2rb.identifier: bz://1/mynick
       p2rb.is_squashed: true
       p2rb.unpublished_rids: '[]'
     diffs:
-    - id: 12
+    - id: 13
       revision: 4
       base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
       name: diff
       extra: {}
       patch:
       - diff --git a/foo2 b/foo2
       - new file mode 100644
       - '--- /dev/null'
@@ -738,16 +764,17 @@ recycling behavior when commit IDs are p
     p2rb.reviewer_map: '{"3": [], "2": [], "5": [], "4": []}'
   commit_extra_data:
     p2rb: true
     p2rb.base_commit: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     p2rb.commits: '[["eeb6d49dcb0950d771959358f662cf2e5ddc9dc1", 3], ["607f375f35c0866a8e08bc1d6aaecc6ad259ed6e",
       4], ["81ee86efd38ff60717aeeeff153292e84e58be0b", 5]]'
     p2rb.discard_on_publish_rids: '[5]'
     p2rb.first_public_ancestor: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
+    p2rb.has_commit_message_filediff: true
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: true
     p2rb.unpublished_rids: '[]'
   diffs:
   - id: 1
     revision: 1
     base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     name: diff
@@ -779,17 +806,17 @@ recycling behavior when commit IDs are p
     - +foo4
     - diff --git a/foo5 b/foo5
     - new file mode 100644
     - '--- /dev/null'
     - +++ b/foo5
     - '@@ -0,0 +1,1 @@'
     - +foo5
     - ''
-  - id: 7
+  - id: 8
     revision: 2
     base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     name: diff
     extra: {}
     patch:
     - diff --git a/foo1 b/foo1
     - new file mode 100644
     - '--- /dev/null'
@@ -810,17 +837,17 @@ recycling behavior when commit IDs are p
     - +foo3
     - diff --git a/foo4 b/foo4
     - new file mode 100644
     - '--- /dev/null'
     - +++ b/foo4
     - '@@ -0,0 +1,1 @@'
     - +foo4
     - ''
-  - id: 8
+  - id: 9
     revision: 3
     base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     name: diff
     extra: {}
     patch:
     - diff --git a/foo2 b/foo2
     - new file mode 100644
     - '--- /dev/null'
@@ -854,21 +881,22 @@ recycling behavior when commit IDs are p
       p2rb.reviewer_map: '{"3": [], "5": []}'
     commit_extra_data:
       p2rb: true
       p2rb.base_commit: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
       p2rb.commits: '[["eeb6d49dcb0950d771959358f662cf2e5ddc9dc1", 3], ["3b99865d1bab8480235d913f4bcfc951fd9e3032",
         4]]'
       p2rb.discard_on_publish_rids: '[]'
       p2rb.first_public_ancestor: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
+      p2rb.has_commit_message_filediff: true
       p2rb.identifier: bz://1/mynick
       p2rb.is_squashed: true
       p2rb.unpublished_rids: '[]'
     diffs:
-    - id: 14
+    - id: 15
       revision: 4
       base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
       name: diff
       extra: {}
       patch:
       - diff --git a/foo2 b/foo2
       - new file mode 100644
       - '--- /dev/null'
--- a/hgext/reviewboard/tests/test-commits-deleted-obsolescence.t
+++ b/hgext/reviewboard/tests/test-commits-deleted-obsolescence.t
@@ -132,16 +132,17 @@ on publish.
   commit_extra_data:
     p2rb: true
     p2rb.base_commit: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     p2rb.commits: '[["0b3e14fe3ff19019110705e72dcf563c0ef551f6", 2], ["bce658a3f6d6aa04bf5c449e0e779e839de4690e",
       3], ["713878e22d952d478e88bfdef897fdfc73060351", 4], ["4d0f846364eb509a1b6ae3294f05439101f6e7d3",
       5], ["4e50148c492dde95397cd666f2d4e4ad4fd2176f", 6]]'
     p2rb.discard_on_publish_rids: '[6]'
     p2rb.first_public_ancestor: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
+    p2rb.has_commit_message_filediff: true
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: true
     p2rb.unpublished_rids: '[]'
   diffs:
   - id: 1
     revision: 1
     base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     name: diff
@@ -193,21 +194,22 @@ on publish.
     commit_extra_data:
       p2rb: true
       p2rb.base_commit: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
       p2rb.commits: '[["0b3e14fe3ff19019110705e72dcf563c0ef551f6", 2], ["bce658a3f6d6aa04bf5c449e0e779e839de4690e",
         3], ["713878e22d952d478e88bfdef897fdfc73060351", 4], ["4d0f846364eb509a1b6ae3294f05439101f6e7d3",
         5]]'
       p2rb.discard_on_publish_rids: '[]'
       p2rb.first_public_ancestor: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
+      p2rb.has_commit_message_filediff: true
       p2rb.identifier: bz://1/mynick
       p2rb.is_squashed: true
       p2rb.unpublished_rids: '[]'
     diffs:
-    - id: 7
+    - id: 8
       revision: 2
       base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
       name: diff
       extra: {}
       patch:
       - diff --git a/foo1 b/foo1
       - new file mode 100644
       - '--- /dev/null'
@@ -255,16 +257,17 @@ The parent review should have dropped th
   commit_extra_data:
     p2rb: true
     p2rb.base_commit: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     p2rb.commits: '[["0b3e14fe3ff19019110705e72dcf563c0ef551f6", 2], ["bce658a3f6d6aa04bf5c449e0e779e839de4690e",
       3], ["713878e22d952d478e88bfdef897fdfc73060351", 4], ["4d0f846364eb509a1b6ae3294f05439101f6e7d3",
       5]]'
     p2rb.discard_on_publish_rids: '[]'
     p2rb.first_public_ancestor: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
+    p2rb.has_commit_message_filediff: true
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: true
     p2rb.unpublished_rids: '[]'
   diffs:
   - id: 1
     revision: 1
     base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     name: diff
@@ -296,17 +299,17 @@ The parent review should have dropped th
     - +foo4
     - diff --git a/foo5 b/foo5
     - new file mode 100644
     - '--- /dev/null'
     - +++ b/foo5
     - '@@ -0,0 +1,1 @@'
     - +foo5
     - ''
-  - id: 7
+  - id: 8
     revision: 2
     base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     name: diff
     extra: {}
     patch:
     - diff --git a/foo1 b/foo1
     - new file mode 100644
     - '--- /dev/null'
@@ -352,26 +355,36 @@ Review 6 should be marked as discarded
   - 'MozReview-Commit-ID: JmjAjw'
   target_people: []
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: 4e50148c492dde95397cd666f2d4e4ad4fd2176f
+    p2rb.commit_message_filediff_ids: '{"1": 14}'
+    p2rb.commit_message_filename: commit-message-4d0f8
     p2rb.first_public_ancestor: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 6
+  - id: 7
     revision: 1
     base_commit_id: 4d0f846364eb509a1b6ae3294f05439101f6e7d3
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-4d0f8 b/commit-message-4d0f8
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-4d0f8
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Foo 5
+    - +
+    - '+MozReview-Commit-ID: JmjAjw'
     - diff --git a/foo5 b/foo5
     - new file mode 100644
     - '--- /dev/null'
     - +++ b/foo5
     - '@@ -0,0 +1,1 @@'
     - +foo5
     - ''
   approved: false
@@ -429,16 +442,17 @@ on publish.
   commit_extra_data:
     p2rb: true
     p2rb.base_commit: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     p2rb.commits: '[["0b3e14fe3ff19019110705e72dcf563c0ef551f6", 2], ["bce658a3f6d6aa04bf5c449e0e779e839de4690e",
       3], ["713878e22d952d478e88bfdef897fdfc73060351", 4], ["4d0f846364eb509a1b6ae3294f05439101f6e7d3",
       5]]'
     p2rb.discard_on_publish_rids: '[2]'
     p2rb.first_public_ancestor: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
+    p2rb.has_commit_message_filediff: true
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: true
     p2rb.unpublished_rids: '[]'
   diffs:
   - id: 1
     revision: 1
     base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     name: diff
@@ -470,17 +484,17 @@ on publish.
     - +foo4
     - diff --git a/foo5 b/foo5
     - new file mode 100644
     - '--- /dev/null'
     - +++ b/foo5
     - '@@ -0,0 +1,1 @@'
     - +foo5
     - ''
-  - id: 7
+  - id: 8
     revision: 2
     base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     name: diff
     extra: {}
     patch:
     - diff --git a/foo1 b/foo1
     - new file mode 100644
     - '--- /dev/null'
@@ -520,21 +534,22 @@ on publish.
       p2rb.reviewer_map: '{"3": [], "2": [], "5": [], "4": []}'
     commit_extra_data:
       p2rb: true
       p2rb.base_commit: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
       p2rb.commits: '[["eeb6d49dcb0950d771959358f662cf2e5ddc9dc1", 3], ["607f375f35c0866a8e08bc1d6aaecc6ad259ed6e",
         4], ["81ee86efd38ff60717aeeeff153292e84e58be0b", 5]]'
       p2rb.discard_on_publish_rids: '[]'
       p2rb.first_public_ancestor: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
+      p2rb.has_commit_message_filediff: true
       p2rb.identifier: bz://1/mynick
       p2rb.is_squashed: true
       p2rb.unpublished_rids: '[]'
     diffs:
-    - id: 8
+    - id: 9
       revision: 3
       base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
       name: diff
       extra: {}
       patch:
       - diff --git a/foo2 b/foo2
       - new file mode 100644
       - '--- /dev/null'
@@ -574,26 +589,36 @@ The dropped commit should now be discard
   - 'MozReview-Commit-ID: 124Bxg'
   target_people: []
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: 0b3e14fe3ff19019110705e72dcf563c0ef551f6
+    p2rb.commit_message_filediff_ids: '{"1": 6}'
+    p2rb.commit_message_filename: commit-message-93d94
     p2rb.first_public_ancestor: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 2
+  - id: 3
     revision: 1
     base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-93d94 b/commit-message-93d94
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-93d94
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Foo 1
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
     - diff --git a/foo1 b/foo1
     - new file mode 100644
     - '--- /dev/null'
     - +++ b/foo1
     - '@@ -0,0 +1,1 @@'
     - +foo1
     - ''
   approved: false
@@ -646,16 +671,17 @@ The parent review should have been updat
     p2rb.reviewer_map: '{"3": [], "5": [], "4": []}'
   commit_extra_data:
     p2rb: true
     p2rb.base_commit: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     p2rb.commits: '[["eeb6d49dcb0950d771959358f662cf2e5ddc9dc1", 3], ["a27a94c54524d4331dec2f92f647067bfd6dfbd4",
       5]]'
     p2rb.discard_on_publish_rids: '[]'
     p2rb.first_public_ancestor: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
+    p2rb.has_commit_message_filediff: true
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: true
     p2rb.unpublished_rids: '[]'
   diffs:
   - id: 1
     revision: 1
     base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     name: diff
@@ -687,17 +713,17 @@ The parent review should have been updat
     - +foo4
     - diff --git a/foo5 b/foo5
     - new file mode 100644
     - '--- /dev/null'
     - +++ b/foo5
     - '@@ -0,0 +1,1 @@'
     - +foo5
     - ''
-  - id: 7
+  - id: 8
     revision: 2
     base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     name: diff
     extra: {}
     patch:
     - diff --git a/foo1 b/foo1
     - new file mode 100644
     - '--- /dev/null'
@@ -718,17 +744,17 @@ The parent review should have been updat
     - +foo3
     - diff --git a/foo4 b/foo4
     - new file mode 100644
     - '--- /dev/null'
     - +++ b/foo4
     - '@@ -0,0 +1,1 @@'
     - +foo4
     - ''
-  - id: 8
+  - id: 9
     revision: 3
     base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     name: diff
     extra: {}
     patch:
     - diff --git a/foo2 b/foo2
     - new file mode 100644
     - '--- /dev/null'
@@ -743,17 +769,17 @@ The parent review should have been updat
     - +foo3
     - diff --git a/foo4 b/foo4
     - new file mode 100644
     - '--- /dev/null'
     - +++ b/foo4
     - '@@ -0,0 +1,1 @@'
     - +foo4
     - ''
-  - id: 12
+  - id: 13
     revision: 4
     base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     name: diff
     extra: {}
     patch:
     - diff --git a/foo2 b/foo2
     - new file mode 100644
     - '--- /dev/null'
@@ -817,16 +843,17 @@ because the new commit is logically diff
     p2rb.reviewer_map: '{"3": [], "5": [], "4": []}'
   commit_extra_data:
     p2rb: true
     p2rb.base_commit: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     p2rb.commits: '[["eeb6d49dcb0950d771959358f662cf2e5ddc9dc1", 3], ["a27a94c54524d4331dec2f92f647067bfd6dfbd4",
       5]]'
     p2rb.discard_on_publish_rids: '[5]'
     p2rb.first_public_ancestor: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
+    p2rb.has_commit_message_filediff: true
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: true
     p2rb.unpublished_rids: '[7]'
   diffs:
   - id: 1
     revision: 1
     base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     name: diff
@@ -858,17 +885,17 @@ because the new commit is logically diff
     - +foo4
     - diff --git a/foo5 b/foo5
     - new file mode 100644
     - '--- /dev/null'
     - +++ b/foo5
     - '@@ -0,0 +1,1 @@'
     - +foo5
     - ''
-  - id: 7
+  - id: 8
     revision: 2
     base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     name: diff
     extra: {}
     patch:
     - diff --git a/foo1 b/foo1
     - new file mode 100644
     - '--- /dev/null'
@@ -889,17 +916,17 @@ because the new commit is logically diff
     - +foo3
     - diff --git a/foo4 b/foo4
     - new file mode 100644
     - '--- /dev/null'
     - +++ b/foo4
     - '@@ -0,0 +1,1 @@'
     - +foo4
     - ''
-  - id: 8
+  - id: 9
     revision: 3
     base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     name: diff
     extra: {}
     patch:
     - diff --git a/foo2 b/foo2
     - new file mode 100644
     - '--- /dev/null'
@@ -914,17 +941,17 @@ because the new commit is logically diff
     - +foo3
     - diff --git a/foo4 b/foo4
     - new file mode 100644
     - '--- /dev/null'
     - +++ b/foo4
     - '@@ -0,0 +1,1 @@'
     - +foo4
     - ''
-  - id: 12
+  - id: 13
     revision: 4
     base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     name: diff
     extra: {}
     patch:
     - diff --git a/foo2 b/foo2
     - new file mode 100644
     - '--- /dev/null'
@@ -952,21 +979,22 @@ because the new commit is logically diff
       p2rb.reviewer_map: '{"3": [], "5": []}'
     commit_extra_data:
       p2rb: true
       p2rb.base_commit: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
       p2rb.commits: '[["eeb6d49dcb0950d771959358f662cf2e5ddc9dc1", 3], ["3b99865d1bab8480235d913f4bcfc951fd9e3032",
         7]]'
       p2rb.discard_on_publish_rids: '[]'
       p2rb.first_public_ancestor: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
+      p2rb.has_commit_message_filediff: true
       p2rb.identifier: bz://1/mynick
       p2rb.is_squashed: true
       p2rb.unpublished_rids: '[]'
     diffs:
-    - id: 14
+    - id: 15
       revision: 5
       base_commit_id: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
       name: diff
       extra: {}
       patch:
       - diff --git a/foo2 b/foo2
       - new file mode 100644
       - '--- /dev/null'
@@ -998,52 +1026,78 @@ Review request 5 (whose commit was delet
   - 'MozReview-Commit-ID: F63vXs'
   target_people: []
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: a27a94c54524d4331dec2f92f647067bfd6dfbd4
+    p2rb.commit_message_filediff_ids: '{"1": 12, "3": 31, "2": 27}'
+    p2rb.commit_message_filename: commit-message-71387
     p2rb.first_public_ancestor: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 5
+  - id: 6
     revision: 1
     base_commit_id: 713878e22d952d478e88bfdef897fdfc73060351
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-71387 b/commit-message-71387
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-71387
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Foo 4
+    - +
+    - '+MozReview-Commit-ID: F63vXs'
     - diff --git a/foo4 b/foo4
     - new file mode 100644
     - '--- /dev/null'
     - +++ b/foo4
     - '@@ -0,0 +1,1 @@'
     - +foo4
     - ''
-  - id: 11
+  - id: 12
     revision: 2
     base_commit_id: 607f375f35c0866a8e08bc1d6aaecc6ad259ed6e
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-71387 b/commit-message-71387
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-71387
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Foo 4
+    - +
+    - '+MozReview-Commit-ID: F63vXs'
     - diff --git a/foo4 b/foo4
     - new file mode 100644
     - '--- /dev/null'
     - +++ b/foo4
     - '@@ -0,0 +1,1 @@'
     - +foo4
     - ''
-  - id: 13
+  - id: 14
     revision: 3
     base_commit_id: eeb6d49dcb0950d771959358f662cf2e5ddc9dc1
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-71387 b/commit-message-71387
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-71387
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Foo 4
+    - +
+    - '+MozReview-Commit-ID: F63vXs'
     - diff --git a/foo4 b/foo4
     - new file mode 100644
     - '--- /dev/null'
     - +++ b/foo4
     - '@@ -0,0 +1,1 @@'
     - +foo4
     - ''
   approved: false
@@ -1057,16 +1111,17 @@ Review request 5 (whose commit was delet
   commit: null
   submitter: default+5
   summary: ''
   description: ''
   target_people: []
   extra_data: {}
   commit_extra_data:
     p2rb: true
+    p2rb.commit_message_filename: commit-message-eeb6d
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs: []
   approved: false
   approval_failure: The review request is not public.
   draft:
     bugs:
     - '1'
@@ -1077,26 +1132,35 @@ Review request 5 (whose commit was delet
     - ''
     - 'MozReview-Commit-ID: OTOPw0'
     target_people: []
     extra: {}
     commit_extra_data:
       p2rb: true
       p2rb.author: test
       p2rb.commit_id: 3b99865d1bab8480235d913f4bcfc951fd9e3032
+      p2rb.commit_message_filediff_ids: '{"1": 35}'
       p2rb.first_public_ancestor: 93d9429b41ecf0d2ad8c62b6ea26686dd20330f4
       p2rb.identifier: bz://1/mynick
       p2rb.is_squashed: false
     diffs:
-    - id: 15
+    - id: 16
       revision: 1
       base_commit_id: eeb6d49dcb0950d771959358f662cf2e5ddc9dc1
       name: diff
       extra: {}
       patch:
+      - diff --git a/commit-message-eeb6d b/commit-message-eeb6d
+      - new file mode 100644
+      - '--- /dev/null'
+      - +++ b/commit-message-eeb6d
+      - '@@ -0,0 +1,3 @@'
+      - +Bug 1 - Foo 6
+      - +
+      - '+MozReview-Commit-ID: OTOPw0'
       - diff --git a/foo6 b/foo6
       - new file mode 100644
       - '--- /dev/null'
       - +++ b/foo6
       - '@@ -0,0 +1,1 @@'
       - +foo6
       - ''
 
--- a/hgext/reviewboard/tests/test-operation-prevention.t
+++ b/hgext/reviewboard/tests/test-operation-prevention.t
@@ -47,16 +47,17 @@ Publishing the parent should succeed.
     calculated_trophies: true
     p2rb.reviewer_map: '{"2": []}'
   commit_extra_data:
     p2rb: true
     p2rb.base_commit: 3a9f6899ef84c99841f546030b036d0124a863cf
     p2rb.commits: '[["4f4c73d9c6594a0a800a82758ceb6fb12a6b9f83", 2]]'
     p2rb.discard_on_publish_rids: '[]'
     p2rb.first_public_ancestor: 3a9f6899ef84c99841f546030b036d0124a863cf
+    p2rb.has_commit_message_filediff: true
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: true
     p2rb.unpublished_rids: '[]'
   diffs:
   - id: 1
     revision: 1
     base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
     name: diff
@@ -87,26 +88,36 @@ Publishing the parent should succeed.
   - 'MozReview-Commit-ID: 124Bxg'
   target_people: []
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: 4f4c73d9c6594a0a800a82758ceb6fb12a6b9f83
+    p2rb.commit_message_filediff_ids: '{"1": 2}'
+    p2rb.commit_message_filename: commit-message-3a9f6
     p2rb.first_public_ancestor: 3a9f6899ef84c99841f546030b036d0124a863cf
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 2
+  - id: 3
     revision: 1
     base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-3a9f6 b/commit-message-3a9f6
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-3a9f6
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Initial commit to review
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo
     - +initial
     - ''
   approved: false
--- a/hgext/reviewboard/tests/test-push-http.t
+++ b/hgext/reviewboard/tests/test-push-http.t
@@ -372,16 +372,17 @@ Test creating a review via HTTP
     p2rb.reviewer_map: '{}'
   commit_extra_data:
     p2rb: true
     p2rb.base_commit: 8d7f5c4152d8f67d67500d3b92903e365c0122f1
     p2rb.commits: '[["9d326020e0dcd3e421680e4b78bf80c9e30df0e6", 2], ["c6548fe145857055779b23d94ef3f911e8d261b0",
       3]]'
     p2rb.discard_on_publish_rids: '[]'
     p2rb.first_public_ancestor: 8d7f5c4152d8f67d67500d3b92903e365c0122f1
+    p2rb.has_commit_message_filediff: true
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: true
     p2rb.unpublished_rids: '[]'
   diffs:
   - id: 1
     revision: 1
     base_commit_id: 8d7f5c4152d8f67d67500d3b92903e365c0122f1
     name: diff
--- a/hgext/reviewboard/tests/test-review-request-approval.t
+++ b/hgext/reviewboard/tests/test-review-request-approval.t
@@ -57,26 +57,36 @@ Create a review request from an L1 user
   - level1b
   - level3
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: 4f4c73d9c6594a0a800a82758ceb6fb12a6b9f83
+    p2rb.commit_message_filediff_ids: '{"1": 2}'
+    p2rb.commit_message_filename: commit-message-3a9f6
     p2rb.first_public_ancestor: 3a9f6899ef84c99841f546030b036d0124a863cf
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 2
+  - id: 3
     revision: 1
     base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-3a9f6 b/commit-message-3a9f6
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-3a9f6
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Initial commit to review
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo
     - +initial
     - ''
   approved: false
@@ -104,26 +114,36 @@ Have an L1 user provide a r+ review whic
   - level1b
   - level3
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: 4f4c73d9c6594a0a800a82758ceb6fb12a6b9f83
+    p2rb.commit_message_filediff_ids: '{"1": 2}'
+    p2rb.commit_message_filename: commit-message-3a9f6
     p2rb.first_public_ancestor: 3a9f6899ef84c99841f546030b036d0124a863cf
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 2
+  - id: 3
     revision: 1
     base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-3a9f6 b/commit-message-3a9f6
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-3a9f6
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Initial commit to review
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo
     - +initial
     - ''
   approved: false
@@ -161,26 +181,36 @@ Have an L3 user provide a r+ review whic
   - level1b
   - level3
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: 4f4c73d9c6594a0a800a82758ceb6fb12a6b9f83
+    p2rb.commit_message_filediff_ids: '{"1": 2}'
+    p2rb.commit_message_filename: commit-message-3a9f6
     p2rb.first_public_ancestor: 3a9f6899ef84c99841f546030b036d0124a863cf
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 2
+  - id: 3
     revision: 1
     base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-3a9f6 b/commit-message-3a9f6
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-3a9f6
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Initial commit to review
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo
     - +initial
     - ''
   approved: true
@@ -224,26 +254,36 @@ Posting a new review without r+ should c
   - level1b
   - level3
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: 4f4c73d9c6594a0a800a82758ceb6fb12a6b9f83
+    p2rb.commit_message_filediff_ids: '{"1": 2}'
+    p2rb.commit_message_filename: commit-message-3a9f6
     p2rb.first_public_ancestor: 3a9f6899ef84c99841f546030b036d0124a863cf
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 2
+  - id: 3
     revision: 1
     base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-3a9f6 b/commit-message-3a9f6
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-3a9f6
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Initial commit to review
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo
     - +initial
     - ''
   approved: false
@@ -296,26 +336,36 @@ One more r+ should switch it back to app
   - level1b
   - level3
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: 4f4c73d9c6594a0a800a82758ceb6fb12a6b9f83
+    p2rb.commit_message_filediff_ids: '{"1": 2}'
+    p2rb.commit_message_filename: commit-message-3a9f6
     p2rb.first_public_ancestor: 3a9f6899ef84c99841f546030b036d0124a863cf
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 2
+  - id: 3
     revision: 1
     base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-3a9f6 b/commit-message-3a9f6
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-3a9f6
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Initial commit to review
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo
     - +initial
     - ''
   approved: true
@@ -378,39 +428,57 @@ Even though the author is L1, adding a n
   - level1b
   - level3
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: f867b363f9fd58135c77672e3c34f222f16ff677
+    p2rb.commit_message_filediff_ids: '{"1": 2, "2": 5}'
+    p2rb.commit_message_filename: commit-message-3a9f6
     p2rb.first_public_ancestor: 3a9f6899ef84c99841f546030b036d0124a863cf
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 2
+  - id: 3
     revision: 1
     base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-3a9f6 b/commit-message-3a9f6
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-3a9f6
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Initial commit to review
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo
     - +initial
     - ''
-  - id: 4
+  - id: 5
     revision: 2
     base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-3a9f6 b/commit-message-3a9f6
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-3a9f6
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Initial commit to review
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo
     - +modified
     - ''
   approved: true
@@ -471,39 +539,57 @@ A new r+ from L3 should give approval
   - level1b
   - level3
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: f867b363f9fd58135c77672e3c34f222f16ff677
+    p2rb.commit_message_filediff_ids: '{"1": 2, "2": 5}'
+    p2rb.commit_message_filename: commit-message-3a9f6
     p2rb.first_public_ancestor: 3a9f6899ef84c99841f546030b036d0124a863cf
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 2
+  - id: 3
     revision: 1
     base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-3a9f6 b/commit-message-3a9f6
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-3a9f6
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Initial commit to review
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo
     - +initial
     - ''
-  - id: 4
+  - id: 5
     revision: 2
     base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-3a9f6 b/commit-message-3a9f6
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-3a9f6
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Initial commit to review
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo
     - +modified
     - ''
   approved: true
@@ -577,39 +663,57 @@ Opening issues, even from an L1 user, sh
   - level1b
   - level3
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: f867b363f9fd58135c77672e3c34f222f16ff677
+    p2rb.commit_message_filediff_ids: '{"1": 2, "2": 5}'
+    p2rb.commit_message_filename: commit-message-3a9f6
     p2rb.first_public_ancestor: 3a9f6899ef84c99841f546030b036d0124a863cf
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 2
+  - id: 3
     revision: 1
     base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-3a9f6 b/commit-message-3a9f6
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-3a9f6
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Initial commit to review
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo
     - +initial
     - ''
-  - id: 4
+  - id: 5
     revision: 2
     base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-3a9f6 b/commit-message-3a9f6
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-3a9f6
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Initial commit to review
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo
     - +modified
     - ''
   approved: false
@@ -668,17 +772,17 @@ Opening issues, even from an L1 user, sh
       public: true
       user: level1b
       issue_opened: true
       issue_status: open
       first_line: 1
       num_lines: 1
       text: Issue Text
       text_type: plain
-      diff_id: 4
+      diff_id: 6
       diff_dest_file: foo
     diff_count: 1
 
 Fixing the issue should restore approval
 
   $ rbmanage update-issue-status 2 6 1 resolved
   updated issue status on diff comment 1
   $ rbmanage dumpreview 2
@@ -698,39 +802,57 @@ Fixing the issue should restore approval
   - level1b
   - level3
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: f867b363f9fd58135c77672e3c34f222f16ff677
+    p2rb.commit_message_filediff_ids: '{"1": 2, "2": 5}'
+    p2rb.commit_message_filename: commit-message-3a9f6
     p2rb.first_public_ancestor: 3a9f6899ef84c99841f546030b036d0124a863cf
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 2
+  - id: 3
     revision: 1
     base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-3a9f6 b/commit-message-3a9f6
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-3a9f6
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Initial commit to review
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo
     - +initial
     - ''
-  - id: 4
+  - id: 5
     revision: 2
     base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-3a9f6 b/commit-message-3a9f6
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-3a9f6
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Initial commit to review
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo
     - +modified
     - ''
   approved: true
@@ -789,17 +911,17 @@ Fixing the issue should restore approval
       public: true
       user: level1b
       issue_opened: true
       issue_status: resolved
       first_line: 1
       num_lines: 1
       text: Issue Text
       text_type: plain
-      diff_id: 4
+      diff_id: 6
       diff_dest_file: foo
     diff_count: 1
 
 Review requests created by L3 users
 
   $ exportbzauth l3@example.com password
   $ echo author2 > foo
   $ hg commit --amend -m "Bug 2 - initial commit to review" > /dev/null
@@ -827,26 +949,36 @@ Review requests created by L3 users
   - level1a
   - level1b
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: b366ef9913208b4030857319aa20520f229a74f3
+    p2rb.commit_message_filediff_ids: '{"1": 8}'
+    p2rb.commit_message_filename: commit-message-3a9f6
     p2rb.first_public_ancestor: 3a9f6899ef84c99841f546030b036d0124a863cf
     p2rb.identifier: bz://2/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 6
+  - id: 7
     revision: 1
     base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-3a9f6 b/commit-message-3a9f6
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-3a9f6
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 2 - initial commit to review
+    - +
+    - '+MozReview-Commit-ID: F63vXs'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo
     - +author2
     - ''
   approved: false
@@ -874,26 +1006,36 @@ Even a ship-it from an L1 user will give
   - level1a
   - level1b
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: b366ef9913208b4030857319aa20520f229a74f3
+    p2rb.commit_message_filediff_ids: '{"1": 8}'
+    p2rb.commit_message_filename: commit-message-3a9f6
     p2rb.first_public_ancestor: 3a9f6899ef84c99841f546030b036d0124a863cf
     p2rb.identifier: bz://2/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 6
+  - id: 7
     revision: 1
     base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-3a9f6 b/commit-message-3a9f6
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-3a9f6
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 2 - initial commit to review
+    - +
+    - '+MozReview-Commit-ID: F63vXs'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo
     - +author2
     - ''
   approved: true
@@ -934,39 +1076,57 @@ ship-its. Posting a new diff should not 
   - level1a
   - level1b
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: bedcf57f515ad540f582962e37ecd424d82424fd
+    p2rb.commit_message_filediff_ids: '{"1": 8, "2": 11}'
+    p2rb.commit_message_filename: commit-message-3a9f6
     p2rb.first_public_ancestor: 3a9f6899ef84c99841f546030b036d0124a863cf
     p2rb.identifier: bz://2/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 6
+  - id: 7
     revision: 1
     base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-3a9f6 b/commit-message-3a9f6
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-3a9f6
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 2 - initial commit to review
+    - +
+    - '+MozReview-Commit-ID: F63vXs'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo
     - +author2
     - ''
-  - id: 8
+  - id: 9
     revision: 2
     base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-3a9f6 b/commit-message-3a9f6
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-3a9f6
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 2 - initial commit to review
+    - +
+    - '+MozReview-Commit-ID: F63vXs'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo
     - +modified2
     - ''
   approved: true
--- a/hgext/reviewboard/tests/test-review-request-closed-discarded.t
+++ b/hgext/reviewboard/tests/test-review-request-closed-discarded.t
@@ -130,16 +130,17 @@ no Commit ID set.
     p2rb.reviewer_map: '{}'
   commit_extra_data:
     p2rb: true
     p2rb.base_commit: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     p2rb.commits: '[["98467d80785ec84dd871f213c167ed704a6d974d", 2], ["3a446ae4382006c43cdfa5aa33c494f582736f35",
       3]]'
     p2rb.discard_on_publish_rids: '[]'
     p2rb.first_public_ancestor: 7c5bdf0cec4a90edb36300f8f3679857f46db829
+    p2rb.has_commit_message_filediff: true
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: true
     p2rb.unpublished_rids: '[]'
   diffs:
   - id: 1
     revision: 1
     base_commit_id: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     name: diff
@@ -172,26 +173,36 @@ Child review request with ID 2 should be
   - 'MozReview-Commit-ID: 124Bxg'
   target_people: []
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: 98467d80785ec84dd871f213c167ed704a6d974d
+    p2rb.commit_message_filediff_ids: '{"1": 2}'
+    p2rb.commit_message_filename: commit-message-7c5bd
     p2rb.first_public_ancestor: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 2
+  - id: 3
     revision: 1
     base_commit_id: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-7c5bd b/commit-message-7c5bd
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-7c5bd
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Foo 1
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo0
     - +foo1
     - ''
   approved: false
@@ -214,26 +225,36 @@ Child review request with ID 3 should be
   - 'MozReview-Commit-ID: 5ijR9k'
   target_people: []
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: 3a446ae4382006c43cdfa5aa33c494f582736f35
+    p2rb.commit_message_filediff_ids: '{"1": 4}'
+    p2rb.commit_message_filename: commit-message-98467
     p2rb.first_public_ancestor: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 3
+  - id: 4
     revision: 1
     base_commit_id: 98467d80785ec84dd871f213c167ed704a6d974d
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-98467 b/commit-message-98467
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-98467
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Foo 2
+    - +
+    - '+MozReview-Commit-ID: 5ijR9k'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo1
     - +foo2
     - ''
   approved: false
@@ -323,16 +344,17 @@ Commit ID re-instated.
     p2rb.reviewer_map: '{}'
   commit_extra_data:
     p2rb: true
     p2rb.base_commit: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     p2rb.commits: '[["98467d80785ec84dd871f213c167ed704a6d974d", 2], ["3a446ae4382006c43cdfa5aa33c494f582736f35",
       3]]'
     p2rb.discard_on_publish_rids: '[]'
     p2rb.first_public_ancestor: 7c5bdf0cec4a90edb36300f8f3679857f46db829
+    p2rb.has_commit_message_filediff: true
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: true
     p2rb.unpublished_rids: '[]'
   diffs:
   - id: 1
     revision: 1
     base_commit_id: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     name: diff
@@ -359,16 +381,17 @@ Commit ID re-instated.
       p2rb.reviewer_map: '{}'
     commit_extra_data:
       p2rb: true
       p2rb.base_commit: 7c5bdf0cec4a90edb36300f8f3679857f46db829
       p2rb.commits: '[["98467d80785ec84dd871f213c167ed704a6d974d", 2], ["3a446ae4382006c43cdfa5aa33c494f582736f35",
         3]]'
       p2rb.discard_on_publish_rids: '[]'
       p2rb.first_public_ancestor: 7c5bdf0cec4a90edb36300f8f3679857f46db829
+      p2rb.has_commit_message_filediff: true
       p2rb.identifier: bz://1/mynick
       p2rb.is_squashed: true
       p2rb.unpublished_rids: '[]'
     diffs: []
 
 Child review request with ID 2 should be re-opened...
 
   $ rbmanage dumpreview 2
@@ -386,26 +409,36 @@ Child review request with ID 2 should be
   - 'MozReview-Commit-ID: 124Bxg'
   target_people: []
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: 98467d80785ec84dd871f213c167ed704a6d974d
+    p2rb.commit_message_filediff_ids: '{"1": 2}'
+    p2rb.commit_message_filename: commit-message-7c5bd
     p2rb.first_public_ancestor: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 2
+  - id: 3
     revision: 1
     base_commit_id: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-7c5bd b/commit-message-7c5bd
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-7c5bd
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Foo 1
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo0
     - +foo1
     - ''
   approved: false
@@ -421,16 +454,18 @@ Child review request with ID 2 should be
     - 'MozReview-Commit-ID: 124Bxg'
     target_people: []
     extra:
       calculated_trophies: true
     commit_extra_data:
       p2rb: true
       p2rb.author: test
       p2rb.commit_id: 98467d80785ec84dd871f213c167ed704a6d974d
+      p2rb.commit_message_filediff_ids: '{"1": 2}'
+      p2rb.commit_message_filename: commit-message-7c5bd
       p2rb.first_public_ancestor: 7c5bdf0cec4a90edb36300f8f3679857f46db829
       p2rb.identifier: bz://1/mynick
       p2rb.is_squashed: false
     diffs: []
 
 Child review request with ID 3 should be re-opened...
 
   $ rbmanage dumpreview 3
@@ -448,26 +483,36 @@ Child review request with ID 3 should be
   - 'MozReview-Commit-ID: 5ijR9k'
   target_people: []
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: 3a446ae4382006c43cdfa5aa33c494f582736f35
+    p2rb.commit_message_filediff_ids: '{"1": 4}'
+    p2rb.commit_message_filename: commit-message-98467
     p2rb.first_public_ancestor: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 3
+  - id: 4
     revision: 1
     base_commit_id: 98467d80785ec84dd871f213c167ed704a6d974d
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-98467 b/commit-message-98467
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-98467
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Foo 2
+    - +
+    - '+MozReview-Commit-ID: 5ijR9k'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo1
     - +foo2
     - ''
   approved: false
@@ -483,16 +528,18 @@ Child review request with ID 3 should be
     - 'MozReview-Commit-ID: 5ijR9k'
     target_people: []
     extra:
       calculated_trophies: true
     commit_extra_data:
       p2rb: true
       p2rb.author: test
       p2rb.commit_id: 3a446ae4382006c43cdfa5aa33c494f582736f35
+      p2rb.commit_message_filediff_ids: '{"1": 4}'
+      p2rb.commit_message_filename: commit-message-98467
       p2rb.first_public_ancestor: 7c5bdf0cec4a90edb36300f8f3679857f46db829
       p2rb.identifier: bz://1/mynick
       p2rb.is_squashed: false
     diffs: []
 
 There should still not be a visible attachment on the bug
 
   $ bugzilla dump-bug 1
@@ -576,16 +623,17 @@ Squashed review request should be publis
     p2rb.reviewer_map: '{}'
   commit_extra_data:
     p2rb: true
     p2rb.base_commit: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     p2rb.commits: '[["98467d80785ec84dd871f213c167ed704a6d974d", 2], ["3a446ae4382006c43cdfa5aa33c494f582736f35",
       3]]'
     p2rb.discard_on_publish_rids: '[]'
     p2rb.first_public_ancestor: 7c5bdf0cec4a90edb36300f8f3679857f46db829
+    p2rb.has_commit_message_filediff: true
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: true
     p2rb.unpublished_rids: '[]'
   diffs:
   - id: 1
     revision: 1
     base_commit_id: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     name: diff
@@ -618,26 +666,36 @@ Child review request with ID 2 should be
   - 'MozReview-Commit-ID: 124Bxg'
   target_people: []
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: 98467d80785ec84dd871f213c167ed704a6d974d
+    p2rb.commit_message_filediff_ids: '{"1": 2}'
+    p2rb.commit_message_filename: commit-message-7c5bd
     p2rb.first_public_ancestor: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 2
+  - id: 3
     revision: 1
     base_commit_id: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-7c5bd b/commit-message-7c5bd
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-7c5bd
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Foo 1
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo0
     - +foo1
     - ''
   approved: false
@@ -660,26 +718,36 @@ Child review request with ID 3 should be
   - 'MozReview-Commit-ID: 5ijR9k'
   target_people: []
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: 3a446ae4382006c43cdfa5aa33c494f582736f35
+    p2rb.commit_message_filediff_ids: '{"1": 4}'
+    p2rb.commit_message_filename: commit-message-98467
     p2rb.first_public_ancestor: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 3
+  - id: 4
     revision: 1
     base_commit_id: 98467d80785ec84dd871f213c167ed704a6d974d
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-98467 b/commit-message-98467
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-98467
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Foo 2
+    - +
+    - '+MozReview-Commit-ID: 5ijR9k'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo1
     - +foo2
     - ''
   approved: false
@@ -797,16 +865,17 @@ Pushing to a discarded review series wil
     p2rb.reviewer_map: '{}'
   commit_extra_data:
     p2rb: true
     p2rb.base_commit: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     p2rb.commits: '[["98467d80785ec84dd871f213c167ed704a6d974d", 2], ["3a446ae4382006c43cdfa5aa33c494f582736f35",
       3]]'
     p2rb.discard_on_publish_rids: '[]'
     p2rb.first_public_ancestor: 7c5bdf0cec4a90edb36300f8f3679857f46db829
+    p2rb.has_commit_message_filediff: true
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: true
     p2rb.unpublished_rids: '[]'
   diffs:
   - id: 1
     revision: 1
     base_commit_id: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     name: diff
@@ -838,21 +907,22 @@ Pushing to a discarded review series wil
     p2rb.reviewer_map: '{}'
   commit_extra_data:
     p2rb: true
     p2rb.base_commit: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     p2rb.commits: '[["98467d80785ec84dd871f213c167ed704a6d974d", 5], ["3a446ae4382006c43cdfa5aa33c494f582736f35",
       6], ["1ec9946fd47ff9b5cb07e9d9c8b4d393b688e01b", 7]]'
     p2rb.discard_on_publish_rids: '[]'
     p2rb.first_public_ancestor: 7c5bdf0cec4a90edb36300f8f3679857f46db829
+    p2rb.has_commit_message_filediff: true
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: true
     p2rb.unpublished_rids: '[]'
   diffs:
-  - id: 4
+  - id: 5
     revision: 1
     base_commit_id: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     name: diff
     extra: {}
     patch:
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
--- a/hgext/reviewboard/tests/test-review-request-closed-submitted.t
+++ b/hgext/reviewboard/tests/test-review-request-closed-submitted.t
@@ -72,16 +72,17 @@ Squashed review request with ID 1 should
     p2rb.reviewer_map: '{}'
   commit_extra_data:
     p2rb: true
     p2rb.base_commit: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     p2rb.commits: '[["98467d80785ec84dd871f213c167ed704a6d974d", 2], ["3a446ae4382006c43cdfa5aa33c494f582736f35",
       3]]'
     p2rb.discard_on_publish_rids: '[]'
     p2rb.first_public_ancestor: 7c5bdf0cec4a90edb36300f8f3679857f46db829
+    p2rb.has_commit_message_filediff: true
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: true
     p2rb.unpublished_rids: '[]'
   diffs:
   - id: 1
     revision: 1
     base_commit_id: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     name: diff
@@ -114,26 +115,36 @@ Child review request with ID 2 should be
   - 'MozReview-Commit-ID: 124Bxg'
   target_people: []
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: 98467d80785ec84dd871f213c167ed704a6d974d
+    p2rb.commit_message_filediff_ids: '{"1": 2}'
+    p2rb.commit_message_filename: commit-message-7c5bd
     p2rb.first_public_ancestor: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 2
+  - id: 3
     revision: 1
     base_commit_id: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-7c5bd b/commit-message-7c5bd
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-7c5bd
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Foo 1
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo0
     - +foo1
     - ''
   approved: false
@@ -154,26 +165,36 @@ Child review request with ID 2 should be
   - 'MozReview-Commit-ID: 5ijR9k'
   target_people: []
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: 3a446ae4382006c43cdfa5aa33c494f582736f35
+    p2rb.commit_message_filediff_ids: '{"1": 4}'
+    p2rb.commit_message_filename: commit-message-98467
     p2rb.first_public_ancestor: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 3
+  - id: 4
     revision: 1
     base_commit_id: 98467d80785ec84dd871f213c167ed704a6d974d
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-98467 b/commit-message-98467
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-98467
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Foo 2
+    - +
+    - '+MozReview-Commit-ID: 5ijR9k'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo1
     - +foo2
     - ''
   approved: false
@@ -221,16 +242,17 @@ Squashed review request with ID 1 should
     p2rb.reviewer_map: '{}'
   commit_extra_data:
     p2rb: true
     p2rb.base_commit: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     p2rb.commits: '[["98467d80785ec84dd871f213c167ed704a6d974d", 2], ["3a446ae4382006c43cdfa5aa33c494f582736f35",
       3]]'
     p2rb.discard_on_publish_rids: '[]'
     p2rb.first_public_ancestor: 7c5bdf0cec4a90edb36300f8f3679857f46db829
+    p2rb.has_commit_message_filediff: true
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: true
     p2rb.unpublished_rids: '[]'
   diffs:
   - id: 1
     revision: 1
     base_commit_id: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     name: diff
@@ -263,26 +285,36 @@ Child review request with ID 2 should be
   - 'MozReview-Commit-ID: 124Bxg'
   target_people: []
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: 98467d80785ec84dd871f213c167ed704a6d974d
+    p2rb.commit_message_filediff_ids: '{"1": 2}'
+    p2rb.commit_message_filename: commit-message-7c5bd
     p2rb.first_public_ancestor: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 2
+  - id: 3
     revision: 1
     base_commit_id: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-7c5bd b/commit-message-7c5bd
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-7c5bd
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Foo 1
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo0
     - +foo1
     - ''
   approved: false
@@ -305,26 +337,36 @@ Child review request with ID 3 should be
   - 'MozReview-Commit-ID: 5ijR9k'
   target_people: []
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: 3a446ae4382006c43cdfa5aa33c494f582736f35
+    p2rb.commit_message_filediff_ids: '{"1": 4}'
+    p2rb.commit_message_filename: commit-message-98467
     p2rb.first_public_ancestor: 7c5bdf0cec4a90edb36300f8f3679857f46db829
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 3
+  - id: 4
     revision: 1
     base_commit_id: 98467d80785ec84dd871f213c167ed704a6d974d
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-98467 b/commit-message-98467
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-98467
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Foo 2
+    - +
+    - '+MozReview-Commit-ID: 5ijR9k'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo1
     - +foo2
     - ''
   approved: false
--- a/hgext/reviewboard/tests/test-review-request-delegation.t
+++ b/hgext/reviewboard/tests/test-review-request-delegation.t
@@ -79,33 +79,33 @@ Change the reviewer while logged in as r
     reviewers:
     - reviewer2
     reviewers_status:
       reviewer2:
         review_flag: r?
         ship_it: false
     diff:
       delete: 1
-      insert: 1
+      insert: 4
   - summary: Bug 1 - Forgot water r?reviewer1
     id: 3
     commit: 4935598400374354824ffde84a8b6767823100d1
     submitter: l3author
     issue_open_count: 0
     status: pending
     has_draft: false
     reviewers:
     - reviewer1
     reviewers_status:
       reviewer1:
         review_flag: r?
         ship_it: false
     diff:
       delete: 0
-      insert: 1
+      insert: 4
 
 Test multiple reviewers
 
   $ rbmanage modify-reviewers 1 2 'reviewer1,reviewer2'
   $ rbmanage dump-summary 1
   parent:
     summary: bz://1/mynick
     id: 1
@@ -134,33 +134,33 @@ Test multiple reviewers
       reviewer1:
         review_flag: r?
         ship_it: false
       reviewer2:
         review_flag: r?
         ship_it: false
     diff:
       delete: 1
-      insert: 1
+      insert: 4
   - summary: Bug 1 - Forgot water r?reviewer1
     id: 3
     commit: 4935598400374354824ffde84a8b6767823100d1
     submitter: l3author
     issue_open_count: 0
     status: pending
     has_draft: false
     reviewers:
     - reviewer1
     reviewers_status:
       reviewer1:
         review_flag: r?
         ship_it: false
     diff:
       delete: 0
-      insert: 1
+      insert: 4
 
 Test invalid reviewer
 
   $ rbmanage modify-reviewers 1 2 'invalid'
   API Error: 400: 105: The reviewer 'invalid' was not found
   [1]
 
 Change the reviewer while logged in as the submitter
@@ -192,33 +192,33 @@ Change the reviewer while logged in as t
     reviewers:
     - reviewer2
     reviewers_status:
       reviewer2:
         review_flag: r?
         ship_it: false
     diff:
       delete: 1
-      insert: 1
+      insert: 4
   - summary: Bug 1 - Forgot water r?reviewer1
     id: 3
     commit: 4935598400374354824ffde84a8b6767823100d1
     submitter: l3author
     issue_open_count: 0
     status: pending
     has_draft: false
     reviewers:
     - reviewer1
     reviewers_status:
       reviewer1:
         review_flag: r?
         ship_it: false
     diff:
       delete: 0
-      insert: 1
+      insert: 4
 
 Test user without editbugs
 
   $ exportbzauth reviewer2@example.com password
   $ rbmanage modify-reviewers 1 2 'reviewer1'
   API Error: 500: 225: Error publishing: Bugzilla error: You are not authorized to edit attachment 1.
   [1]
 
@@ -263,33 +263,33 @@ Test ensure-drafts
     reviewers:
     - reviewer2
     reviewers_status:
       reviewer2:
         review_flag: r?
         ship_it: false
     diff:
       delete: 1
-      insert: 1
+      insert: 4
   - summary: Bug 1 - Forgot water r?reviewer1
     id: 3
     commit: 4935598400374354824ffde84a8b6767823100d1
     submitter: l3author
     issue_open_count: 0
     status: pending
     has_draft: false
     reviewers:
     - reviewer1
     reviewers_status:
       reviewer1:
         review_flag: r?
         ship_it: false
     diff:
       delete: 0
-      insert: 1
+      insert: 4
   $ rbmanage ensure-drafts 1
   $ rbmanage dump-summary 1
   parent:
     summary: bz://1/mynick
     id: 1
     submitter: l3author
     issue_open_count: 0
     status: pending
@@ -311,33 +311,33 @@ Test ensure-drafts
     reviewers:
     - reviewer2
     reviewers_status:
       reviewer2:
         review_flag: r?
         ship_it: false
     diff:
       delete: 1
-      insert: 1
+      insert: 4
   - summary: Bug 1 - Forgot water r?reviewer1
     id: 3
     commit: 4935598400374354824ffde84a8b6767823100d1
     submitter: l3author
     issue_open_count: 0
     status: pending
     has_draft: true
     reviewers:
     - reviewer1
     reviewers_status:
       reviewer1:
         review_flag: r?
         ship_it: false
     diff:
       delete: 0
-      insert: 1
+      insert: 4
   $ rbmanage publish 1
   $ rbmanage dump-summary 1
   parent:
     summary: bz://1/mynick
     id: 1
     submitter: l3author
     issue_open_count: 0
     status: pending
@@ -363,25 +363,25 @@ Test ensure-drafts
       reviewer1:
         review_flag: r?
         ship_it: false
       reviewer2:
         review_flag: r?
         ship_it: false
     diff:
       delete: 1
-      insert: 1
+      insert: 4
   - summary: Bug 1 - Forgot water r?reviewer1
     id: 3
     commit: 4935598400374354824ffde84a8b6767823100d1
     submitter: l3author
     issue_open_count: 0
     status: pending
     has_draft: false
     reviewers:
     - reviewer1
     reviewers_status:
       reviewer1:
         review_flag: r?
         ship_it: false
     diff:
       delete: 0
-      insert: 1
+      insert: 4
--- a/hgext/reviewboard/tests/test-review-request-delete-draft.t
+++ b/hgext/reviewboard/tests/test-review-request-delete-draft.t
@@ -81,16 +81,17 @@ We should have a disagreement between pu
     calculated_trophies: true
     p2rb.reviewer_map: '{}'
   commit_extra_data:
     p2rb: true
     p2rb.base_commit: 3a9f6899ef84c99841f546030b036d0124a863cf
     p2rb.commits: '[["65e5c536f9cc5816ef28ebaff6a0db47b9af0fee", 2]]'
     p2rb.discard_on_publish_rids: '[]'
     p2rb.first_public_ancestor: 3a9f6899ef84c99841f546030b036d0124a863cf
+    p2rb.has_commit_message_filediff: true
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: true
     p2rb.unpublished_rids: '[]'
   diffs:
   - id: 1
     revision: 1
     base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
     name: diff
@@ -116,21 +117,22 @@ We should have a disagreement between pu
       calculated_trophies: true
       p2rb.reviewer_map: '{"2": []}'
     commit_extra_data:
       p2rb: true
       p2rb.base_commit: 3a9f6899ef84c99841f546030b036d0124a863cf
       p2rb.commits: '[["2c68bc327689343c967bcb80b9a3fd8d9bc50eb4", 2]]'
       p2rb.discard_on_publish_rids: '[]'
       p2rb.first_public_ancestor: 3a9f6899ef84c99841f546030b036d0124a863cf
+      p2rb.has_commit_message_filediff: true
       p2rb.identifier: bz://1/mynick
       p2rb.is_squashed: true
       p2rb.unpublished_rids: '[]'
     diffs:
-    - id: 3
+    - id: 4
       revision: 2
       base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
       name: diff
       extra: {}
       patch:
       - diff --git a/foo b/foo
       - '--- a/foo'
       - +++ b/foo
@@ -154,26 +156,36 @@ We should have a disagreement between pu
   - 'MozReview-Commit-ID: 124Bxg'
   target_people: []
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: 65e5c536f9cc5816ef28ebaff6a0db47b9af0fee
+    p2rb.commit_message_filediff_ids: '{"1": 2}'
+    p2rb.commit_message_filename: commit-message-3a9f6
     p2rb.first_public_ancestor: 3a9f6899ef84c99841f546030b036d0124a863cf
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 2
+  - id: 3
     revision: 1
     base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-3a9f6 b/commit-message-3a9f6
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-3a9f6
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Initial commit
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo
     - +foo1
     - ''
   approved: false
@@ -189,26 +201,36 @@ We should have a disagreement between pu
     - 'MozReview-Commit-ID: 124Bxg'
     target_people: []
     extra:
       calculated_trophies: true
     commit_extra_data:
       p2rb: true
       p2rb.author: test
       p2rb.commit_id: 2c68bc327689343c967bcb80b9a3fd8d9bc50eb4
+      p2rb.commit_message_filediff_ids: '{"1": 2, "2": 5}'
+      p2rb.commit_message_filename: commit-message-3a9f6
       p2rb.first_public_ancestor: 3a9f6899ef84c99841f546030b036d0124a863cf
       p2rb.identifier: bz://1/mynick
       p2rb.is_squashed: false
     diffs:
-    - id: 4
+    - id: 5
       revision: 2
       base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
       name: diff
       extra: {}
       patch:
+      - diff --git a/commit-message-3a9f6 b/commit-message-3a9f6
+      - new file mode 100644
+      - '--- /dev/null'
+      - +++ b/commit-message-3a9f6
+      - '@@ -0,0 +1,3 @@'
+      - +Bug 1 - Initial commit
+      - +
+      - '+MozReview-Commit-ID: 124Bxg'
       - diff --git a/foo b/foo
       - '--- a/foo'
       - +++ b/foo
       - '@@ -1,1 +1,1 @@'
       - -foo
       - +foo3
       - ''
 
@@ -232,16 +254,17 @@ Discarding the parent review request dra
     calculated_trophies: true
     p2rb.reviewer_map: '{}'
   commit_extra_data:
     p2rb: true
     p2rb.base_commit: 3a9f6899ef84c99841f546030b036d0124a863cf
     p2rb.commits: '[["65e5c536f9cc5816ef28ebaff6a0db47b9af0fee", 2]]'
     p2rb.discard_on_publish_rids: '[]'
     p2rb.first_public_ancestor: 3a9f6899ef84c99841f546030b036d0124a863cf
+    p2rb.has_commit_message_filediff: true
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: true
     p2rb.unpublished_rids: '[]'
   diffs:
   - id: 1
     revision: 1
     base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
     name: diff
@@ -272,26 +295,36 @@ Discarding the parent review request dra
   - 'MozReview-Commit-ID: 124Bxg'
   target_people: []
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: 65e5c536f9cc5816ef28ebaff6a0db47b9af0fee
+    p2rb.commit_message_filediff_ids: '{"1": 2}'
+    p2rb.commit_message_filename: commit-message-3a9f6
     p2rb.first_public_ancestor: 3a9f6899ef84c99841f546030b036d0124a863cf
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 2
+  - id: 3
     revision: 1
     base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-3a9f6 b/commit-message-3a9f6
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-3a9f6
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - Initial commit
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo
     - +foo1
     - ''
   approved: false
--- a/hgext/reviewboard/tests/test-review-request-summary.t
+++ b/hgext/reviewboard/tests/test-review-request-summary.t
@@ -73,29 +73,29 @@
     reviewers:
     - reviewer
     reviewers_status:
       reviewer:
         review_flag: r?
         ship_it: false
     diff:
       delete: 1
-      insert: 1
+      insert: 4
   - summary: Bug 1 - Foo 2
     id: 3
     commit: 3a446ae4382006c43cdfa5aa33c494f582736f35
     submitter: default+5
     issue_open_count: 0
     status: pending
     has_draft: false
     reviewers: []
     reviewers_status: {}
     diff:
       delete: 1
-      insert: 1
+      insert: 4
 
 Only parents have summaries.
 
   $ rbmanage dump-summary 2
   API Error: 400: 1001: Review request is not a parent
   [1]
 
   $ rbmanage create-review 2
@@ -132,29 +132,29 @@ Opening an issue should be reflected in 
     reviewers:
     - reviewer
     reviewers_status:
       reviewer:
         review_flag: r?
         ship_it: false
     diff:
       delete: 1
-      insert: 1
+      insert: 4
   - summary: Bug 1 - Foo 2
     id: 3
     commit: 3a446ae4382006c43cdfa5aa33c494f582736f35
     submitter: default+5
     issue_open_count: 0
     status: pending
     has_draft: false
     reviewers: []
     reviewers_status: {}
     diff:
       delete: 1
-      insert: 1
+      insert: 4
 
 Resolving an issue should decrement the issue count.
 
   $ rbmanage update-issue-status 2 1 1 resolved
   updated issue status on diff comment 1
 
   $ rbmanage dump-summary 1
   parent:
@@ -180,29 +180,29 @@ Resolving an issue should decrement the 
     reviewers:
     - reviewer
     reviewers_status:
       reviewer:
         review_flag: r?
         ship_it: false
     diff:
       delete: 1
-      insert: 1
+      insert: 4
   - summary: Bug 1 - Foo 2
     id: 3
     commit: 3a446ae4382006c43cdfa5aa33c494f582736f35
     submitter: default+5
     issue_open_count: 0
     status: pending
     has_draft: false
     reviewers: []
     reviewers_status: {}
     diff:
       delete: 1
-      insert: 1
+      insert: 4
 
 Giving a ship-it should result in a change in the reviewer status
 
   $ exportbzauth reviewer@example.com password1
   $ rbmanage create-review 2 --review-flag='r+' --public
   created review 2
 
   $ rbmanage dump-summary 1
@@ -229,29 +229,29 @@ Giving a ship-it should result in a chan
     reviewers:
     - reviewer
     reviewers_status:
       reviewer:
         review_flag: r+
         ship_it: true
     diff:
       delete: 1
-      insert: 1
+      insert: 4
   - summary: Bug 1 - Foo 2
     id: 3
     commit: 3a446ae4382006c43cdfa5aa33c494f582736f35
     submitter: default+5
     issue_open_count: 0
     status: pending
     has_draft: false
     reviewers: []
     reviewers_status: {}
     diff:
       delete: 1
-      insert: 1
+      insert: 4
 
   $ exportbzauth default@example.com password
 
 Verify we can also get the summaries by bug, including closed ones.
 
   $ rbmanage closesubmitted 2
 
   $ rbmanage dump-summaries-by-bug 1
@@ -278,29 +278,29 @@ Verify we can also get the summaries by 
       reviewers:
       - reviewer
       reviewers_status:
         reviewer:
           review_flag: r+
           ship_it: true
       diff:
         delete: 1
-        insert: 1
+        insert: 4
     - summary: Bug 1 - Foo 2
       id: 3
       commit: 3a446ae4382006c43cdfa5aa33c494f582736f35
       submitter: default+5
       issue_open_count: 0
       status: pending
       has_draft: false
       reviewers: []
       reviewers_status: {}
       diff:
         delete: 1
-        insert: 1
+        insert: 4
 
 Verify that we get nothing from non-existent bugs.
 
   $ rbmanage dump-summaries-by-bug 2
   []
 
 Create a draft with different diffstats
 
@@ -359,29 +359,29 @@ Verify that requesting the summary as th
     reviewers:
     - reviewer
     reviewers_status:
       reviewer:
         review_flag: r+
         ship_it: true
     diff:
       delete: 1
-      insert: 1
+      insert: 4
   - summary: Bug 1 - Foo 2
     id: 3
     commit: 3a446ae4382006c43cdfa5aa33c494f582736f35
     submitter: default+5
     issue_open_count: 0
     status: pending
     has_draft: true
     reviewers: []
     reviewers_status: {}
     diff:
       delete: 1
-      insert: 3
+      insert: 6
 
 Verify non-submitters will not see the draft diffstats
 
   $ exportbzauth reviewer@example.com password1
   $ rbmanage dump-summary 1
   parent:
     summary: bz://1/mynick
     id: 1
@@ -405,26 +405,26 @@ Verify non-submitters will not see the d
     reviewers:
     - reviewer
     reviewers_status:
       reviewer:
         review_flag: r+
         ship_it: true
     diff:
       delete: 1
-      insert: 1
+      insert: 4
   - summary: Bug 1 - Foo 2
     id: 3
     commit: 3a446ae4382006c43cdfa5aa33c494f582736f35
     submitter: default+5
     issue_open_count: 0
     status: pending
     has_draft: true
     reviewers: []
     reviewers_status: {}
     diff:
       delete: 1
-      insert: 1
+      insert: 4
 
 Cleanup
 
   $ mozreview stop
   stopped 9 containers
--- a/hgext/reviewboard/tests/test-specify-reviewers.t
+++ b/hgext/reviewboard/tests/test-specify-reviewers.t
@@ -224,26 +224,36 @@ Publishing series during push works
   - remus
   - romulus
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: 214fce3608426755a50ae60ae8645eb9bc1f7537
+    p2rb.commit_message_filediff_ids: '{"1": 18}'
+    p2rb.commit_message_filename: commit-message-ccfcf
     p2rb.first_public_ancestor: 3a9f6899ef84c99841f546030b036d0124a863cf
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 10
+  - id: 11
     revision: 1
     base_commit_id: ccfcf9b70a65731d01240f24815edf0cf6b64739
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-ccfcf b/commit-message-ccfcf
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-ccfcf
+    - '@@ -0,0 +1,3 @@'
+    - +Bug 1 - More stuff; [r?remus, r?romulus]
+    - +
+    - '+MozReview-Commit-ID: cXO9WC'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,8 +1,9 @@'
     - ' initial'
     - ' blah'
     - ' blah'
     - ' blah'
--- a/hgext/reviewboard/tests/test-unicode.t
+++ b/hgext/reviewboard/tests/test-unicode.t
@@ -50,26 +50,36 @@ The globbing is patching over a bug in m
   - 'MozReview-Commit-ID: 124Bxg'
   target_people: []
   extra_data:
     calculated_trophies: true
   commit_extra_data:
     p2rb: true
     p2rb.author: test
     p2rb.commit_id: 86ab97a5dd61e8ec7ff3c23212db732e3531af01
+    p2rb.commit_message_filediff_ids: '{"1": 2}'
+    p2rb.commit_message_filename: commit-message-3a9f6
     p2rb.first_public_ancestor: 3a9f6899ef84c99841f546030b036d0124a863cf
     p2rb.identifier: bz://1/mynick
     p2rb.is_squashed: false
   diffs:
-  - id: 2
+  - id: 3
     revision: 1
     base_commit_id: 3a9f6899ef84c99841f546030b036d0124a863cf
     name: diff
     extra: {}
     patch:
+    - diff --git a/commit-message-3a9f6 b/commit-message-3a9f6
+    - new file mode 100644
+    - '--- /dev/null'
+    - +++ b/commit-message-3a9f6
+    - '@@ -0,0 +1,3 @@'
+    - "+Bug 1 - Initial commit to review \u2019 \u3053"
+    - +
+    - '+MozReview-Commit-ID: 124Bxg'
     - diff --git a/foo b/foo
     - '--- a/foo'
     - +++ b/foo
     - '@@ -1,1 +1,1 @@'
     - -foo
     - +initial
     - ''
   approved: false
@@ -174,16 +184,17 @@ Put some wonky byte sequences in the dif
   commit: null
   submitter: author+6
   summary: ''
   description: ''
   target_people: []
   extra_data: {}
   commit_extra_data:
     p2rb: true
+    p2rb.commit_message_filename: commit-message-86ab9
     p2rb.identifier: bz://2/mynick
     p2rb.is_squashed: false
   diffs: []
   approved: false
   approval_failure: The review request is not public.
   draft:
     bugs:
     - '2'
@@ -194,26 +205,35 @@ Put some wonky byte sequences in the dif
     - ''
     - 'MozReview-Commit-ID: 5ijR9k'
     target_people: []
     extra: {}
     commit_extra_data:
       p2rb: true
       p2rb.author: test
       p2rb.commit_id: 78025579528e119adf8ccc61727fccc1e23bda1c
+      p2rb.commit_message_filediff_ids: '{"1": 5}'
       p2rb.first_public_ancestor: 3a9f6899ef84c99841f546030b036d0124a863cf
       p2rb.identifier: bz://2/mynick
       p2rb.is_squashed: false
     diffs:
-    - id: 4
+    - id: 5
       revision: 1
       base_commit_id: 86ab97a5dd61e8ec7ff3c23212db732e3531af01
       name: diff
       extra: {}
       patch:
+      - diff --git a/commit-message-86ab9 b/commit-message-86ab9
+      - new file mode 100644
+      - '--- /dev/null'
+      - +++ b/commit-message-86ab9
+      - '@@ -0,0 +1,3 @@'
+      - +Bug 2 - base
+      - +
+      - '+MozReview-Commit-ID: 5ijR9k'
       - diff --git a/foo b/foo
       - '--- a/foo'
       - +++ b/foo
       - '@@ -1,1 +1,1 @@'
       - -initial
       - !!binary |
         K2hlbGxvIHdvcmxkIP//fg==
       - ''
@@ -226,16 +246,17 @@ Put some wonky byte sequences in the dif
   commit: null
   submitter: author+6
   summary: ''
   description: ''
   target_people: []
   extra_data: {}
   commit_extra_data:
     p2rb: true
+    p2rb.commit_message_filename: commit-message-78025
     p2rb.identifier: bz://2/mynick
     p2rb.is_squashed: false
   diffs: []
   approved: false
   approval_failure: The review request is not public.
   draft:
     bugs:
     - '2'
@@ -246,50 +267,75 @@ Put some wonky byte sequences in the dif
     - ''
     - 'MozReview-Commit-ID: APOgLo'
     target_people: []
     extra: {}
     commit_extra_data:
       p2rb: true
       p2rb.author: test
       p2rb.commit_id: 6204fc917b213cf88051df32860d62ca91ae1422
+      p2rb.commit_message_filediff_ids: '{"1": 7}'
       p2rb.first_public_ancestor: 3a9f6899ef84c99841f546030b036d0124a863cf
       p2rb.identifier: bz://2/mynick
       p2rb.is_squashed: false
     diffs:
-    - id: 5
+    - id: 6
       revision: 1
       base_commit_id: 78025579528e119adf8ccc61727fccc1e23bda1c
       name: diff
       extra: {}
       patch:
+      - diff --git a/commit-message-78025 b/commit-message-78025
+      - new file mode 100644
+      - '--- /dev/null'
+      - +++ b/commit-message-78025
+      - '@@ -0,0 +1,3 @@'
+      - +Bug 2 - tip
+      - +
+      - '+MozReview-Commit-ID: APOgLo'
       - diff --git a/foo b/foo
       - '--- a/foo'
       - +++ b/foo
       - '@@ -1,1 +1,1 @@'
       - !!binary |
         LWhlbGxvIHdvcmxkIP//fg==
       - "+hello world \u3053\u3093\u306B\u3061\u306F\u4E16\u754C"
       - ''
 
 The raw diff demonstrates the original bytes are preserved
 
   $ rbmanage dump-raw-diff 4
-  ID: 4 (draft)
+  ID: 5 (draft)
+  diff --git a/commit-message-86ab9 b/commit-message-86ab9
+  new file mode 100644
+  --- /dev/null
+  +++ b/commit-message-86ab9
+  @@ -0,0 +1,3 @@
+  +Bug 2 - base
+  +
+  +MozReview-Commit-ID: 5ijR9k
   diff --git a/foo b/foo
   --- a/foo
   +++ b/foo
   @@ -1,1 +1,1 @@
   -initial
   +hello world \xff\xff~ (esc)
   
   
 
   $ rbmanage dump-raw-diff 5
-  ID: 5 (draft)
+  ID: 6 (draft)
+  diff --git a/commit-message-78025 b/commit-message-78025
+  new file mode 100644
+  --- /dev/null
+  +++ b/commit-message-78025
+  @@ -0,0 +1,3 @@
+  +Bug 2 - tip
+  +
+  +MozReview-Commit-ID: APOgLo
   diff --git a/foo b/foo
   --- a/foo
   +++ b/foo
   @@ -1,1 +1,1 @@
   -hello world \xff\xff~ (esc)
   +hello world \xe3\x81\x93\xe3\x82\x93\xe3\x81\xab\xe3\x81\xa1\xe3\x81\xaf\xe4\xb8\x96\xe7\x95\x8c (esc)
   
   
--- a/pylib/mozreview/mozreview/extension.py
+++ b/pylib/mozreview/mozreview/extension.py
@@ -307,16 +307,18 @@ class MozReviewExtension(Extension):
                      'mozreview/after-login-form.html', apply_to=['login'])
         TemplateHook(self, 'base-after-content',
                      '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)
+        TemplateHook(self, 'base-after-content', 'mozreview/diff-data.html',
+                     apply_to=diffviewer_url_names)
         TemplateHook(self, 'review-summary-body',
                      'mozreview/review_summary_flag_info.html')
 
         ReviewRequestFieldsHook(self, 'main', [CommitsListField])
         ReviewRequestFieldsHook(self, 'main', [CommitDetailField])
         CommitContextTemplateHook(self, 'mozreview-pre-review-request-box',
                                   'mozreview/commits.html',
                                   apply_to=review_request_url_names)
--- a/pylib/mozreview/mozreview/extra_data.py
+++ b/pylib/mozreview/mozreview/extra_data.py
@@ -17,35 +17,42 @@ from mozreview.models import (
 )
 
 MOZREVIEW_KEY = 'p2rb'
 
 # Built-in extra_data keys:
 REVIEWER_MAP_KEY = MOZREVIEW_KEY + '.reviewer_map'
 REVIEW_FLAG_KEY = MOZREVIEW_KEY + '.review_flag'
 
+# Repository extra_data key:
+TEMP_DIFFSET_ID_KEY = MOZREVIEW_KEY + '.temp_diffset_id'
+
 # CommitData field keys:
 AUTHOR_KEY = MOZREVIEW_KEY + '.author'
 BASE_COMMIT_KEY = MOZREVIEW_KEY + '.base_commit'
 COMMIT_ID_KEY = MOZREVIEW_KEY + '.commit_id'
 COMMITS_KEY = MOZREVIEW_KEY + '.commits'
+COMMIT_MSG_FILEDIFF_IDS_KEY = MOZREVIEW_KEY + '.commit_message_filediff_ids'
+COMMIT_MSG_FILENAME_KEY = MOZREVIEW_KEY + '.commit_message_filename'
 DISCARD_ON_PUBLISH_KEY = MOZREVIEW_KEY + '.discard_on_publish_rids'
 FIRST_PUBLIC_ANCESTOR_KEY = MOZREVIEW_KEY + '.first_public_ancestor'
+HAS_COMMIT_MSG_FILEDIFF_KEY = MOZREVIEW_KEY + '.has_commit_message_filediff'
 IDENTIFIER_KEY = MOZREVIEW_KEY + '.identifier'
 SQUASHED_KEY = MOZREVIEW_KEY + '.is_squashed'
 UNPUBLISHED_KEY = MOZREVIEW_KEY + '.unpublished_rids'
 PUBLISH_AS_KEY = MOZREVIEW_KEY + '.publish_as'
 
 # CommitData fields which should be automatically copied from
 # draft_extra_data to extra_data when a review request is published.
 DRAFTED_COMMIT_DATA_KEYS = (
     AUTHOR_KEY,
+    COMMIT_ID_KEY,
+    COMMIT_MSG_FILEDIFF_IDS_KEY,
     FIRST_PUBLIC_ANCESTOR_KEY,
     IDENTIFIER_KEY,
-    COMMIT_ID_KEY,
 )
 
 REVIEWID_RE = re.compile('bz://(\d+)/[^/]+')
 
 logger = logging.getLogger(__name__)
 
 
 def fetch_commit_data(review_request_details, commit_data=None):
--- a/pylib/mozreview/mozreview/resources/batch_review_request.py
+++ b/pylib/mozreview/mozreview/resources/batch_review_request.py
@@ -24,21 +24,25 @@ from mozautomation.commitparser import (
 from reviewboard.accounts.backends import (
     get_enabled_auth_backends,
 )
 from reviewboard.accounts.errors import (
     UserQueryError,
 )
 from reviewboard.diffviewer.models import (
     DiffSet,
+    FileDiff,
 )
 from reviewboard.reviews.models import (
     ReviewRequest,
     ReviewRequestDraft,
 )
+from reviewboard.scmtools.core import (
+    PRE_CREATION,
+)
 from reviewboard.scmtools.models import (
     Repository,
 )
 from reviewboard.webapi.decorators import (
     webapi_check_local_site,
 )
 from reviewboard.webapi.encoder import (
     status_to_string,
@@ -50,34 +54,46 @@ from reviewboard.webapi.resources import
 from mozreview.errors import (
     REVIEW_REQUEST_UPDATE_NOT_ALLOWED,
 )
 from mozreview.extra_data import (
     AUTHOR_KEY,
     BASE_COMMIT_KEY,
     COMMITS_KEY,
     COMMIT_ID_KEY,
+    COMMIT_MSG_FILEDIFF_IDS_KEY,
+    COMMIT_MSG_FILENAME_KEY,
     DISCARD_ON_PUBLISH_KEY,
     fetch_commit_data,
     FIRST_PUBLIC_ANCESTOR_KEY,
+    HAS_COMMIT_MSG_FILEDIFF_KEY,
     IDENTIFIER_KEY,
     MOZREVIEW_KEY,
     SQUASHED_KEY,
+    TEMP_DIFFSET_ID_KEY,
     UNPUBLISHED_KEY,
 )
 from mozreview.models import (
     DiffSetVerification,
 )
 from mozreview.resources.bugzilla_login import (
     auth_api_key
 )
 from mozreview.review_helpers import (
     gen_latest_reviews,
 )
 
+COMMIT_MSG_DIFF_FORMAT = """
+diff --git a/%(source_filename)s b/%(target_filename)s
+new file mode 100644
+--- /dev/null
++++ b/%(target_filename)s
+@@ -0,0 +1,%(num_lines)s @@
+%(diff)s""".lstrip()
+
 logger = logging.getLogger(__name__)
 
 SUBMITTED_OR_DISCARDED_ERROR = '''
 Review request is submitted or discarded.
 You must reopen the review request before it can be updated.
 Review requests should only be reopened if your changes have not landed or have
 been backed out - file new bugs for follow-up work.
 '''.strip()
@@ -387,16 +403,17 @@ class BatchReviewRequestResource(WebAPIR
                     local_site=local_site)
 
             squashed_commit_data = fetch_commit_data(squashed_rr)
             squashed_commit_data.extra_data.update({
                 MOZREVIEW_KEY: True,
                 IDENTIFIER_KEY: identifier,
                 FIRST_PUBLIC_ANCESTOR_KEY: (
                     commits['squashed']['first_public_ancestor']),
+                HAS_COMMIT_MSG_FILEDIFF_KEY: True,
                 SQUASHED_KEY: True,
                 DISCARD_ON_PUBLISH_KEY: '[]',
                 UNPUBLISHED_KEY: '[]',
             })
             squashed_commit_data.draft_extra_data.update({
                 IDENTIFIER_KEY: identifier,
             })
             squashed_commit_data.save(
@@ -513,16 +530,22 @@ class BatchReviewRequestResource(WebAPIR
                 discard_on_publish_rids.remove(rid)
             except ValueError:
                 pass
 
         logger.info('%s: %d/%d commits mapped exactly' % (
                     identifier, len(processed_nodes),
                     len(commits['individual'])))
 
+        # Commit msg FileDiff should be created only if this is a completely
+        # new ReviewRequest, or if the ReviewRequest we're updating
+        # already had commit message FileDiff.
+        create_comm_msg_filediff = squashed_commit_data.extra_data.get(
+            HAS_COMMIT_MSG_FILEDIFF_KEY, False)
+
         # Find commits that map to a previous version.
         for commit in commits['individual']:
             node = commit['id']
             if node in processed_nodes:
                 continue
 
             # The client may have sent obsolescence data saying which commit this
             # commit has derived from. Use that data (if available) to try to find
@@ -538,17 +561,18 @@ class BatchReviewRequestResource(WebAPIR
 
                 del remaining_nodes[precursor]
                 unclaimed_rids.remove(rid)
 
                 rr = ReviewRequest.objects.get(pk=rid)
                 draft, warns = update_review_request(local_site, request,
                                                      privileged_user,
                                                      reviewer_cache, rr,
-                                                     commit)
+                                                     commit,
+                                                     create_comm_msg_filediff)
                 squashed_reviewers.update(u for u in draft.target_people.all())
                 warnings.extend(warns)
                 processed_nodes.add(node)
                 node_to_rid[node] = rid
                 review_requests[rid] = rr
                 review_data[rid] = get_review_request_data(rr)
 
                 try:
@@ -594,17 +618,18 @@ class BatchReviewRequestResource(WebAPIR
                 except KeyError:
                     pass
 
                 unclaimed_rids.remove(rr.id)
                 unclaimed_rrs.remove(rr)
                 draft, warns = update_review_request(local_site, request,
                                                      privileged_user,
                                                      reviewer_cache, rr,
-                                                     commit)
+                                                     commit,
+                                                     create_comm_msg_filediff)
                 squashed_reviewers.update(u for u in draft.target_people.all())
                 warnings.extend(warns)
                 processed_nodes.add(node)
                 node_to_rid[node] = rr.id
                 review_requests[rr.id] = rr
                 review_data[rr.id] = get_review_request_data(rr)
                 try:
                     discard_on_publish_rids.remove(rr.id)
@@ -648,17 +673,18 @@ class BatchReviewRequestResource(WebAPIR
 
                 logger.info('%s: mapping %s to unclaimed request %d' % (
                             identifier, node, assumed_old_rid))
 
                 rr = ReviewRequest.objects.get(pk=assumed_old_rid)
                 draft, warns = update_review_request(local_site, request,
                                                      privileged_user,
                                                      reviewer_cache, rr,
-                                                     commit)
+                                                     commit,
+                                                     create_comm_msg_filediff)
                 squashed_reviewers.update(u for u in draft.target_people.all())
                 warnings.extend(warns)
                 processed_nodes.add(commit['id'])
                 node_to_rid[node] = assumed_old_rid
                 review_requests[assumed_old_rid] = rr
                 review_data[assumed_old_rid] = get_review_request_data(rr)
 
                 try:
@@ -687,17 +713,18 @@ class BatchReviewRequestResource(WebAPIR
             })
             commit_data.save(
                 update_fields=['extra_data', 'draft_extra_data'])
 
             logger.info('%s: created review request %d for commit %s' % (
                         identifier, rr.id, node))
             draft, warns = update_review_request(local_site, request,
                                                  privileged_user,
-                                                 reviewer_cache, rr, commit)
+                                                 reviewer_cache, rr, commit,
+                                                 create_comm_msg_filediff)
             squashed_reviewers.update(u for u in draft.target_people.all())
             warnings.extend(warns)
             processed_nodes.add(commit['id'])
             node_to_rid[node] = rr.id
             review_requests[rr.id] = rr
             review_data[rr.id] = get_review_request_data(rr)
             unpublished_rids.append(rr.id)
 
@@ -904,39 +931,55 @@ def resolve_reviewers(cache, requested_r
             if not any(u.username == user.username for u in reviewers):
                 reviewers.add(user)
         else:
             unrecognized.add(reviewer)
 
     return reviewers, unrecognized
 
 
+def get_temp_diffset(repository):
+    """Get or create a temporary diffset.
+
+    Creating a FileDiff requires an already existing DiffSet
+    (There is a non-nullable ForeignKey). Since we must
+    create a particular FileDiff before its DiffSet is created
+    we first create a temporary DiffSet to point at.
+
+    Create this temporary DiffSet, or return it if it has been
+    created in the past."""
+    id = repository.extra_data.get(TEMP_DIFFSET_ID_KEY, None)
+    if id:
+        return DiffSet.objects.get(pk=id)
+    diffset = DiffSet.objects.create(
+        name='temp diffset',
+        revision=0,
+        repository=repository
+    )
+    repository.extra_data[TEMP_DIFFSET_ID_KEY] = diffset.pk
+    repository.save(update_fields=['extra_data'])
+    return diffset
+
+
 def update_review_request(local_site, request, privileged_user, reviewer_cache,
-                          rr, commit):
+                          rr, commit, create_commit_msg_filediff):
     """Synchronize the state of a review request with a commit.
 
     Updates the commit message, refreshes the diff, etc.
     """
     try:
         draft = rr.draft.get()
     except ReviewRequestDraft.DoesNotExist:
         draft = ReviewRequestDraft.create(rr)
 
     draft.summary = commit['message'].splitlines()[0]
     draft.description = commit['message']
     draft.bugs_closed = commit['bug']
 
     commit_data = fetch_commit_data(draft)
-    commit_data.draft_extra_data.update({
-        AUTHOR_KEY: commit['author'],
-        COMMIT_ID_KEY: commit['id'],
-        FIRST_PUBLIC_ANCESTOR_KEY: commit['first_public_ancestor'],
-    })
-    commit_data.save(
-        update_fields=['draft_extra_data'])
 
     reviewer_users, unrecognized_reviewers = \
         resolve_reviewers(reviewer_cache, commit.get('reviewers', []))
     requal_reviewer_users, unrecognized_requal_reviewers = \
         resolve_reviewers(reviewer_cache, commit.get('requal_reviewers', []))
 
     warnings = []
 
@@ -950,16 +993,56 @@ def update_review_request(local_site, re
             if not pr.get(user.username, False):
                 warnings.append('commit message for %s has r=%s but they '
                                 'have not granted a ship-it. review will be '
                                 'requested on your behalf' % (
                                 commit['id'][:12], user.username))
 
         reviewer_users |= requal_reviewer_users
 
+    # Commit message FileDiff creation.
+    base_commit_id = commit.get('base_commit_id')
+    commit_message_filediff = None
+
+    if create_commit_msg_filediff:
+        # Prepare commit message data
+        commit_message_name = commit_data.draft_extra_data.get(
+            COMMIT_MSG_FILENAME_KEY,
+            'commit-message-%s' % base_commit_id[0:5])
+        commit_message_lines = commit['message'].split('\n')
+        commit_message_diff = COMMIT_MSG_DIFF_FORMAT % {
+            'source_filename': commit_message_name,
+            'target_filename': commit_message_name,
+            'num_lines': len(commit_message_lines),
+            'diff': '%s\n' % '\n'.join(
+                ['+%s' % l for l in commit_message_lines])}
+
+        commit_data.extra_data[COMMIT_MSG_FILENAME_KEY] = commit_message_name
+
+        # Commit message FileDiff has to be displayed as the first one.
+        # Therefore it needs to be created before other FileDiffs in
+        # the DiffSet.
+        # FileDiff object has a required DiffSet field. Because target DiffSet
+        # is created along with other FileDiffs, there is a need to
+        # create a temporary one.
+        # Later in the code temporary DiffSet is replaced in the commit
+        # message FileDiff with the target one.
+        temp_diffset = get_temp_diffset(rr.repository)
+
+        commit_message_filediff = FileDiff.objects.create(
+            diffset=temp_diffset,
+            source_file=commit_message_name,
+            dest_file=commit_message_name,
+            source_revision=PRE_CREATION,
+            dest_detail='',
+            parent_diff='',
+            binary=False,
+            status='M',
+            diff=commit_message_diff)
+
     # Carry over from last time unless commit message overrules.
     if reviewer_users:
         draft.target_people.clear()
     for user in sorted(reviewer_users):
         draft.target_people.add(user)
         logger.debug('adding reviewer %s to #%d' % (user.username, rr.id))
 
     try:
@@ -967,28 +1050,50 @@ def update_review_request(local_site, re
             repository=rr.repository,
             diff_file_name='diff',
             diff_file_contents=commit['diff_b64'].encode('ascii').decode('base64'),
             parent_diff_file_name='diff',
             parent_diff_file_contents=None,
             diffset_history=None,
             basedir='',
             request=request,
-            base_commit_id=commit.get('base_commit_id'),
+            base_commit_id=base_commit_id,
             save=True,
         )
+
         update_diffset_history(rr, diffset)
         diffset.save()
 
         DiffSetVerification(diffset=diffset).save(
             authorized_user=privileged_user, force_insert=True)
     except Exception:
         logger.exception('error processing diff')
         raise DiffProcessingException()
 
+    # Now that the proper DiffSet has been created, re-assign
+    # the commit message FileDiff we created to the new DiffSet.
+    if commit_message_filediff:
+        commit_message_filediff.diffset = diffset
+        commit_message_filediff.save()
+
+        commit_msg_filediff_ids = json.loads(commit_data.draft_extra_data.get(
+            COMMIT_MSG_FILEDIFF_IDS_KEY, '{}'))
+        commit_msg_filediff_ids[
+            str(diffset.revision)] = commit_message_filediff.pk
+        # Store commit message FileDiffs ids in extra_data
+        commit_data.draft_extra_data[COMMIT_MSG_FILEDIFF_IDS_KEY] = json.dumps(
+            commit_msg_filediff_ids)
+
+    commit_data.draft_extra_data.update({
+        AUTHOR_KEY: commit['author'],
+        COMMIT_ID_KEY: commit['id'],
+        FIRST_PUBLIC_ANCESTOR_KEY: commit['first_public_ancestor'],
+    })
+    commit_data.save(update_fields=['draft_extra_data', 'extra_data'])
+
     update_review_request_draft_diffset(rr, diffset, draft=draft)
 
     return draft, warnings
 
 
 def get_review_request_data(rr):
     """Obtain a dictionary containing review request metadata.
 
--- a/pylib/mozreview/mozreview/static/mozreview/css/viewdiff.less
+++ b/pylib/mozreview/mozreview/static/mozreview/css/viewdiff.less
@@ -101,8 +101,38 @@ span.ghost-commentflag {
 /* make review comments more noticable */
 
 .comment-block-container {
   border-top: 2px dotted #a1c0e3;  // @comment-flag-color
 }
 .comment-block-container-draft {
   border-top: 2px dotted #aceb6f;  // @comment-flag-draft-color;
 }
+
+/* Select commit message FileDiff.
+   We're adding a content of the commit message as a FileDiff. FileDiff's DOM
+   Element is a div.diff-container. Commit message element is displayed as
+   a first FileDiff in the series as a second child of #diffs, after
+   a div.diff-highlight. Due to a bug in Reviewboard, diff-highlight might not
+   be present and commit message FileDiff will become the first child of
+   the #diff.
+   See https://hellosplat.com/s/beanbag/tickets/4520/
+
+   We use an ugly CSS hack to remove "commit-message-[hash]" text from
+   sight and instead display "commit-message".
+ */
+.with-commit-msg-filediff {
+    .diff-container:first-child .sidebyside,
+    :not(.diff-container) + .diff-container .sidebyside {
+        .filename-row th {
+            color: transparent;
+            position: relative;
+        }
+
+       .filename-row th a:after{
+           content: "commit-message";
+           color: #000;
+           position: absolute;
+           font-weight: normal;
+           font-style: italic;
+       }
+    }
+}
--- a/pylib/mozreview/mozreview/static/mozreview/js/init_filediffreviewer.js
+++ b/pylib/mozreview/mozreview/static/mozreview/js/init_filediffreviewer.js
@@ -103,9 +103,62 @@
     // numbers separated by a dash.
     if (revision.indexOf('-') === -1) {
       // We still need to wait for an update of the FileDiff collection
       // to make sure we trigger a render of the diff buttons AFTER
       // the DiffFile views are rendered.
       page.model.get('files').once('update', renderDiffButton);
     }
   });
+
+  // Restyle and modify content if commit message FileDiff is present.
+  // commitMsgIds is an array holding a commit message FileDiff ids in
+  // relation to Revision numbers.
+  var commitMsgIds = [];
+  var commitMsgData = $('#commit-message-filediff-data');
+  var currentRevisionNumber = commitMsgData.data('currentRevisionNumber');
+  if (currentRevisionNumber) {
+    commitMsgIds = commitMsgData.data('commitMessageIds');
+  }
+
+  function changeCommitMsgIndexFileName() {
+    // Change the fileName of the first FileDiff index if commit msg
+    // FileDiff is present.
+    $('.with-commit-msg-filediff #diff_index tbody tr:first td a')
+      .text('commit-message');
+  }
+
+  function changeCommitMsgFileDiff(page) {
+    // Commit message FileDiff element is displayed only on the first page.
+    if (page !== 1) {
+      // Remove styling of the first FileDiff element
+      $('#review_request').removeClass('with-commit-msg-filediff');
+      return;
+    }
+    // Add styling of the first FileDiff element
+    $('#review_request').addClass('with-commit-msg-filediff');
+    // Set the diff_index's commit message fileName
+    changeCommitMsgIndexFileName();
+  }
+
+  function detectPageAndChangeCommitMsgFileDiff() {
+    var page;
+    if ($('#pagination1').children().length == 0) {
+      page = 1;
+    } else {
+      page = +$('#pagination1 span.paginate-current').text();
+    }
+    changeCommitMsgFileDiff(page);
+  }
+
+  // Old ReviewRequests don't have commit message FileDiff. In such case
+  // commitMsgIds is an empty array.
+  if (currentRevisionNumber && commitMsgIds[currentRevisionNumber]) {
+    // Check the initial stage of the pager.
+    detectPageAndChangeCommitMsgFileDiff();
+    // Listen to pagination events.
+    page._paginationView1.on('pageSelected', changeCommitMsgFileDiff);
+    page._paginationView2.on('pageSelected', changeCommitMsgFileDiff);
+    // Listen to context loaded event and change name if needed.
+    $(document).on('mr:diff-context-loaded',
+                   detectPageAndChangeCommitMsgFileDiff);
+  }
 });
new file mode 100644
--- /dev/null
+++ b/pylib/mozreview/mozreview/templates/mozreview/diff-data.html
@@ -0,0 +1,5 @@
+{% load mozreview %}
+
+<div id="commit-message-filediff-data"
+  data-commit-message-ids='{{ review_request|commit_message_filediff_ids|safe }}'
+  data-current-revision-number={{ diffset.revision }}></div>
--- a/pylib/mozreview/mozreview/templatetags/mozreview.py
+++ b/pylib/mozreview/mozreview/templatetags/mozreview.py
@@ -7,16 +7,17 @@ from django.utils.safestring import Safe
 from mozreview.diffs import (
     latest_revision_reviewed,
 )
 from mozreview.diffviewer import (
     get_diffstats,
 )
 from mozreview.extra_data import (
     COMMIT_ID_KEY,
+    COMMIT_MSG_FILEDIFF_IDS_KEY,
     fetch_commit_data,
     is_parent,
     is_pushed,
     REVIEW_FLAG_KEY,
 )
 from mozreview.review_helpers import get_reviewers_status
 
 from reviewboard.reviews.models import (
@@ -44,16 +45,24 @@ def isDraft(review_request):
 
 @register.filter()
 def commit_id(review_request_details):
     """Return the commit id of a review request or review request draft"""
     commit_data = fetch_commit_data(review_request_details)
     return str(commit_data.get_for(review_request_details, COMMIT_ID_KEY))
 
 
+@register.filter()
+def commit_message_filediff_ids(review_request_details):
+    """Return the commit message FileDiff ids of a ReviewRequest or Draft"""
+    commit_data = fetch_commit_data(review_request_details)
+    return commit_data.get_for(review_request_details,
+                               COMMIT_MSG_FILEDIFF_IDS_KEY, default='[]')
+
+
 def reviewer_list(review_request):
     return ', '.join([user.username
                       for user in review_request.target_people.all()])
 
 
 @register.filter()
 def extra_data(review_request, key):
     return review_request.extra_data[key]