Merge branch 'stable-2.6'

* stable-2.6:
  Support triggering GC for one project via REST
  Add support to retrieve repository statistics for a project via REST
  Document the response codes that are returned by the REST endpoints
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index 2ccd39a..71edfec 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -257,6 +257,41 @@
   Location: https://profiles/avatar/john_doe.jpeg?s=20x20
 ----
 
+[[get-diff-preferences]]
+Get Diff Preferences
+~~~~~~~~~~~~~~~~~~~~
+[verse]
+'GET /accounts/link:#account-id[\{account-id\}]/preferences.diff'
+
+Retrieves the diff preferences of a user.
+
+.Request
+----
+  GET /a/accounts/self/preferences.diff HTTP/1.0
+----
+
+As result the diff preferences of the user are returned as a
+link:#diff-preferences-info[DiffPreferencesInfo] entity.
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json;charset=UTF-8
+
+  )]}'
+  {
+    "context": 10,
+    "ignore_whitespace": "IGNORE_ALL_SPACE",
+    "intraline_difference": true,
+    "line_length": 100,
+    "show_tabs": true,
+    "show_whitespace_errors": true,
+    "syntax_highlighting": true,
+    "tab_size": 8
+  }
+----
+
 
 [[ids]]
 IDs
@@ -349,6 +384,52 @@
 capability.
 |=================================
 
+[[diff-preferences-info]]
+DiffPreferencesInfo
+~~~~~~~~~~~~~~~~~~~
+The `DiffPreferencesInfo` entity contains information about the diff
+preferences of a user.
+
+[options="header",width="50%",cols="1,^1,5"]
+|=====================================
+|Field Name              ||Description
+|`context`               ||
+The number of lines of context when viewing a patch.
+|`expand_all_comments`   |not set if `false`|
+Whether all inline comments should be automatically expanded.
+|`ignore_whitespace`     ||
+Whether whitespace changes should be ignored and if yes, which
+whitespace changes should be ignored. +
+Allowed values are `IGNORE_NONE`, `IGNORE_SPACE_AT_EOL`,
+`IGNORE_SPACE_CHANGE`, `IGNORE_ALL_SPACE`.
+|`intraline_difference`  |not set if `false`|
+Whether intraline differences should be highlighted.
+|`line_length`           ||
+Number of characters that should be displayed in one line.
+|`manual_review`         |not set if `false`|
+Whether the 'Reviewed' flag should not be set automatically on a patch
+when it is viewed.
+|`retain_header`         |not set if `false`|
+Whether the header that is displayed above the patch (that either shows
+the commit message, the diff preferences, the patch sets or the files)
+should be retained on file switch.
+|`show_line_endings`     |not set if `false`|
+Whether Windows EOL/Cr-Lf should be displayed as '\r' in a dotted-line
+box.
+|`show_tabs`             |not set if `false`|
+Whether tabs should be shown.
+|`show_whitespace_errors`|not set if `false`|
+Whether whitespace errors should be shown.
+|`skip_deleted`          |not set if `false`|
+Whether deleted files should be skipped on file switch.
+|`skip_uncommented`      |not set if `false`|
+Whether uncommented files should be skipped on file switch.
+|`syntax_highlighting`   |not set if `false`|
+Whether syntax highlighting should be enabled.
+|`tab_size`              ||
+Number of spaces that should be used to display one tab.
+|=====================================
+
 [[query-limit-info]]
 QueryLimitInfo
 ~~~~~~~~~~~~~~
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 696a174..51dcdd1 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -1518,6 +1518,99 @@
   HTTP/1.1 204 No Content
 ----
 
+[[list-comments]]
+List Comments
+~~~~~~~~~~~~~
+[verse]
+'GET /changes/link:#change-id[\{change-id\}]/revisions/link:#revision-id[\{revision-id\}]/comments/'
+
+Lists the published comments of a revision.
+
+As result a map is returned that maps the file path to a list of
+link:#comment-info[CommentInfo] entries. The entries in the map are
+sorted by file path.
+
+.Request
+----
+  GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/674ac754f91e64a0efb8087e59a176484bd534d1/comments/ HTTP/1.0
+----
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json;charset=UTF-8
+
+  )]}'
+  {
+    "gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java": [
+      {
+        "kind": "gerritcodereview#comment",
+        "id": "TvcXrmjM",
+        "line": 23,
+        "message": "[nit] trailing whitespace",
+        "updated": "2013-02-26 15:40:43.986000000",
+        "author": {
+          "_account_id": 1000096,
+          "name": "John Doe",
+          "email": "john.doe@example.com"
+        }
+      },
+      {
+        "kind": "gerritcodereview#comment",
+        "id": "TveXwFiA",
+        "line": 49,
+        "in_reply_to": "TfYX-Iuo",
+        "message": "Done",
+        "updated": "2013-02-26 15:40:45.328000000",
+        "author": {
+          "_account_id": 1000097,
+          "name": "Jane Roe",
+          "email": "jane.roe@example.com"
+        }
+      }
+    ]
+  }
+----
+
+[[get-comment]]
+Get Comment
+~~~~~~~~~~~
+[verse]
+'GET /changes/link:#change-id[\{change-id\}]/revisions/link:#revision-id[\{revision-id\}]/comments/link:#comment-id[\{comment-id\}]'
+
+Retrieves a published comment of a revision.
+
+.Request
+----
+  GET /changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940/revisions/674ac754f91e64a0efb8087e59a176484bd534d1/comments/TvcXrmjM HTTP/1.0
+----
+
+As response a link:#comment-info[CommentInfo] entity is returned that
+describes the published comment.
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json;charset=UTF-8
+
+  )]}'
+  {
+    "kind": "gerritcodereview#comment",
+    "id": "TvcXrmjM",
+    "path": "gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java",
+    "line": 23,
+    "message": "[nit] trailing whitespace",
+    "updated": "2013-02-26 15:40:43.986000000",
+    "author": {
+      "_account_id": 1000096,
+      "name": "John Doe",
+      "email": "john.doe@example.com"
+    }
+  }
+----
+
 [[set-reviewed]]
 Set Reviewed
 ~~~~~~~~~~~~
@@ -1582,6 +1675,11 @@
   ("I8473b95934b5732ac55d26311a706c9c2bde9940")
 * a legacy numeric change ID ("4247")
 
+[[comment-id]]
+\{comment-id\}
+~~~~~~~~~~~~~~
+UUID of a published comment.
+
 [[draft-id]]
 \{draft-id\}
 ~~~~~~~~~~~~
@@ -1739,7 +1837,7 @@
 |===========================
 |Field Name    ||Description
 |`kind`        ||`gerritcodereview#comment`
-|`id`          ||The URL encoded UUID of the draft comment.
+|`id`          ||The URL encoded UUID of the comment.
 |`path`        |optional|
 The path of the file for which the inline comment was done. +
 Not set if returned in a map where the key is the file path.
@@ -1756,6 +1854,10 @@
 |`updated`     ||
 The link:rest-api.html#timestamp[timestamp] of when this comment was
 written.
+|`author`      |optional|
+The author of the message as an +
+link:rest-api-accounts.html#account-info[AccountInfo] entity. +
+Unset for draft comments, assumed to be the calling user.
 |===========================
 
 [[comment-input]]
diff --git a/gerrit-acceptance-tests/pom.xml b/gerrit-acceptance-tests/pom.xml
index 410ae66..879be10 100644
--- a/gerrit-acceptance-tests/pom.xml
+++ b/gerrit-acceptance-tests/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-acceptance-tests</artifactId>
diff --git a/gerrit-antlr/pom.xml b/gerrit-antlr/pom.xml
index 314d8d2..e1eba40 100644
--- a/gerrit-antlr/pom.xml
+++ b/gerrit-antlr/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-antlr</artifactId>
diff --git a/gerrit-cache-h2/pom.xml b/gerrit-cache-h2/pom.xml
index 1a26b21..4abd77c 100644
--- a/gerrit-cache-h2/pom.xml
+++ b/gerrit-cache-h2/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-cache-h2</artifactId>
diff --git a/gerrit-common/pom.xml b/gerrit-common/pom.xml
index 1db3549..d9173f0 100644
--- a/gerrit-common/pom.xml
+++ b/gerrit-common/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-common</artifactId>
diff --git a/gerrit-extension-api/pom.xml b/gerrit-extension-api/pom.xml
index 1080ed1..4f57209 100644
--- a/gerrit-extension-api/pom.xml
+++ b/gerrit-extension-api/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-extension-api</artifactId>
diff --git a/gerrit-gwtdebug/pom.xml b/gerrit-gwtdebug/pom.xml
index 942f507..0622e1b 100644
--- a/gerrit-gwtdebug/pom.xml
+++ b/gerrit-gwtdebug/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-gwtdebug</artifactId>
diff --git a/gerrit-gwtui/pom.xml b/gerrit-gwtui/pom.xml
index 8d807a5..7f72ea4 100644
--- a/gerrit-gwtui/pom.xml
+++ b/gerrit-gwtui/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-gwtui</artifactId>
diff --git a/gerrit-httpd/pom.xml b/gerrit-httpd/pom.xml
index 65641aa..87f2f10 100644
--- a/gerrit-httpd/pom.xml
+++ b/gerrit-httpd/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-httpd</artifactId>
diff --git a/gerrit-launcher/pom.xml b/gerrit-launcher/pom.xml
index 4f33ce8..396c09c 100644
--- a/gerrit-launcher/pom.xml
+++ b/gerrit-launcher/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-launcher</artifactId>
diff --git a/gerrit-main/pom.xml b/gerrit-main/pom.xml
index d174af1..25c9401 100644
--- a/gerrit-main/pom.xml
+++ b/gerrit-main/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-main</artifactId>
diff --git a/gerrit-openid/pom.xml b/gerrit-openid/pom.xml
index bb8b93d..b232496 100644
--- a/gerrit-openid/pom.xml
+++ b/gerrit-openid/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-openid</artifactId>
diff --git a/gerrit-package-plugins/pom.xml b/gerrit-package-plugins/pom.xml
index bf1fe41..f7fd22c 100644
--- a/gerrit-package-plugins/pom.xml
+++ b/gerrit-package-plugins/pom.xml
@@ -22,7 +22,7 @@
   <groupId>com.google.gerrit</groupId>
   <artifactId>gerrit-package-plugins</artifactId>
   <packaging>war</packaging>
-  <version>2.6-SNAPSHOT</version>
+  <version>2.7-SNAPSHOT</version>
 
   <name>Gerrit Code Review - Package Plugins</name>
   <url>http://code.google.com/p/gerrit/</url>
diff --git a/gerrit-patch-commonsnet/pom.xml b/gerrit-patch-commonsnet/pom.xml
index 78ca61c..7718721 100644
--- a/gerrit-patch-commonsnet/pom.xml
+++ b/gerrit-patch-commonsnet/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-patch-commonsnet</artifactId>
diff --git a/gerrit-patch-jgit/pom.xml b/gerrit-patch-jgit/pom.xml
index 7e50af8..222d622 100644
--- a/gerrit-patch-jgit/pom.xml
+++ b/gerrit-patch-jgit/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-patch-jgit</artifactId>
diff --git a/gerrit-pgm/pom.xml b/gerrit-pgm/pom.xml
index f73a2b0..c7121c7a 100644
--- a/gerrit-pgm/pom.xml
+++ b/gerrit-pgm/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-pgm</artifactId>
diff --git a/gerrit-plugin-api/pom.xml b/gerrit-plugin-api/pom.xml
index 81a50984..0abe81f 100644
--- a/gerrit-plugin-api/pom.xml
+++ b/gerrit-plugin-api/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-plugin-api</artifactId>
diff --git a/gerrit-plugin-archetype/pom.xml b/gerrit-plugin-archetype/pom.xml
index 71feabc..9b954f9 100644
--- a/gerrit-plugin-archetype/pom.xml
+++ b/gerrit-plugin-archetype/pom.xml
@@ -21,7 +21,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-plugin-archetype</artifactId>
diff --git a/gerrit-plugin-gwt-archetype/pom.xml b/gerrit-plugin-gwt-archetype/pom.xml
index 91cd142..66f5a8f 100644
--- a/gerrit-plugin-gwt-archetype/pom.xml
+++ b/gerrit-plugin-gwt-archetype/pom.xml
@@ -21,7 +21,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-plugin-gwt-archetype</artifactId>
diff --git a/gerrit-plugin-gwtui/pom.xml b/gerrit-plugin-gwtui/pom.xml
index 1460870..5195204 100644
--- a/gerrit-plugin-gwtui/pom.xml
+++ b/gerrit-plugin-gwtui/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-plugin-gwtui</artifactId>
diff --git a/gerrit-plugin-js-archetype/pom.xml b/gerrit-plugin-js-archetype/pom.xml
index 1f0f53a..5b12f7d 100644
--- a/gerrit-plugin-js-archetype/pom.xml
+++ b/gerrit-plugin-js-archetype/pom.xml
@@ -21,7 +21,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-plugin-js-archetype</artifactId>
diff --git a/gerrit-prettify/pom.xml b/gerrit-prettify/pom.xml
index 4db1056..2479adc 100644
--- a/gerrit-prettify/pom.xml
+++ b/gerrit-prettify/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-prettify</artifactId>
diff --git a/gerrit-reviewdb/pom.xml b/gerrit-reviewdb/pom.xml
index 96599b3..d69c5c5 100644
--- a/gerrit-reviewdb/pom.xml
+++ b/gerrit-reviewdb/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-reviewdb</artifactId>
diff --git a/gerrit-server/pom.xml b/gerrit-server/pom.xml
index 0b13fbd..68b1625 100644
--- a/gerrit-server/pom.xml
+++ b/gerrit-server/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-server</artifactId>
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetDiffPreferences.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetDiffPreferences.java
new file mode 100644
index 0000000..8e65a23
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetDiffPreferences.java
@@ -0,0 +1,78 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.account;
+
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.reviewdb.client.AccountDiffPreference;
+import com.google.gerrit.reviewdb.client.AccountDiffPreference.Whitespace;
+import com.google.gerrit.server.CurrentUser;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+public class GetDiffPreferences implements RestReadView<AccountResource> {
+
+  private final Provider<CurrentUser> self;
+
+  @Inject
+  GetDiffPreferences(Provider<CurrentUser> self) {
+    this.self = self;
+  }
+
+  @Override
+  public DiffPreferencesInfo apply(AccountResource rsrc) throws AuthException {
+    if (self.get() != rsrc.getUser()
+        && !self.get().getCapabilities().canAdministrateServer()) {
+      throw new AuthException("restricted to administrator");
+    }
+    return DiffPreferencesInfo.parse(rsrc.getUser().getAccountDiffPreference());
+  }
+
+  static class DiffPreferencesInfo {
+    static DiffPreferencesInfo parse(AccountDiffPreference p) {
+      DiffPreferencesInfo info = new DiffPreferencesInfo();
+      info.context = p.getContext();
+      info.expandAllComments = p.isExpandAllComments() ? true : null;
+      info.ignoreWhitespace = p.getIgnoreWhitespace();
+      info.intralineDifference = p.isIntralineDifference() ? true : null;
+      info.lineLength = p.getLineLength();
+      info.manualReview = p.isManualReview() ? true : null;
+      info.retainHeader = p.isRetainHeader() ? true : null;
+      info.showLineEndings = p.isShowLineEndings() ? true : null;
+      info.showTabs = p.isShowTabs() ? true : null;
+      info.showWhitespaceErrors = p.isShowWhitespaceErrors() ? true : null;
+      info.skipDeleted = p.isSkipDeleted() ? true : null;
+      info.skipUncommented = p.isSkipUncommented() ? true : null;
+      info.syntaxHighlighting = p.isSyntaxHighlighting() ? true : null;
+      info.tabSize = p.getTabSize();
+      return info;
+    }
+
+    short context;
+    Boolean expandAllComments;
+    Whitespace ignoreWhitespace;
+    Boolean intralineDifference;
+    int lineLength;
+    Boolean manualReview;
+    Boolean retainHeader;
+    Boolean showLineEndings;
+    Boolean showTabs;
+    Boolean showWhitespaceErrors;
+    Boolean skipDeleted;
+    Boolean skipUncommented;
+    Boolean syntaxHighlighting;
+    int tabSize;
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java
index ac045f75e9..17cf479 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java
@@ -33,6 +33,7 @@
     get(ACCOUNT_KIND, "avatar").to(GetAvatar.class);
     child(ACCOUNT_KIND, "capabilities").to(Capabilities.class);
     get(ACCOUNT_KIND, "groups").to(GetGroups.class);
+    get(ACCOUNT_KIND, "preferences.diff").to(GetDiffPreferences.class);
     get(CAPABILITY_KIND).to(GetCapabilities.CheckOne.class);
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentInfo.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentInfo.java
new file mode 100644
index 0000000..798b9c6
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentInfo.java
@@ -0,0 +1,55 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.package com.google.gerrit.server.change;
+
+package com.google.gerrit.server.change;
+
+import com.google.common.base.Strings;
+import com.google.gerrit.extensions.restapi.Url;
+import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.server.account.AccountInfo;
+
+import java.sql.Timestamp;
+
+public class CommentInfo {
+  static enum Side {
+    PARENT, REVISION;
+  }
+
+  final String kind = "gerritcodereview#comment";
+  String id;
+  String path;
+  Side side;
+  Integer line;
+  String inReplyTo;
+  String message;
+  Timestamp updated;
+  AccountInfo author;
+
+  CommentInfo(PatchLineComment c, AccountInfo.Loader accountLoader) {
+    id = Url.encode(c.getKey().get());
+    path = c.getKey().getParentKey().getFileName();
+    if (c.getSide() == 0) {
+      side = Side.PARENT;
+    }
+    if (c.getLine() > 0) {
+      line = c.getLine();
+    }
+    inReplyTo = Url.encode(c.getParentUuid());
+    message = Strings.emptyToNull(c.getMessage());
+    updated = c.getWrittenOn();
+    if (accountLoader != null) {
+      author = accountLoader.get(c.getAuthor());
+    }
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentResource.java
new file mode 100644
index 0000000..ec47d01
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CommentResource.java
@@ -0,0 +1,51 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.change;
+
+import com.google.gerrit.extensions.restapi.RestResource;
+import com.google.gerrit.extensions.restapi.RestView;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.inject.TypeLiteral;
+
+public class CommentResource implements RestResource {
+  public static final TypeLiteral<RestView<CommentResource>> COMMENT_KIND =
+      new TypeLiteral<RestView<CommentResource>>() {};
+
+  private final RevisionResource rev;
+  private final PatchLineComment comment;
+
+  CommentResource(RevisionResource rev, PatchLineComment c) {
+    this.rev = rev;
+    this.comment = c;
+  }
+
+  public PatchSet getPatchSet() {
+    return rev.getPatchSet();
+  }
+
+  PatchLineComment getComment() {
+    return comment;
+  }
+
+  String getId() {
+    return comment.getKey().get();
+  }
+
+  Account.Id getAuthorId() {
+    return comment.getAuthor();
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Comments.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Comments.java
new file mode 100644
index 0000000..91cfbf8
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Comments.java
@@ -0,0 +1,64 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.change;
+
+import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.restapi.ChildCollection;
+import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.RestView;
+import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+class Comments implements ChildCollection<RevisionResource, CommentResource> {
+  private final DynamicMap<RestView<CommentResource>> views;
+  private final Provider<ListComments> list;
+  private final Provider<ReviewDb> dbProvider;
+
+  @Inject
+  Comments(DynamicMap<RestView<CommentResource>> views,
+      Provider<ListComments> list,
+      Provider<ReviewDb> dbProvider) {
+    this.views = views;
+    this.list = list;
+    this.dbProvider = dbProvider;
+  }
+
+  @Override
+  public DynamicMap<RestView<CommentResource>> views() {
+    return views;
+  }
+
+  @Override
+  public RestView<RevisionResource> list() {
+    return list.get();
+  }
+
+  @Override
+  public CommentResource parse(RevisionResource rev, IdString id)
+      throws ResourceNotFoundException, OrmException {
+    String uuid = id.get();
+    for (PatchLineComment c : dbProvider.get().patchComments()
+        .publishedByPatchSet(rev.getPatchSet().getId())) {
+      if (uuid.equals(c.getKey().get())) {
+        return new CommentResource(rev, c);
+      }
+    }
+    throw new ResourceNotFoundException(id);
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraft.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraft.java
index 348be97..5157af4 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraft.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CreateDraft.java
@@ -42,7 +42,7 @@
   }
 
   @Override
-  public Response<GetDraft.Comment> apply(RevisionResource rsrc, Input in)
+  public Response<CommentInfo> apply(RevisionResource rsrc, Input in)
       throws AuthException, BadRequestException, ResourceConflictException, OrmException {
     if (Strings.isNullOrEmpty(in.path)) {
       throw new BadRequestException("path must be non-empty");
@@ -60,9 +60,9 @@
         rsrc.getAccountId(),
         Url.decode(in.inReplyTo));
     c.setStatus(Status.DRAFT);
-    c.setSide(in.side == GetDraft.Side.PARENT ? (short) 0 : (short) 1);
+    c.setSide(in.side == CommentInfo.Side.PARENT ? (short) 0 : (short) 1);
     c.setMessage(in.message.trim());
     db.get().patchComments().insert(Collections.singleton(c));
-    return Response.created(new GetDraft.Comment(c));
+    return Response.created(new CommentInfo(c, null));
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetComment.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetComment.java
new file mode 100644
index 0000000..68b0435
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetComment.java
@@ -0,0 +1,38 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.change;
+
+import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.server.account.AccountInfo;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+
+class GetComment implements RestReadView<CommentResource> {
+
+  private final AccountInfo.Loader.Factory accountLoaderFactory;
+
+  @Inject
+  GetComment(AccountInfo.Loader.Factory accountLoaderFactory) {
+    this.accountLoaderFactory = accountLoaderFactory;
+  }
+
+  @Override
+  public Object apply(CommentResource rsrc) throws OrmException {
+    AccountInfo.Loader accountLoader = accountLoaderFactory.create(true);
+    CommentInfo ci = new CommentInfo(rsrc.getComment(), accountLoader);
+    accountLoader.fill();
+    return ci;
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDraft.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDraft.java
index 596659d..6b36048 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDraft.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/GetDraft.java
@@ -14,49 +14,15 @@
 
 package com.google.gerrit.server.change;
 
-import com.google.common.base.Strings;
 import com.google.gerrit.extensions.restapi.AuthException;
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.extensions.restapi.RestReadView;
-import com.google.gerrit.extensions.restapi.Url;
-import com.google.gerrit.reviewdb.client.PatchLineComment;
-
-import java.sql.Timestamp;
 
 class GetDraft implements RestReadView<DraftResource> {
   @Override
   public Object apply(DraftResource rsrc) throws AuthException,
       BadRequestException, ResourceConflictException, Exception {
-    return new Comment(rsrc.getComment());
-  }
-
-  static enum Side {
-    PARENT, REVISION;
-  }
-
-  static class Comment {
-    final String kind = "gerritcodereview#comment";
-    String id;
-    String path;
-    Side side;
-    Integer line;
-    String inReplyTo;
-    String message;
-    Timestamp updated;
-
-    Comment(PatchLineComment c) {
-      id = Url.encode(c.getKey().get());
-      path = c.getKey().getParentKey().getFileName();
-      if (c.getSide() == 0) {
-        side = Side.PARENT;
-      }
-      if (c.getLine() > 0) {
-        line = c.getLine();
-      }
-      inReplyTo = Url.encode(c.getParentUuid());
-      message = Strings.emptyToNull(c.getMessage());
-      updated = c.getWrittenOn();
-    }
+    return new CommentInfo(rsrc.getComment(), null);
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListComments.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListComments.java
new file mode 100644
index 0000000..23a71e8
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListComments.java
@@ -0,0 +1,40 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.change;
+
+import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.account.AccountInfo;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+class ListComments extends ListDrafts {
+  @Inject
+  ListComments(Provider<ReviewDb> db, AccountInfo.Loader.Factory alf) {
+    super(db, alf);
+  }
+
+  @Override
+  protected boolean includeAuthorInfo() {
+    return true;
+  }
+
+  protected Iterable<PatchLineComment> listComments(RevisionResource rsrc)
+      throws OrmException {
+    return db.get().patchComments()
+        .publishedByPatchSet(rsrc.getPatchSet().getId());
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListDrafts.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListDrafts.java
index 564363e..47863ac 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ListDrafts.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ListDrafts.java
@@ -24,8 +24,10 @@
 import com.google.gerrit.extensions.restapi.RestReadView;
 import com.google.gerrit.reviewdb.client.PatchLineComment;
 import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.change.GetDraft.Comment;
-import com.google.gerrit.server.change.GetDraft.Side;
+import com.google.gerrit.server.account.AccountInfo;
+import com.google.gerrit.server.change.CommentInfo;
+import com.google.gerrit.server.change.CommentInfo.Side;
+import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
@@ -35,23 +37,36 @@
 import java.util.Map;
 
 class ListDrafts implements RestReadView<RevisionResource> {
-  private final Provider<ReviewDb> db;
+  protected final Provider<ReviewDb> db;
+  private final AccountInfo.Loader.Factory accountLoaderFactory;
 
   @Inject
-  ListDrafts(Provider<ReviewDb> db) {
+  ListDrafts(Provider<ReviewDb> db, AccountInfo.Loader.Factory alf) {
     this.db = db;
+    this.accountLoaderFactory = alf;
+  }
+
+  protected Iterable<PatchLineComment> listComments(RevisionResource rsrc)
+      throws OrmException {
+    return db.get().patchComments()
+        .draftByPatchSetAuthor(
+            rsrc.getPatchSet().getId(),
+            rsrc.getAccountId());
+  }
+
+  protected boolean includeAuthorInfo() {
+    return false;
   }
 
   @Override
   public Object apply(RevisionResource rsrc) throws AuthException,
       BadRequestException, ResourceConflictException, Exception {
-    Map<String, List<Comment>> out = Maps.newTreeMap();
-    for (PatchLineComment c : db.get().patchComments()
-        .draftByPatchSetAuthor(
-            rsrc.getPatchSet().getId(),
-            rsrc.getAccountId())) {
-      Comment o = new Comment(c);
-      List<Comment> list = out.get(o.path);
+    Map<String, List<CommentInfo>> out = Maps.newTreeMap();
+    AccountInfo.Loader accountLoader =
+        includeAuthorInfo() ? accountLoaderFactory.create(true) : null;
+    for (PatchLineComment c : listComments(rsrc)) {
+      CommentInfo o = new CommentInfo(c, accountLoader);
+      List<CommentInfo> list = out.get(o.path);
       if (list == null) {
         list = Lists.newArrayList();
         out.put(o.path, list);
@@ -59,10 +74,10 @@
       o.path = null;
       list.add(o);
     }
-    for (List<Comment> list : out.values()) {
-      Collections.sort(list, new Comparator<Comment>() {
+    for (List<CommentInfo> list : out.values()) {
+      Collections.sort(list, new Comparator<CommentInfo>() {
         @Override
-        public int compare(Comment a, Comment b) {
+        public int compare(CommentInfo a, CommentInfo b) {
           int c = firstNonNull(a.side, Side.REVISION).ordinal()
                 - firstNonNull(b.side, Side.REVISION).ordinal();
           if (c == 0) {
@@ -75,6 +90,9 @@
         }
       });
     }
+    if (accountLoader != null) {
+      accountLoader.fill();
+    }
     return out;
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
index 7e175a7..690faf3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.server.change;
 
 import static com.google.gerrit.server.change.ChangeResource.CHANGE_KIND;
+import static com.google.gerrit.server.change.CommentResource.COMMENT_KIND;
 import static com.google.gerrit.server.change.DraftResource.DRAFT_KIND;
 import static com.google.gerrit.server.change.PatchResource.PATCH_KIND;
 import static com.google.gerrit.server.change.ReviewerResource.REVIEWER_KIND;
@@ -33,9 +34,11 @@
     bind(Revisions.class);
     bind(Reviewers.class);
     bind(Drafts.class);
+    bind(Comments.class);
     bind(Patches.class);
 
     DynamicMap.mapOf(binder(), CHANGE_KIND);
+    DynamicMap.mapOf(binder(), COMMENT_KIND);
     DynamicMap.mapOf(binder(), DRAFT_KIND);
     DynamicMap.mapOf(binder(), PATCH_KIND);
     DynamicMap.mapOf(binder(), REVIEWER_KIND);
@@ -70,6 +73,9 @@
     put(DRAFT_KIND).to(PutDraft.class);
     delete(DRAFT_KIND).to(DeleteDraft.class);
 
+    child(REVISION_KIND, "comments").to(Comments.class);
+    get(COMMENT_KIND).to(GetComment.class);
+
     child(REVISION_KIND, "files").to(Patches.class);
     put(PATCH_KIND, "reviewed").to(PutReviewed.class);
     delete(PATCH_KIND, "reviewed").to(DeleteReviewed.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
index 66b939b..044e266 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PostReview.java
@@ -92,7 +92,7 @@
 
   static class Comment {
     String id;
-    GetDraft.Side side;
+    CommentInfo.Side side;
     int line;
     String inReplyTo;
     String message;
@@ -288,7 +288,7 @@
         }
         e.setStatus(PatchLineComment.Status.PUBLISHED);
         e.setWrittenOn(timestamp);
-        e.setSide(c.side == GetDraft.Side.PARENT ? (short) 0 : (short) 1);
+        e.setSide(c.side == CommentInfo.Side.PARENT ? (short) 0 : (short) 1);
         e.setMessage(c.message);
         (create ? ins : upd).add(e);
       }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraft.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraft.java
index d5eaa9b..befb8d7 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraft.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/PutDraft.java
@@ -23,7 +23,7 @@
 import com.google.gerrit.reviewdb.client.Patch;
 import com.google.gerrit.reviewdb.client.PatchLineComment;
 import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.change.GetDraft.Side;
+import com.google.gerrit.server.change.CommentInfo.Side;
 import com.google.gerrit.server.change.PutDraft.Input;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -85,12 +85,12 @@
     } else {
       db.get().patchComments().update(Collections.singleton(update(c, in)));
     }
-    return new GetDraft.Comment(c);
+    return new CommentInfo(c, null);
   }
 
   private PatchLineComment update(PatchLineComment e, Input in) {
     if (in.side != null) {
-      e.setSide(in.side == GetDraft.Side.PARENT ? (short) 0 : (short) 1);
+      e.setSide(in.side == CommentInfo.Side.PARENT ? (short) 0 : (short) 1);
     }
     if (in.line != null) {
       e.setLine(in.line);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
index 57e2706..02caa19 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
@@ -27,7 +27,6 @@
 import com.google.common.collect.Sets;
 import com.google.gerrit.common.ChangeHooks;
 import com.google.gerrit.common.data.Capable;
-import com.google.gerrit.common.data.LabelTypes;
 import com.google.gerrit.common.data.SubmitTypeRecord;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Branch;
@@ -160,8 +159,7 @@
       final ProjectCache pc, final LabelNormalizer fs,
       final GitReferenceUpdated gru, final MergedSender.Factory msf,
       final MergeFailSender.Factory mfsf,
-      final LabelTypes labelTypes, final PatchSetInfoFactory psif,
-      final IdentifiedUser.GenericFactory iuf,
+      final PatchSetInfoFactory psif, final IdentifiedUser.GenericFactory iuf,
       final ChangeControl.GenericFactory changeControlFactory,
       final MergeQueue mergeQueue, @Assisted final Branch.NameKey branch,
       final ChangeHooks hooks, final AccountCache accountCache,
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java
new file mode 100644
index 0000000..21d1ce4
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/change/CommentsTest.java
@@ -0,0 +1,224 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.change;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+import com.google.gerrit.extensions.registration.DynamicMap;
+import com.google.gerrit.extensions.restapi.IdString;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.extensions.restapi.RestView;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Patch;
+import com.google.gerrit.reviewdb.client.PatchLineComment;
+import com.google.gerrit.reviewdb.client.PatchLineComment.Status;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gerrit.reviewdb.server.PatchLineCommentAccess;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.account.AccountInfo;
+import com.google.gerrit.server.change.CommentInfo.Side;
+import com.google.gwtorm.server.ListResultSet;
+import com.google.gwtorm.server.ResultSet;
+import com.google.inject.AbstractModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+import com.google.inject.TypeLiteral;
+
+import junit.framework.TestCase;
+
+import org.easymock.IAnswer;
+
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+public class CommentsTest extends TestCase {
+
+  private Injector injector;
+  private RevisionResource revRes1;
+  private RevisionResource revRes2;
+  private PatchLineComment plc1;
+  private PatchLineComment plc2;
+  private PatchLineComment plc3;
+
+  @Override
+  protected void setUp() throws Exception {
+    @SuppressWarnings("unchecked")
+    final DynamicMap<RestView<CommentResource>> views =
+        createMock(DynamicMap.class);
+    final TypeLiteral<DynamicMap<RestView<CommentResource>>> viewsType =
+        new TypeLiteral<DynamicMap<RestView<CommentResource>>>() {};
+    final AccountInfo.Loader.Factory alf =
+        createMock(AccountInfo.Loader.Factory.class);
+    final ReviewDb db = createMock(ReviewDb.class);
+
+    AbstractModule mod = new AbstractModule() {
+      @Override
+      protected void configure() {
+        bind(viewsType).toInstance(views);
+        bind(AccountInfo.Loader.Factory.class).toInstance(alf);
+        bind(ReviewDb.class).toInstance(db);
+      }};
+
+    Account.Id account1 = new Account.Id(1);
+    Account.Id account2 = new Account.Id(2);
+    AccountInfo.Loader accountLoader = createMock(AccountInfo.Loader.class);
+    accountLoader.fill();
+    expectLastCall().anyTimes();
+    expect(accountLoader.get(account1))
+        .andReturn(new AccountInfo(account1)).anyTimes();
+    expect(accountLoader.get(account2))
+        .andReturn(new AccountInfo(account2)).anyTimes();
+    expect(alf.create(true)).andReturn(accountLoader).anyTimes();
+    replay(accountLoader, alf);
+
+    revRes1 = createMock(RevisionResource.class);
+    revRes2 = createMock(RevisionResource.class);
+
+    PatchLineCommentAccess plca = createMock(PatchLineCommentAccess.class);
+    expect(db.patchComments()).andReturn(plca).anyTimes();
+
+    Change.Id changeId = new Change.Id(123);
+    PatchSet.Id psId1 = new PatchSet.Id(changeId, 1);
+    PatchSet ps1 = new PatchSet(psId1);
+    expect(revRes1.getPatchSet()).andReturn(ps1).anyTimes();
+    PatchSet.Id psId2 = new PatchSet.Id(changeId, 2);
+    PatchSet ps2 = new PatchSet(psId2);
+    expect(revRes2.getPatchSet()).andReturn(ps2);
+
+    long timeBase = System.currentTimeMillis();
+    plc1 = newPatchLineComment(psId1, "Comment1", null,
+        "FileOne.txt", Side.REVISION, 1, account1, timeBase,
+        "First Comment");
+    plc2 = newPatchLineComment(psId1, "Comment2", "Comment1",
+        "FileOne.txt", Side.REVISION, 1, account2, timeBase + 1000,
+        "Reply to First Comment");
+    plc3 = newPatchLineComment(psId1, "Comment3", "Comment1",
+        "FileOne.txt", Side.PARENT, 1, account1, timeBase + 2000,
+        "First Parent Comment");
+
+    expect(plca.publishedByPatchSet(psId1))
+        .andAnswer(results(plc1, plc2, plc3)).anyTimes();
+    expect(plca.publishedByPatchSet(psId2))
+        .andAnswer(results()).anyTimes();
+
+    replay(db, revRes1, revRes2, plca);
+    injector = Guice.createInjector(mod);
+  }
+
+  public void testListComments() throws Exception {
+    // test ListComments for patch set 1
+    assertListComments(injector, revRes1, ImmutableMap.of(
+        "FileOne.txt", Lists.newArrayList(plc3, plc1, plc2)));
+
+    // test ListComments for patch set 2
+    assertListComments(injector, revRes2,
+        Collections.<String, ArrayList<PatchLineComment>>emptyMap());
+  }
+
+  public void testGetComment() throws Exception {
+    // test GetComment for existing comment
+    assertGetComment(injector, revRes1, plc1, plc1.getKey().get());
+
+    // test GetComment for non-existent comment
+    assertGetComment(injector, revRes1, null, "BadComment");
+  }
+
+  private static IAnswer<ResultSet<PatchLineComment>> results(
+      final PatchLineComment... comments) {
+    return new IAnswer<ResultSet<PatchLineComment>>() {
+      @Override
+      public ResultSet<PatchLineComment> answer() throws Throwable {
+        return new ListResultSet<PatchLineComment>(Lists.newArrayList(comments));
+      }};
+  }
+
+  private static void assertGetComment(Injector inj, RevisionResource res,
+      PatchLineComment expected, String uuid) throws Exception {
+    GetComment getComment = inj.getInstance(GetComment.class);
+    Comments comments = inj.getInstance(Comments.class);
+    try {
+      CommentResource commentRes = comments.parse(res, IdString.fromUrl(uuid));
+      if (expected == null) {
+        fail("Expected no comment");
+      }
+      CommentInfo actual = (CommentInfo) getComment.apply(commentRes);
+      assertComment(expected, actual);
+    } catch (ResourceNotFoundException e) {
+      if (expected != null) {
+        fail("Expected to find comment");
+      }
+    }
+  }
+
+  private static void assertListComments(Injector inj, RevisionResource res,
+      Map<String, ArrayList<PatchLineComment>> expected) throws Exception {
+    Comments comments = inj.getInstance(Comments.class);
+    RestReadView<RevisionResource> listView =
+        (RestReadView<RevisionResource>) comments.list();
+    @SuppressWarnings("unchecked")
+    Map<String, List<CommentInfo>> actual =
+        (Map<String, List<CommentInfo>>) listView.apply(res);
+    assertNotNull(actual);
+    assertEquals(expected.size(), actual.size());
+    assertEquals(expected.keySet(), actual.keySet());
+    for (String filename : expected.keySet()) {
+      List<PatchLineComment> expectedComments = expected.get(filename);
+      List<CommentInfo> actualComments = actual.get(filename);
+      assertNotNull(actualComments);
+      assertEquals(expectedComments.size(), actualComments.size());
+      for (int i = 0; i < expectedComments.size(); i++) {
+        assertComment(expectedComments.get(i), actualComments.get(i));
+      }
+    }
+  }
+
+  private static void assertComment(PatchLineComment plc, CommentInfo ci) {
+    assertEquals(plc.getKey().get(), ci.id);
+    assertEquals(plc.getParentUuid(), ci.inReplyTo);
+    assertEquals("gerritcodereview#comment", ci.kind);
+    assertEquals(plc.getMessage(), ci.message);
+    assertNotNull(ci.author);
+    assertEquals(plc.getAuthor(), ci.author._id);
+    assertEquals(plc.getLine(), (int) ci.line);
+    assertEquals(plc.getSide() == 0 ? Side.PARENT : Side.REVISION,
+        Objects.firstNonNull(ci.side, Side.REVISION));
+    assertEquals(plc.getWrittenOn(), ci.updated);
+  }
+
+  private static PatchLineComment newPatchLineComment(PatchSet.Id psId,
+      String uuid, String inReplyToUuid, String filename, Side side, int line,
+      Account.Id authorId, long millis, String message) {
+    Patch.Key p = new Patch.Key(psId, filename);
+    PatchLineComment.Key id = new PatchLineComment.Key(p, uuid);
+    PatchLineComment plc =
+        new PatchLineComment(id, line, authorId, inReplyToUuid);
+    plc.setMessage(message);
+    plc.setSide(side == CommentInfo.Side.PARENT ? (short) 0 : (short) 1);
+    plc.setStatus(Status.PUBLISHED);
+    plc.setWrittenOn(new Timestamp(millis));
+    return plc;
+  }
+}
diff --git a/gerrit-sshd/pom.xml b/gerrit-sshd/pom.xml
index e8fcc11..75a43c0 100644
--- a/gerrit-sshd/pom.xml
+++ b/gerrit-sshd/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-sshd</artifactId>
diff --git a/gerrit-util-cli/pom.xml b/gerrit-util-cli/pom.xml
index 04e3b09..f2325a4 100644
--- a/gerrit-util-cli/pom.xml
+++ b/gerrit-util-cli/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-util-cli</artifactId>
diff --git a/gerrit-util-ssl/pom.xml b/gerrit-util-ssl/pom.xml
index 69ae3b5..1e941e0 100644
--- a/gerrit-util-ssl/pom.xml
+++ b/gerrit-util-ssl/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-util-ssl</artifactId>
diff --git a/gerrit-war/pom.xml b/gerrit-war/pom.xml
index 9802b68..dc1a3f7 100644
--- a/gerrit-war/pom.xml
+++ b/gerrit-war/pom.xml
@@ -22,7 +22,7 @@
   <parent>
     <groupId>com.google.gerrit</groupId>
     <artifactId>gerrit-parent</artifactId>
-    <version>2.6-SNAPSHOT</version>
+    <version>2.7-SNAPSHOT</version>
   </parent>
 
   <artifactId>gerrit-war</artifactId>
diff --git a/pom.xml b/pom.xml
index bb05004..6e4c0f6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,7 +22,7 @@
   <groupId>com.google.gerrit</groupId>
   <artifactId>gerrit-parent</artifactId>
   <packaging>pom</packaging>
-  <version>2.6-SNAPSHOT</version>
+  <version>2.7-SNAPSHOT</version>
 
   <name>Gerrit Code Review - Parent</name>
   <url>http://code.google.com/p/gerrit/</url>