Merge "Fix inclusion of LF in IntralineLoader" into stable-2.8
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 814472a..6d00dfa 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -2507,6 +2507,14 @@
 [[sshd]] Section sshd
 ~~~~~~~~~~~~~~~~~~~~~
 
+[[sshd.backend]]sshd.backend::
++
+Starting from version 0.9.0 Apache SSHD project added support for NIO2
+IoSession. To use the new NIO2 session the `backend` option must be set
+to `NIO2`.
++
+By default, `MINA`.
+
 [[sshd.listenAddress]]sshd.listenAddress::
 +
 Specifies the local addresses the internal SSHD should listen
@@ -2545,20 +2553,13 @@
 +
 By default, sshd.listenAddress.
 
-[[sshd.reuseAddress]]sshd.reuseAddress::
-+
-If true, permits the daemon to bind to the port even if the port
-is already in use.  If false, the daemon ensures the port is not
-in use before starting.  Busy sites may need to set this to true
-to permit fast restarts.
-+
-By default, true.
-
 [[sshd.tcpKeepAlive]]sshd.tcpKeepAlive::
 +
 If true, enables TCP keepalive messages to the other side, so
 the daemon can terminate connections if the peer disappears.
 +
+Only effective when `sshd.backend` is set to `MINA`.
++
 By default, true.
 
 [[sshd.threads]]sshd.threads::
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index 02bb549..70431c6 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -921,6 +921,87 @@
   }
 ----
 
+[[get-starred-changes]]
+Get Starred Changes
+~~~~~~~~~~~~~~~~~~~
+[verse]
+'GET /accounts/link:#account-id[\{account-id\}]/starred.changes'
+
+Gets the changes starred by the identified user account. This
+URL endpoint is functionally identical to the changes query
+`GET /changes/?q=is:starred`. The result is a list of
+link:rest-api-changes.html#change-info[ChangeInfo] entities.
+
+.Request
+----
+  GET /a/accounts/self/starred.changes
+----
+
+.Response
+----
+  HTTP/1.1 200 OK
+  Content-Disposition: attachment
+  Content-Type: application/json;charset=UTF-8
+
+  )]}'
+  [
+    {
+      "kind": "gerritcodereview#change",
+      "id": "myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940",
+      "project": "myProject",
+      "branch": "master",
+      "change_id": "I8473b95934b5732ac55d26311a706c9c2bde9940",
+      "subject": "Implementing Feature X",
+      "status": "NEW",
+      "created": "2013-02-01 09:59:32.126000000",
+      "updated": "2013-02-21 11:16:36.775000000",
+      "mergeable": true,
+      "_sortkey": "0023412400000f7d",
+      "_number": 3965,
+      "owner": {
+        "name": "John Doe"
+      }
+    }
+  ]
+----
+
+[[star-change]]
+Star Change
+~~~~~~~~~~~
+[verse]
+'PUT /accounts/link:#account-id[\{account-id\}]/starred.changes/link:rest-api-changes.html#change-id[\{change-id\}]'
+
+Star a change. Starred changes are returned for the search query
+`is:starred` or `starredby:USER` and automatically notify the user
+whenever updates are made to the change.
+
+.Request
+----
+  PUT /a/accounts/self/starred.changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940 HTTP/1.0
+----
+
+.Response
+----
+  HTTP/1.1 204 No Content
+----
+
+[[unstar-change]]
+Unstar Change
+~~~~~~~~~~~~~
+[verse]
+'DELETE /accounts/link:#account-id[\{account-id\}]/starred.changes/link:rest-api-changes#change-id[\{change-id\}]'
+
+Unstar a change. Removes the starred flag, stopping notifications.
+
+.Request
+----
+  DELETE /a/accounts/self/starred.changes/myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9940 HTTP/1.0
+----
+
+.Response
+----
+  HTTP/1.1 204 No Content
+----
 
 [[ids]]
 IDs
diff --git a/ReleaseNotes/ReleaseNotes-2.8.txt b/ReleaseNotes/ReleaseNotes-2.8.txt
index 27ed297..d183cca 100644
--- a/ReleaseNotes/ReleaseNotes-2.8.txt
+++ b/ReleaseNotes/ReleaseNotes-2.8.txt
@@ -278,6 +278,15 @@
 * link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.8/rest-api-accounts.html#get-username[
 Get account username]
 
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.8/rest-api-accounts.html#get-starred-changes[
+Get starred changes]
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.8/rest-api-accounts.html#star-change[
+Star change]
+
+* link:https://gerrit-documentation.storage.googleapis.com/Documentation/2.8/rest-api-accounts.html#unstar-change[
+Unstar change]
+
 Changes
 ^^^^^^^
 
@@ -741,5 +750,7 @@
 * Update guava to 15.0
 * Update H2 to 1.3.173
 * Update bouncycastle to 1.44
+* Update Apache Mina to 2.0.7
+* Update Apache SSHD to 0.9.0.201311081
 * asciidoctor 0.1.4 is now required to build the documentation
 * jsr305 library was removed
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/BUCK b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/BUCK
index d813501..1fca451 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/BUCK
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/BUCK
@@ -2,7 +2,11 @@
 
 acceptance_tests(
   srcs = glob(['*IT.java']),
-  deps = [':util'],
+  deps = [
+    ':util',
+    '//gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git:util',
+    '//gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change:util',
+  ],
 )
 
 java_library(
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/StarredChangesIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/StarredChangesIT.java
new file mode 100644
index 0000000..b5ae7de
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/account/StarredChangesIT.java
@@ -0,0 +1,122 @@
+// 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.acceptance.rest.account;
+
+import static com.google.gerrit.acceptance.git.GitUtil.cloneProject;
+import static com.google.gerrit.acceptance.git.GitUtil.createProject;
+import static com.google.gerrit.acceptance.git.GitUtil.initSsh;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.AccountCreator;
+import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.acceptance.RestSession;
+import com.google.gerrit.acceptance.SshSession;
+import com.google.gerrit.acceptance.TestAccount;
+import com.google.gerrit.acceptance.git.PushOneCommit;
+import com.google.gerrit.acceptance.git.PushOneCommit.Result;
+import com.google.gerrit.acceptance.rest.change.ChangeInfo;
+import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.Project;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import com.google.gwtorm.server.OrmException;
+import com.google.gwtorm.server.SchemaFactory;
+import com.google.inject.Inject;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.List;
+
+public class StarredChangesIT extends AbstractDaemonTest {
+
+  @Inject
+  private AccountCreator accounts;
+
+  @Inject
+  private SchemaFactory<ReviewDb> reviewDbProvider;
+
+  private TestAccount admin;
+
+  private RestSession session;
+  private Git git;
+  private ReviewDb db;
+
+  @Before
+  public void setUp() throws Exception {
+    admin = accounts.admin();
+    session = new RestSession(server, admin);
+    initSsh(admin);
+    Project.NameKey project = new Project.NameKey("p");
+    SshSession sshSession = new SshSession(server, admin);
+    createProject(sshSession, project.get());
+    git = cloneProject(sshSession.getUrl() + "/" + project.get());
+    sshSession.close();
+    db = reviewDbProvider.open();
+  }
+
+  @After
+  public void cleanup() {
+    db.close();
+  }
+
+  @Test
+  public void starredChangeState() throws GitAPIException, IOException,
+      OrmException {
+    Result c1 = createChange();
+    Result c2 = createChange();
+    assertNull(getChange(c1.getChangeId()).starred);
+    assertNull(getChange(c2.getChangeId()).starred);
+    starChange(true, c1.getPatchSetId().getParentKey());
+    starChange(true, c2.getPatchSetId().getParentKey());
+    assertTrue(getChange(c1.getChangeId()).starred);
+    assertTrue(getChange(c2.getChangeId()).starred);
+    starChange(false, c1.getPatchSetId().getParentKey());
+    starChange(false, c2.getPatchSetId().getParentKey());
+    assertNull(getChange(c1.getChangeId()).starred);
+    assertNull(getChange(c2.getChangeId()).starred);
+  }
+
+  private ChangeInfo getChange(String changeId) throws IOException {
+    RestResponse r = session.get("/changes/?q=" + changeId);
+    List<ChangeInfo> c = (new Gson()).fromJson(r.getReader(),
+        new TypeToken<List<ChangeInfo>>() {}.getType());
+    return c.get(0);
+  }
+
+  private void starChange(boolean on, Change.Id id) throws IOException {
+    String url = "/accounts/self/starred.changes/" + id.get();
+    if (on) {
+      RestResponse r = session.put(url);
+      assertEquals(204, r.getStatusCode());
+    } else {
+      RestResponse r = session.delete(url);
+      assertEquals(204, r.getStatusCode());
+    }
+  }
+
+  private Result createChange() throws GitAPIException, IOException {
+    PushOneCommit push = new PushOneCommit(db, admin.getIdent());
+    return push.to(git, "refs/for/master");
+  }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/BUCK b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/BUCK
index b9c2d08..20b1033 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/BUCK
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/BUCK
@@ -38,4 +38,5 @@
     '//lib:guava',
     '//gerrit-reviewdb:server',
   ],
+  visibility = ['//gerrit-acceptance-tests/...'],
 )
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ChangeInfo.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ChangeInfo.java
index fe8737e..8b431f0 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ChangeInfo.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ChangeInfo.java
@@ -24,4 +24,5 @@
   String branch;
   List<ChangeMessageInfo> messages;
   Change.Status status;
+  public Boolean starred;
 }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeListService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeListService.java
deleted file mode 100644
index 0c466497..0000000
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeListService.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (C) 2008 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.common.data;
-
-import com.google.gerrit.common.audit.Audit;
-import com.google.gerrit.common.auth.SignInRequired;
-import com.google.gwtjsonrpc.common.AsyncCallback;
-import com.google.gwtjsonrpc.common.RemoteJsonService;
-import com.google.gwtjsonrpc.common.RpcImpl;
-import com.google.gwtjsonrpc.common.VoidResult;
-import com.google.gwtjsonrpc.common.RpcImpl.Version;
-
-@RpcImpl(version = Version.V2_0)
-public interface ChangeListService extends RemoteJsonService {
-  /**
-   * Add and/or remove changes from the set of starred changes of the caller.
-   *
-   * @param req the add and remove cluster.
-   */
-  @Audit
-  @SignInRequired
-  void toggleStars(ToggleStarRequest req, AsyncCallback<VoidResult> callback);
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/StarredChanges.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/StarredChanges.java
index 7d2084a..b097bd8 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/StarredChanges.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/StarredChanges.java
@@ -15,19 +15,23 @@
 package com.google.gerrit.client.changes;
 
 import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.rpc.GerritCallback;
-import com.google.gerrit.common.data.ToggleStarRequest;
+import com.google.gerrit.client.account.AccountApi;
+import com.google.gerrit.client.rpc.RestApi;
 import com.google.gerrit.reviewdb.client.Change;
+import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
 import com.google.gwt.event.dom.client.KeyPressEvent;
 import com.google.gwt.resources.client.ImageResource;
+import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.Image;
 import com.google.gwtexpui.globalkey.client.KeyCommand;
-import com.google.gwtjsonrpc.common.VoidResult;
 import com.google.web.bindery.event.shared.Event;
 import com.google.web.bindery.event.shared.HandlerRegistration;
 
+import java.util.LinkedHashMap;
+import java.util.Map;
+
 /** Supports the star icon displayed on changes and tracking the status. */
 public class StarredChanges {
   private static final Event.Type<ChangeStarHandler> TYPE =
@@ -105,57 +109,52 @@
   public static void toggleStar(
       final Change.Id changeId,
       final boolean newValue) {
-    if (next == null) {
-      next = new ToggleStarRequest();
-    }
-    next.toggle(changeId, newValue);
+    pending.put(changeId, newValue);
     fireChangeStarEvent(changeId, newValue);
     if (!busy) {
-      start();
+      startRequest();
     }
   }
 
-  private static ToggleStarRequest next;
   private static boolean busy;
+  private static final Map<Change.Id, Boolean> pending =
+      new LinkedHashMap<Change.Id, Boolean>(4);
 
-  private static void start() {
-    final ToggleStarRequest req = next;
-    next = null;
+  private static void startRequest() {
     busy = true;
 
-    Util.LIST_SVC.toggleStars(req, new GerritCallback<VoidResult>() {
+    final Change.Id id = pending.keySet().iterator().next();
+    final boolean starred = pending.remove(id);
+    RestApi call = AccountApi.self().view("starred.changes").id(id.get());
+    AsyncCallback<JavaScriptObject> cb = new AsyncCallback<JavaScriptObject>() {
       @Override
-      public void onSuccess(VoidResult result) {
-        if (next != null) {
-          start();
-        } else {
+      public void onSuccess(JavaScriptObject none) {
+        if (pending.isEmpty()) {
           busy = false;
+        } else {
+          startRequest();
         }
       }
 
       @Override
       public void onFailure(Throwable caught) {
-        rollback(req);
-        if (next != null) {
-          rollback(next);
-          next = null;
+        if (!starred && RestApi.isStatus(caught, 404)) {
+          onSuccess(null);
+          return;
         }
-        busy = false;
-        super.onFailure(caught);
-      }
-    });
-  }
 
-  private static void rollback(ToggleStarRequest req) {
-    if (req.getAddSet() != null) {
-      for (Change.Id id : req.getAddSet()) {
-        fireChangeStarEvent(id, false);
+        fireChangeStarEvent(id, !starred);
+        for (Map.Entry<Change.Id, Boolean> e : pending.entrySet()) {
+          fireChangeStarEvent(e.getKey(), !e.getValue());
+        }
+        pending.clear();
+        busy = false;
       }
-    }
-    if (req.getRemoveSet() != null) {
-      for (Change.Id id : req.getRemoveSet()) {
-        fireChangeStarEvent(id, true);
-      }
+    };
+    if (starred) {
+      call.put(cb);
+    } else {
+      call.delete(cb);
     }
   }
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/Util.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/Util.java
index 590ad87..76dfd58 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/Util.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/Util.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.client.changes;
 
 import com.google.gerrit.common.data.ChangeDetailService;
-import com.google.gerrit.common.data.ChangeListService;
 import com.google.gerrit.common.data.ChangeManageService;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gwt.core.client.GWT;
@@ -27,7 +26,6 @@
   public static final ChangeResources R = GWT.create(ChangeResources.class);
 
   public static final ChangeDetailService DETAIL_SVC;
-  public static final ChangeListService LIST_SVC;
   public static final ChangeManageService MANAGE_SVC;
 
   private static final int SUBJECT_MAX_LENGTH = 80;
@@ -38,9 +36,6 @@
     DETAIL_SVC = GWT.create(ChangeDetailService.class);
     JsonUtil.bind(DETAIL_SVC, "rpc/ChangeDetailService");
 
-    LIST_SVC = GWT.create(ChangeListService.class);
-    JsonUtil.bind(LIST_SVC, "rpc/ChangeListService");
-
     MANAGE_SVC = GWT.create(ChangeManageService.class);
     JsonUtil.bind(MANAGE_SVC, "rpc/ChangeManageService");
   }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/ChangeListServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/ChangeListServiceImpl.java
deleted file mode 100644
index 0b54db1..0000000
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/ChangeListServiceImpl.java
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright (C) 2008 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.httpd.rpc;
-
-import com.google.gerrit.common.data.ChangeListService;
-import com.google.gerrit.common.data.ToggleStarRequest;
-import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.StarredChange;
-import com.google.gerrit.reviewdb.server.ReviewDb;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gwtjsonrpc.common.AsyncCallback;
-import com.google.gwtjsonrpc.common.VoidResult;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-
-public class ChangeListServiceImpl extends BaseServiceImplementation implements
-    ChangeListService {
-  private final Provider<CurrentUser> currentUser;
-
-  @Inject
-  ChangeListServiceImpl(final Provider<ReviewDb> schema,
-      final Provider<CurrentUser> currentUser) {
-    super(schema, currentUser);
-    this.currentUser = currentUser;
-  }
-
-  public void toggleStars(final ToggleStarRequest req,
-      final AsyncCallback<VoidResult> callback) {
-    run(callback, new Action<VoidResult>() {
-      public VoidResult run(final ReviewDb db) throws OrmException {
-        final Account.Id me = getAccountId();
-        final Set<Change.Id> existing = currentUser.get().getStarredChanges();
-        List<StarredChange> add = new ArrayList<StarredChange>();
-        List<StarredChange.Key> remove = new ArrayList<StarredChange.Key>();
-
-        if (req.getAddSet() != null) {
-          for (final Change.Id id : req.getAddSet()) {
-            if (!existing.contains(id)) {
-              add.add(new StarredChange(new StarredChange.Key(me, id)));
-            }
-          }
-        }
-
-        if (req.getRemoveSet() != null) {
-          for (final Change.Id id : req.getRemoveSet()) {
-            remove.add(new StarredChange.Key(me, id));
-          }
-        }
-
-        db.starredChanges().insert(add);
-        db.starredChanges().deleteKeys(remove);
-        return VoidResult.INSTANCE;
-      }
-    });
-  }
-}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/UiRpcModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/UiRpcModule.java
index 7de332a..08e1582 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/UiRpcModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/UiRpcModule.java
@@ -27,7 +27,6 @@
 
   @Override
   protected void configureServlets() {
-    rpc(ChangeListServiceImpl.class);
     rpc(SuggestServiceImpl.class);
     rpc(SystemInfoServiceImpl.class);
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountResource.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountResource.java
index 629bd15..106c033 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountResource.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountResource.java
@@ -17,7 +17,9 @@
 import com.google.gerrit.extensions.restapi.RestResource;
 import com.google.gerrit.extensions.restapi.RestView;
 import com.google.gerrit.reviewdb.client.AccountSshKey;
+import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.change.ChangeResource;
 import com.google.inject.TypeLiteral;
 
 public class AccountResource implements RestResource {
@@ -33,6 +35,9 @@
   public static final TypeLiteral<RestView<SshKey>> SSH_KEY_KIND =
       new TypeLiteral<RestView<SshKey>>() {};
 
+  public static final TypeLiteral<RestView<StarredChange>> STARRED_CHANGE_KIND =
+      new TypeLiteral<RestView<StarredChange>>() {};
+
   private final IdentifiedUser user;
 
   public AccountResource(IdentifiedUser user) {
@@ -90,4 +95,17 @@
       return sshKey;
     }
   }
+
+  public static class StarredChange extends AccountResource {
+    private final ChangeResource change;
+
+    public StarredChange(IdentifiedUser user, ChangeResource change) {
+      super(user);
+      this.change = change;
+    }
+
+    public Change getChange() {
+      return change.getChange();
+    }
+  }
 }
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 79a0089..11f2e91 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
@@ -18,6 +18,7 @@
 import static com.google.gerrit.server.account.AccountResource.CAPABILITY_KIND;
 import static com.google.gerrit.server.account.AccountResource.EMAIL_KIND;
 import static com.google.gerrit.server.account.AccountResource.SSH_KEY_KIND;
+import static com.google.gerrit.server.account.AccountResource.STARRED_CHANGE_KIND;
 
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.restapi.RestApiModule;
@@ -30,9 +31,10 @@
     bind(Capabilities.class);
 
     DynamicMap.mapOf(binder(), ACCOUNT_KIND);
+    DynamicMap.mapOf(binder(), CAPABILITY_KIND);
     DynamicMap.mapOf(binder(), EMAIL_KIND);
     DynamicMap.mapOf(binder(), SSH_KEY_KIND);
-    DynamicMap.mapOf(binder(), CAPABILITY_KIND);
+    DynamicMap.mapOf(binder(), STARRED_CHANGE_KIND);
 
     put(ACCOUNT_KIND).to(PutAccount.class);
     get(ACCOUNT_KIND).to(GetAccount.class);
@@ -65,6 +67,11 @@
     put(ACCOUNT_KIND, "preferences.diff").to(SetDiffPreferences.class);
     get(CAPABILITY_KIND).to(GetCapabilities.CheckOne.class);
 
+    child(ACCOUNT_KIND, "starred.changes").to(StarredChanges.class);
+    put(STARRED_CHANGE_KIND).to(StarredChanges.Put.class);
+    delete(STARRED_CHANGE_KIND).to(StarredChanges.Delete.class);
+    bind(StarredChanges.Create.class);
+
     install(new FactoryModuleBuilder().build(CreateAccount.Factory.class));
     install(new FactoryModuleBuilder().build(CreateEmail.Factory.class));
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/StarredChanges.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/StarredChanges.java
new file mode 100644
index 0000000..0e335d0
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/StarredChanges.java
@@ -0,0 +1,199 @@
+// 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.registration.DynamicMap;
+import com.google.gerrit.extensions.restapi.AcceptsCreate;
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.BadRequestException;
+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.Response;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.extensions.restapi.RestReadView;
+import com.google.gerrit.extensions.restapi.RestView;
+import com.google.gerrit.extensions.restapi.TopLevelResource;
+import com.google.gerrit.extensions.restapi.UnprocessableEntityException;
+import com.google.gerrit.reviewdb.client.StarredChange;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.IdentifiedUser;
+import com.google.gerrit.server.change.ChangeResource;
+import com.google.gerrit.server.change.ChangesCollection;
+import com.google.gerrit.server.query.change.QueryChanges;
+import com.google.gwtorm.server.OrmDuplicateKeyException;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Collections;
+
+class StarredChanges implements
+    ChildCollection<AccountResource, AccountResource.StarredChange>,
+    AcceptsCreate<AccountResource> {
+  private static final Logger log = LoggerFactory.getLogger(StarredChanges.class);
+
+  private final ChangesCollection changes;
+  private final DynamicMap<RestView<AccountResource.StarredChange>> views;
+  private final Provider<Create> createProvider;
+
+  @Inject
+  StarredChanges(ChangesCollection changes,
+      DynamicMap<RestView<AccountResource.StarredChange>> views,
+      Provider<Create> createProvider) {
+    this.changes = changes;
+    this.views = views;
+    this.createProvider = createProvider;
+  }
+
+  @Override
+  public AccountResource.StarredChange parse(AccountResource parent, IdString id)
+      throws ResourceNotFoundException, OrmException, UnsupportedEncodingException {
+    IdentifiedUser user = parent.getUser();
+    try {
+      user.asyncStarredChanges();
+
+      ChangeResource change = changes.parse(TopLevelResource.INSTANCE, id);
+      if (user.getStarredChanges().contains(change.getChange().getId())) {
+        return new AccountResource.StarredChange(user, change);
+      }
+      throw new ResourceNotFoundException(id);
+    } finally {
+      user.abortStarredChanges();
+    }
+  }
+
+  @Override
+  public DynamicMap<RestView<AccountResource.StarredChange>> views() {
+    return views;
+  }
+
+  @Override
+  public RestView<AccountResource> list() throws ResourceNotFoundException {
+    return new RestReadView<AccountResource>() {
+      @Override
+      public Object apply(AccountResource self) throws BadRequestException,
+          AuthException, OrmException {
+        QueryChanges query = changes.list();
+        query.addQuery("starredby:" + self.getUser().getAccountId().get());
+        return query.apply(TopLevelResource.INSTANCE);
+      }
+    };
+  }
+
+  @SuppressWarnings("unchecked")
+  @Override
+  public RestModifyView<AccountResource, EmptyInput> create(
+      AccountResource parent, IdString id) throws UnprocessableEntityException{
+    try {
+      return createProvider.get()
+          .setChange(changes.parse(TopLevelResource.INSTANCE, id));
+    } catch (ResourceNotFoundException e) {
+      throw new UnprocessableEntityException(String.format("change %s not found", id.get()));
+    } catch (UnsupportedEncodingException e) {
+      log.error("cannot resolve change", e);
+      throw new UnprocessableEntityException("internal server error");
+    } catch (OrmException e) {
+      log.error("cannot resolve change", e);
+      throw new UnprocessableEntityException("internal server error");
+    }
+  }
+
+  static class Create implements RestModifyView<AccountResource, EmptyInput> {
+    private final Provider<CurrentUser> self;
+    private final Provider<ReviewDb> dbProvider;
+    private ChangeResource change;
+
+    @Inject
+    Create(Provider<CurrentUser> self, Provider<ReviewDb> dbProvider) {
+      this.self = self;
+      this.dbProvider = dbProvider;
+    }
+
+    Create setChange(ChangeResource change) {
+      this.change = change;
+      return this;
+    }
+
+    @Override
+    public Response<?> apply(AccountResource rsrc, EmptyInput in)
+        throws AuthException, OrmException {
+      if (self.get() != rsrc.getUser()) {
+        throw new AuthException("not allowed to add starred change");
+      }
+      try {
+        dbProvider.get().starredChanges().insert(Collections.singleton(
+            new StarredChange(new StarredChange.Key(
+                rsrc.getUser().getAccountId(),
+                change.getChange().getId()))));
+      } catch (OrmDuplicateKeyException e) {
+        return Response.none();
+      }
+      return Response.none();
+    }
+  }
+
+  static class Put implements
+      RestModifyView<AccountResource.StarredChange, EmptyInput> {
+    private final Provider<CurrentUser> self;
+
+    @Inject
+    Put(Provider<CurrentUser> self) {
+      this.self = self;
+    }
+
+    @Override
+    public Response<?> apply(AccountResource.StarredChange rsrc, EmptyInput in)
+        throws AuthException, OrmException {
+      if (self.get() != rsrc.getUser()) {
+        throw new AuthException("not allowed update starred changes");
+      }
+      return Response.none();
+    }
+  }
+
+  static class Delete implements
+      RestModifyView<AccountResource.StarredChange, EmptyInput> {
+    private final Provider<CurrentUser> self;
+    private final Provider<ReviewDb> dbProvider;
+
+    @Inject
+    Delete(Provider<CurrentUser> self, Provider<ReviewDb> dbProvider) {
+      this.self = self;
+      this.dbProvider = dbProvider;
+    }
+
+    @Override
+    public Response<?> apply(AccountResource.StarredChange rsrc,
+        EmptyInput in) throws AuthException, OrmException {
+      if (self.get() != rsrc.getUser()) {
+        throw new AuthException("not allowed remove starred change");
+      }
+      dbProvider.get().starredChanges().delete(Collections.singleton(
+          new StarredChange(new StarredChange.Key(
+              rsrc.getUser().getAccountId(),
+              rsrc.getChange().getId()))));
+      return Response.none();
+    }
+  }
+
+  static class EmptyInput {
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
index f64e424..5a3ab3b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java
@@ -31,6 +31,9 @@
 
 import com.google.common.base.Joiner;
 import com.google.common.base.Objects;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
 import com.google.common.collect.HashBasedTable;
 import com.google.common.collect.HashMultimap;
 import com.google.common.collect.ImmutableList;
@@ -64,6 +67,7 @@
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.reviewdb.client.PatchSetInfo;
 import com.google.gerrit.reviewdb.client.PatchSetInfo.ParentInfo;
+import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.client.UserIdentity;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.AnonymousUser;
@@ -77,7 +81,8 @@
 import com.google.gerrit.server.patch.PatchSetInfoFactory;
 import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
 import com.google.gerrit.server.project.ChangeControl;
-import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.project.NoSuchProjectException;
+import com.google.gerrit.server.project.ProjectControl;
 import com.google.gerrit.server.query.change.ChangeData;
 import com.google.gwtorm.server.OrmException;
 import com.google.gwtorm.server.ResultSet;
@@ -87,6 +92,7 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.IOException;
 import java.sql.Timestamp;
 import java.util.Collection;
 import java.util.Collections;
@@ -97,6 +103,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
+import java.util.concurrent.ExecutionException;
 
 public class ChangeJson {
   private static final Logger log = LoggerFactory.getLogger(ChangeJson.class);
@@ -122,7 +129,7 @@
   private final Provider<CurrentUser> userProvider;
   private final AnonymousUser anonymous;
   private final IdentifiedUser.GenericFactory userFactory;
-  private final ChangeControl.GenericFactory changeControlGenericFactory;
+  private final ProjectControl.GenericFactory projectControlFactory;
   private final PatchSetInfoFactory patchSetInfoFactory;
   private final FileInfoJson fileInfoJson;
   private final AccountInfo.Loader.Factory accountLoaderFactory;
@@ -131,20 +138,20 @@
   private final DynamicMap<RestView<ChangeResource>> changes;
   private final Revisions revisions;
 
-  private ChangeControl.Factory changeControlUserFactory;
   private EnumSet<ListChangesOption> options;
   private AccountInfo.Loader accountLoader;
   private ChangeControl lastControl;
   private Set<Change.Id> reviewed;
+  private LoadingCache<Project.NameKey, ProjectControl> projectControls;
 
   @Inject
   ChangeJson(
       Provider<ReviewDb> db,
       LabelNormalizer ln,
-      Provider<CurrentUser> userProvider,
+      Provider<CurrentUser> user,
       AnonymousUser au,
       IdentifiedUser.GenericFactory uf,
-      ChangeControl.GenericFactory ccf,
+      ProjectControl.GenericFactory pcf,
       PatchSetInfoFactory psi,
       FileInfoJson fileInfoJson,
       AccountInfo.Loader.Factory ailf,
@@ -154,10 +161,10 @@
       Revisions revisions) {
     this.db = db;
     this.labelNormalizer = ln;
-    this.userProvider = userProvider;
+    this.userProvider = user;
     this.anonymous = au;
     this.userFactory = uf;
-    this.changeControlGenericFactory = ccf;
+    this.projectControlFactory = pcf;
     this.patchSetInfoFactory = psi;
     this.fileInfoJson = fileInfoJson;
     this.accountLoaderFactory = ailf;
@@ -167,6 +174,15 @@
     this.revisions = revisions;
 
     options = EnumSet.noneOf(ListChangesOption.class);
+    projectControls = CacheBuilder.newBuilder()
+      .concurrencyLevel(1)
+      .build(new CacheLoader<Project.NameKey, ProjectControl>() {
+        @Override
+        public ProjectControl load(Project.NameKey key)
+            throws NoSuchProjectException, IOException {
+          return projectControlFactory.controlFor(key, userProvider.get());
+        }
+      });
   }
 
   public ChangeJson addOption(ListChangesOption o) {
@@ -179,11 +195,6 @@
     return this;
   }
 
-  public ChangeJson setChangeControlFactory(ChangeControl.Factory cf) {
-    changeControlUserFactory = cf;
-    return this;
-  }
-
   public ChangeInfo format(ChangeResource rsrc) throws OrmException {
     return format(new ChangeData(rsrc.getControl()));
   }
@@ -321,13 +332,12 @@
     }
 
     try {
-      if (changeControlUserFactory != null) {
-        ctrl = changeControlUserFactory.controlFor(cd.change(db));
-      } else {
-        ctrl = changeControlGenericFactory.controlFor(cd.change(db),
-            userProvider.get());
+      Change change = cd.change(db);
+      if (change == null) {
+        return null;
       }
-    } catch (NoSuchChangeException e) {
+      ctrl = projectControls.get(change.getProject()).controlFor(change);
+    } catch (ExecutionException e) {
       return null;
     }
     lastControl = ctrl;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java
index 45328b1..e93a0d8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangesCollection.java
@@ -23,6 +23,7 @@
 import com.google.gerrit.extensions.restapi.TopLevelResource;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.project.ChangeControl;
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.query.change.QueryChanges;
@@ -37,24 +38,27 @@
 public class ChangesCollection implements
     RestCollection<TopLevelResource, ChangeResource> {
   private final Provider<ReviewDb> db;
-  private final ChangeControl.Factory changeControlFactory;
+  private final Provider<CurrentUser> user;
+  private final ChangeControl.GenericFactory changeControlFactory;
   private final Provider<QueryChanges> queryFactory;
   private final DynamicMap<RestView<ChangeResource>> views;
 
   @Inject
   ChangesCollection(
       Provider<ReviewDb> dbProvider,
-      ChangeControl.Factory changeControlFactory,
+      Provider<CurrentUser> user,
+      ChangeControl.GenericFactory changeControlFactory,
       Provider<QueryChanges> queryFactory,
       DynamicMap<RestView<ChangeResource>> views) {
     this.db = dbProvider;
+    this.user = user;
     this.changeControlFactory = changeControlFactory;
     this.queryFactory = queryFactory;
     this.views = views;
   }
 
   @Override
-  public RestView<TopLevelResource> list() {
+  public QueryChanges list() {
     return queryFactory.get();
   }
 
@@ -74,7 +78,7 @@
 
     ChangeControl control;
     try {
-      control = changeControlFactory.validateFor(changes.get(0));
+      control = changeControlFactory.validateFor(changes.get(0), user.get());
     } catch (NoSuchChangeException e) {
       throw new ResourceNotFoundException(id);
     }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Files.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Files.java
index ec8234f..98e2ee9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Files.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Files.java
@@ -37,7 +37,6 @@
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.patch.PatchList;
 import com.google.gerrit.server.patch.PatchListCache;
-import com.google.gerrit.server.patch.PatchListEntry;
 import com.google.gerrit.server.patch.PatchListNotAvailableException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -45,9 +44,9 @@
 
 import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevTree;
 import org.eclipse.jgit.revwalk.RevWalk;
 import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
 import org.kohsuke.args4j.Option;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -219,23 +218,49 @@
           List<String> pathList = Lists.newArrayListWithCapacity(sz);
 
           RevWalk rw = new RevWalk(reader);
-          RevTree o = rw.parseCommit(oldList.getNewId()).getTree();
-          RevTree c = rw.parseCommit(curList.getNewId()).getTree();
-          for (PatchListEntry p : curList.getPatches()) {
-            String path = p.getNewName();
-            if (!Patch.COMMIT_MSG.equals(path) && paths.contains(path)) {
-              TreeWalk tw = TreeWalk.forPath(reader, path, o, c);
-              if (tw != null
-                  && tw.getRawMode(0) != 0
-                  && tw.getRawMode(1) != 0
-                  && tw.idEqual(0, 1)) {
-                inserts.add(new AccountPatchReview(
-                    new Patch.Key(
-                        resource.getPatchSet().getId(),
-                        path),
-                      userId));
-                pathList.add(path);
-              }
+          TreeWalk tw = new TreeWalk(reader);
+          tw.setFilter(PathFilterGroup.createFromStrings(paths));
+          tw.setRecursive(true);
+          int o = tw.addTree(rw.parseCommit(oldList.getNewId()).getTree());
+          int c = tw.addTree(rw.parseCommit(curList.getNewId()).getTree());
+
+          int op = -1;
+          if (oldList.getOldId() != null) {
+            op = tw.addTree(rw.parseCommit(oldList.getOldId()).getTree());
+          }
+
+          int cp = -1;
+          if (curList.getOldId() != null) {
+            cp = tw.addTree(rw.parseCommit(curList.getOldId()).getTree());
+          }
+
+          while (tw.next()) {
+            String path = tw.getPathString();
+            if (tw.getRawMode(o) != 0 && tw.getRawMode(c) != 0
+                && tw.idEqual(o, c)
+                && paths.contains(path)) {
+              // File exists in previously reviewed oldList and in curList.
+              // File content is identical.
+              inserts.add(new AccountPatchReview(
+                  new Patch.Key(
+                      resource.getPatchSet().getId(),
+                      path),
+                    userId));
+              pathList.add(path);
+            } else if (op >= 0 && cp >= 0
+                && tw.getRawMode(o) == 0 && tw.getRawMode(c) == 0
+                && tw.getRawMode(op) != 0 && tw.getRawMode(cp) != 0
+                && tw.idEqual(op, cp)
+                && paths.contains(path)) {
+              // File was deleted in previously reviewed oldList and curList.
+              // File exists in ancestor of oldList and curList.
+              // File content is identical in ancestors.
+              inserts.add(new AccountPatchReview(
+                  new Patch.Key(
+                      resource.getPatchSet().getId(),
+                      path),
+                    userId));
+              pathList.add(path);
             }
           }
           db.get().accountPatchReviews().insert(inserts);
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 7fb09b5..9ed2bee 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
@@ -31,6 +31,7 @@
 public class Module extends RestApiModule {
   @Override
   protected void configure() {
+    bind(ChangesCollection.class);
     bind(Revisions.class);
     bind(Reviewers.class);
     bind(Drafts.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
index d87db2f..698a0ac 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
@@ -94,6 +94,15 @@
       return controlFor(change, user);
     }
 
+    public ChangeControl validateFor(Change change, CurrentUser user)
+        throws NoSuchChangeException, OrmException {
+      ChangeControl c = controlFor(change, user);
+      if (!c.isVisible(db.get())) {
+        throw new NoSuchChangeException(c.getChange().getId());
+      }
+      return c;
+    }
+
     public ChangeControl validateFor(Change.Id id, CurrentUser user)
         throws NoSuchChangeException, OrmException {
       ChangeControl c = controlFor(id, user);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
index f98822f..c111c02 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
@@ -496,6 +496,10 @@
       try {
         Map<String, Ref> allRefs = repo.getRefDatabase().getRefs(ALL);
         for (Entry<String, Ref> entry : allRefs.entrySet()) {
+          String refName = entry.getKey();
+          if (!refName.startsWith("refs/heads") && !refName.startsWith("refs/tags")) {
+            continue;
+          }
           RevCommit tip;
           try {
             tip = rw.parseCommit(entry.getValue().getObjectId());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java
index 44c71a9..4b6a5a6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java
@@ -24,7 +24,6 @@
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.change.ChangeJson;
 import com.google.gerrit.server.change.ChangeJson.ChangeInfo;
-import com.google.gerrit.server.project.ChangeControl;
 import com.google.gerrit.server.query.QueryParseException;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
@@ -82,16 +81,12 @@
   }
 
   @Inject
-  QueryChanges(ChangeJson json,
-      QueryProcessor qp,
-      ChangeControl.Factory cf,
-      Provider<CurrentUser> user) {
+  QueryChanges(ChangeJson json, QueryProcessor qp, Provider<CurrentUser> user) {
     this.json = json;
     this.imp = qp;
     this.user = user;
 
     options = EnumSet.noneOf(ListChangesOption.class);
-    json.setChangeControlFactory(cf);
   }
 
   public void addQuery(String query) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
index 5624f45..2f3fe54 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
@@ -98,7 +98,8 @@
   private final ChangeQueryRewriter queryRewriter;
   private final Provider<ReviewDb> db;
   private final GitRepositoryManager repoManager;
-  private final ChangeControl.Factory changeControlFactory;
+  private final ChangeControl.GenericFactory changeControlFactory;
+  private final CurrentUser user;
   private final int maxLimit;
 
   private OutputFormat outputFormat = OutputFormat.TEXT;
@@ -123,13 +124,14 @@
       ChangeQueryBuilder.Factory queryBuilder, CurrentUser currentUser,
       ChangeQueryRewriter queryRewriter, Provider<ReviewDb> db,
       GitRepositoryManager repoManager,
-      ChangeControl.Factory changeControlFactory) {
+      ChangeControl.GenericFactory changeControlFactory) {
     this.eventFactory = eventFactory;
     this.queryBuilder = queryBuilder.create(currentUser);
     this.queryRewriter = queryRewriter;
     this.db = db;
     this.repoManager = repoManager;
     this.changeControlFactory = changeControlFactory;
+    this.user = currentUser;
     this.maxLimit = currentUser.getCapabilities()
       .getRange(GlobalCapability.QUERY_LIMIT)
       .getMax();
@@ -298,8 +300,11 @@
         List<ChangeData> results = queryChanges(queryString);
         ChangeAttribute c = null;
         for (ChangeData d : results) {
-          LabelTypes labelTypes = changeControlFactory.controlFor(d.getChange())
-              .getLabelTypes();
+          ChangeControl cc = d.changeControl();
+          if (cc == null || cc.getCurrentUser() != user) {
+            cc = changeControlFactory.controlFor(d.change(db), user);
+          }
+          LabelTypes labelTypes = cc.getLabelTypes();
           c = eventFactory.asChangeAttribute(d.getChange());
           eventFactory.extend(c, d.getChange());
           eventFactory.addTrackingIds(c, d.trackingIds(db));
@@ -307,7 +312,7 @@
           if (includeSubmitRecords) {
             PatchSet.Id psId = d.getChange().currentPatchSetId();
             PatchSet patchSet = db.get().patchSets().get(psId);
-            List<SubmitRecord> submitResult = d.changeControl().canSubmit( //
+            List<SubmitRecord> submitResult = cc.canSubmit( //
                 db.get(), patchSet, null, false, true, true);
             eventFactory.addSubmitRecords(c, submitResult);
           }
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/GerritServerSession.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/GerritServerSession.java
new file mode 100644
index 0000000..b7f7c22
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/GerritServerSession.java
@@ -0,0 +1,34 @@
+// 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.sshd;
+
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
+import org.apache.sshd.common.io.IoSession;
+import org.apache.sshd.server.ServerFactoryManager;
+import org.apache.sshd.server.session.ServerSession;
+
+/* Expose addition of close session listeners */
+class GerritServerSession extends ServerSession {
+
+  GerritServerSession(ServerFactoryManager server,
+      IoSession ioSession) throws Exception {
+    super(server, ioSession);
+  }
+
+  void addCloseSessionListener(SshFutureListener<CloseFuture> l) {
+    closeFuture.addListener(l);
+  }
+}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java
index 8519e94..bafc388 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshDaemon.java
@@ -15,7 +15,6 @@
 package com.google.gerrit.sshd;
 
 import static com.google.gerrit.server.ssh.SshAddressesModule.IANA_SSH_PORT;
-
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 import static java.util.concurrent.TimeUnit.SECONDS;
 
@@ -35,20 +34,18 @@
 import com.jcraft.jsch.HostKey;
 import com.jcraft.jsch.JSchException;
 
-import org.apache.mina.core.future.IoFuture;
-import org.apache.mina.core.future.IoFutureListener;
-import org.apache.mina.core.service.IoAcceptor;
-import org.apache.mina.core.session.IoSession;
 import org.apache.mina.transport.socket.SocketSessionConfig;
 import org.apache.sshd.SshServer;
 import org.apache.sshd.common.Channel;
 import org.apache.sshd.common.Cipher;
 import org.apache.sshd.common.Compression;
+import org.apache.sshd.common.ForwardingFilter;
 import org.apache.sshd.common.KeyExchange;
 import org.apache.sshd.common.KeyPairProvider;
 import org.apache.sshd.common.NamedFactory;
 import org.apache.sshd.common.Session;
 import org.apache.sshd.common.Signature;
+import org.apache.sshd.common.SshdSocketAddress;
 import org.apache.sshd.common.cipher.AES128CBC;
 import org.apache.sshd.common.cipher.AES192CBC;
 import org.apache.sshd.common.cipher.AES256CBC;
@@ -56,6 +53,19 @@
 import org.apache.sshd.common.cipher.CipherNone;
 import org.apache.sshd.common.cipher.TripleDESCBC;
 import org.apache.sshd.common.compression.CompressionNone;
+import org.apache.sshd.common.file.FileSystemFactory;
+import org.apache.sshd.common.file.FileSystemView;
+import org.apache.sshd.common.file.SshFile;
+import org.apache.sshd.common.forward.DefaultTcpipForwarderFactory;
+import org.apache.sshd.common.forward.TcpipServerChannel;
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
+import org.apache.sshd.common.io.IoAcceptor;
+import org.apache.sshd.common.io.IoServiceFactory;
+import org.apache.sshd.common.io.IoSession;
+import org.apache.sshd.common.io.mina.MinaServiceFactory;
+import org.apache.sshd.common.io.mina.MinaSession;
+import org.apache.sshd.common.io.nio2.Nio2ServiceFactory;
 import org.apache.sshd.common.mac.HMACMD5;
 import org.apache.sshd.common.mac.HMACMD596;
 import org.apache.sshd.common.mac.HMACSHA1;
@@ -63,26 +73,21 @@
 import org.apache.sshd.common.random.BouncyCastleRandom;
 import org.apache.sshd.common.random.JceRandom;
 import org.apache.sshd.common.random.SingletonRandomFactory;
+import org.apache.sshd.common.session.AbstractSession;
 import org.apache.sshd.common.signature.SignatureDSA;
 import org.apache.sshd.common.signature.SignatureRSA;
 import org.apache.sshd.common.util.Buffer;
 import org.apache.sshd.common.util.SecurityUtils;
 import org.apache.sshd.server.Command;
 import org.apache.sshd.server.CommandFactory;
-import org.apache.sshd.server.FileSystemFactory;
-import org.apache.sshd.server.FileSystemView;
-import org.apache.sshd.server.ForwardingFilter;
 import org.apache.sshd.server.PublickeyAuthenticator;
-import org.apache.sshd.server.SshFile;
 import org.apache.sshd.server.UserAuth;
 import org.apache.sshd.server.auth.UserAuthPublicKey;
 import org.apache.sshd.server.auth.gss.GSSAuthenticator;
 import org.apache.sshd.server.auth.gss.UserAuthGSS;
-import org.apache.sshd.server.channel.ChannelDirectTcpip;
 import org.apache.sshd.server.channel.ChannelSession;
 import org.apache.sshd.server.kex.DHG1;
 import org.apache.sshd.server.kex.DHG14;
-import org.apache.sshd.server.session.ServerSession;
 import org.apache.sshd.server.session.SessionFactory;
 import org.eclipse.jgit.lib.Config;
 import org.slf4j.Logger;
@@ -91,7 +96,6 @@
 import java.io.File;
 import java.io.IOException;
 import java.net.InetAddress;
-import java.net.InetSocketAddress;
 import java.net.SocketAddress;
 import java.net.UnknownHostException;
 import java.security.InvalidKeyException;
@@ -126,6 +130,11 @@
 public class SshDaemon extends SshServer implements SshInfo, LifecycleListener {
   private static final Logger log = LoggerFactory.getLogger(SshDaemon.class);
 
+  public static enum SshSessionBackend {
+    MINA,
+    NIO2
+  }
+
   private final List<SocketAddress> listen;
   private final List<String> advertised;
   private final boolean keepAlive;
@@ -144,7 +153,6 @@
 
     this.listen = listen;
     this.advertised = advertised;
-    reuseAddress = cfg.getBoolean("sshd", "reuseaddress", true);
     keepAlive = cfg.getBoolean("sshd", "tcpkeepalive", true);
 
     getProperties().put(SERVER_IDENTIFICATION,
@@ -161,12 +169,6 @@
 
     long idleTimeoutSeconds = ConfigUtil.getTimeUnit(cfg, "sshd", null,
         "idleTimeout", 0, SECONDS);
-    if (idleTimeoutSeconds == 0) {
-      // Since Apache SSHD does not allow to turn off closing idle connections,
-      // we fake it by using the highest timeout allowed by Apache SSHD, which
-      // amounts to ~24 days.
-      idleTimeoutSeconds = MILLISECONDS.toSeconds(Integer.MAX_VALUE);
-    }
     getProperties().put(
         IDLE_TIMEOUT,
         String.valueOf(SECONDS.toMillis(idleTimeoutSeconds)));
@@ -183,6 +185,14 @@
     final String kerberosPrincipal = cfg.getString(
         "sshd", null, "kerberosPrincipal");
 
+    SshSessionBackend backend = cfg.getEnum(
+        "sshd", null, "backend", SshSessionBackend.MINA);
+
+    System.setProperty(IoServiceFactory.class.getName(),
+        backend == SshSessionBackend.MINA
+            ? MinaServiceFactory.class.getName()
+            : Nio2ServiceFactory.class.getName());
+
     if (SecurityUtils.isBouncyCastleRegistered()) {
       initProviderBouncyCastle();
     } else {
@@ -192,7 +202,7 @@
     initMacs(cfg);
     initSignatures();
     initChannels();
-    initForwardingFilter();
+    initForwarding();
     initFileSystemFactory();
     initSubsystems();
     initCompression();
@@ -202,24 +212,28 @@
     setShellFactory(noShell);
     setSessionFactory(new SessionFactory() {
       @Override
-      protected ServerSession createSession(final IoSession io)
+      protected AbstractSession createSession(final IoSession io)
           throws Exception {
-        if (io.getConfig() instanceof SocketSessionConfig) {
-          final SocketSessionConfig c = (SocketSessionConfig) io.getConfig();
-          c.setKeepAlive(keepAlive);
+        if (io instanceof MinaSession) {
+          if (((MinaSession) io).getSession()
+              .getConfig() instanceof SocketSessionConfig) {
+            ((SocketSessionConfig) ((MinaSession) io).getSession()
+                .getConfig())
+                .setKeepAlive(keepAlive);
+          }
         }
 
-        final ServerSession s = (ServerSession) super.createSession(io);
-        final int id = idGenerator.next();
-        final SocketAddress peer = io.getRemoteAddress();
+        GerritServerSession s = (GerritServerSession)super.createSession(io);
+        int id = idGenerator.next();
+        SocketAddress peer = io.getRemoteAddress();
         final SshSession sd = new SshSession(id, peer);
         s.setAttribute(SshSession.KEY, sd);
 
         // Log a session close without authentication as a failure.
         //
-        io.getCloseFuture().addListener(new IoFutureListener<IoFuture>() {
+        s.addCloseSessionListener(new SshFutureListener<CloseFuture>() {
           @Override
-          public void operationComplete(IoFuture future) {
+          public void operationComplete(CloseFuture future) {
             if (sd.isAuthenticationError()) {
               sshLog.onAuthFail(sd);
             }
@@ -227,6 +241,12 @@
         });
         return s;
       }
+
+      @Override
+      protected AbstractSession doCreateSession(IoSession ioSession)
+          throws Exception {
+        return new GerritServerSession(server, ioSession);
+      }
     });
 
     hostKeys = computeHostKeys();
@@ -245,13 +265,11 @@
   public synchronized void start() {
     if (acceptor == null && !listen.isEmpty()) {
       checkConfig();
-
+      if (sessionFactory == null) {
+        sessionFactory = createSessionFactory();
+      }
+      sessionFactory.setServer(this);
       acceptor = createAcceptor();
-      configure(acceptor);
-
-      final SessionFactory handler = getSessionFactory();
-      handler.setServer(this);
-      acceptor.setHandler(handler);
 
       try {
         acceptor.bind(listen);
@@ -259,7 +277,8 @@
         throw new IllegalStateException("Cannot bind to " + addressList(), e);
       }
 
-      log.info("Started Gerrit SSHD on " + addressList());
+      log.info(String.format("Started Gerrit %s on %s",
+          version, addressList()));
     }
   }
 
@@ -473,7 +492,7 @@
   private void initChannels() {
     setChannelFactories(Arrays.<NamedFactory<Channel>> asList(
         new ChannelSession.Factory(), //
-        new ChannelDirectTcpip.Factory() //
+        new TcpipServerChannel.DirectTcpipFactory() //
         ));
   }
 
@@ -514,28 +533,29 @@
     setPublickeyAuthenticator(pubkey);
   }
 
-  private void initForwardingFilter() {
-    setForwardingFilter(new ForwardingFilter() {
+  private void initForwarding() {
+    setTcpipForwardingFilter(new ForwardingFilter() {
       @Override
-      public boolean canForwardAgent(ServerSession session) {
-        return false;
+      public boolean canForwardAgent(Session session) {
+          return false;
       }
 
       @Override
-      public boolean canForwardX11(ServerSession session) {
-        return false;
+      public boolean canForwardX11(Session session) {
+          return false;
       }
 
       @Override
-      public boolean canConnect(InetSocketAddress address, ServerSession session) {
-        return false;
+      public boolean canListen(SshdSocketAddress address, Session session) {
+          return false;
       }
 
       @Override
-      public boolean canListen(InetSocketAddress address, ServerSession session) {
-        return false;
+      public boolean canConnect(SshdSocketAddress address, Session session) {
+          return false;
       }
     });
+    setTcpipForwarderFactory(new DefaultTcpipForwarderFactory());
   }
 
   private void initFileSystemFactory() {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshUtil.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshUtil.java
index fc1303c..a2f2c1d 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshUtil.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshUtil.java
@@ -21,8 +21,8 @@
 import com.google.gerrit.sshd.SshScope.Context;
 
 import org.apache.commons.codec.binary.Base64;
-import org.apache.mina.core.future.IoFuture;
-import org.apache.mina.core.future.IoFutureListener;
+import org.apache.sshd.common.future.CloseFuture;
+import org.apache.sshd.common.future.SshFutureListener;
 import org.apache.sshd.common.KeyPairProvider;
 import org.apache.sshd.common.SshException;
 import org.apache.sshd.common.util.Buffer;
@@ -138,10 +138,11 @@
         sshScope.set(old);
       }
 
-      session.getIoSession().getCloseFuture().addListener(
-          new IoFutureListener<IoFuture>() {
+      GerritServerSession s = (GerritServerSession) session;
+      s.addCloseSessionListener(
+          new SshFutureListener<CloseFuture>() {
             @Override
-            public void operationComplete(IoFuture future) {
+            public void operationComplete(CloseFuture future) {
               final Context ctx = sshScope.newContext(null, sd, null);
               final Context old = sshScope.set(ctx);
               try {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowCaches.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowCaches.java
index 89ba6ba..ff1de80 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowCaches.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowCaches.java
@@ -32,8 +32,9 @@
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
-import org.apache.mina.core.service.IoAcceptor;
-import org.apache.mina.core.session.IoSession;
+import org.apache.sshd.common.io.IoAcceptor;
+import org.apache.sshd.common.io.IoSession;
+import org.apache.sshd.common.io.mina.MinaSession;
 import org.apache.sshd.server.Environment;
 import org.eclipse.jgit.internal.storage.file.WindowCacheStatAccessor;
 import org.kohsuke.args4j.Option;
@@ -274,8 +275,12 @@
     long now = TimeUtil.nowMs();
     Collection<IoSession> list = acceptor.getManagedSessions().values();
     long oldest = now;
+
     for (IoSession s : list) {
-      oldest = Math.min(oldest, s.getCreationTime());
+      if (s instanceof MinaSession) {
+        MinaSession minaSession = (MinaSession)s;
+        oldest = Math.min(oldest, minaSession.getSession().getCreationTime());
+      }
     }
 
     stdout.format(
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowConnections.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowConnections.java
index f8531ed..d97d750 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowConnections.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ShowConnections.java
@@ -26,8 +26,9 @@
 import com.google.gerrit.sshd.SshSession;
 import com.google.inject.Inject;
 
-import org.apache.mina.core.service.IoAcceptor;
-import org.apache.mina.core.session.IoSession;
+import org.apache.sshd.common.io.IoAcceptor;
+import org.apache.sshd.common.io.IoSession;
+import org.apache.sshd.common.io.mina.MinaSession;
 import org.apache.sshd.server.Environment;
 import org.apache.sshd.server.session.ServerSession;
 import org.kohsuke.args4j.Option;
@@ -84,10 +85,16 @@
     Collections.sort(list, new Comparator<IoSession>() {
       @Override
       public int compare(IoSession arg0, IoSession arg1) {
-        if (arg0.getCreationTime() < arg1.getCreationTime()) {
-          return -1;
-        } else if (arg0.getCreationTime() > arg1.getCreationTime()) {
-          return 1;
+        if (arg0 instanceof MinaSession) {
+          MinaSession mArg0 = (MinaSession) arg0;
+          MinaSession mArg1 = (MinaSession) arg1;
+          if (mArg0.getSession().getCreationTime() < mArg1.getSession()
+              .getCreationTime()) {
+            return -1;
+          } else if (mArg0.getSession().getCreationTime() > mArg1.getSession()
+              .getCreationTime()) {
+            return 1;
+          }
         }
         return (int) (arg0.getId() - arg1.getId());
       }
@@ -104,8 +111,15 @@
       SshSession sd = s != null ? s.getAttribute(SshSession.KEY) : null;
 
       final SocketAddress remoteAddress = io.getRemoteAddress();
-      final long start = io.getCreationTime();
-      final long idle = now - io.getLastIoTime();
+      MinaSession minaSession = io instanceof MinaSession
+          ? (MinaSession) io
+          : null;
+      final long start = minaSession == null
+          ? 0
+          : minaSession.getSession().getCreationTime();
+      final long idle = minaSession == null
+          ? now
+          : now - minaSession.getSession().getLastIoTime();
 
       stdout.print(String.format("%8s %8s %8s   %-15.15s %s\n", //
           id(sd), //
diff --git a/gerrit-war/src/main/resources/log4j.properties b/gerrit-war/src/main/resources/log4j.properties
index 1fcca6d..cb14916 100644
--- a/gerrit-war/src/main/resources/log4j.properties
+++ b/gerrit-war/src/main/resources/log4j.properties
@@ -26,7 +26,7 @@
 log4j.logger.org.apache.sshd.common=WARN
 log4j.logger.org.apache.sshd.server=WARN
 log4j.logger.org.apache.sshd.common.keyprovider.FileKeyPairProvider=INFO
-log4j.logger.com.google.gerrit.server.ssh.GerritServerSession=WARN
+log4j.logger.com.google.gerrit.sshd.GerritServerSession=WARN
 
 # Silence non-critical messages from Jetty.
 #
diff --git a/lib/mina/BUCK b/lib/mina/BUCK
index 3e9558a..9467cc4 100644
--- a/lib/mina/BUCK
+++ b/lib/mina/BUCK
@@ -8,17 +8,18 @@
 
 maven_jar(
   name = 'core',
-  id = 'org.apache.mina:mina-core:2.0.5',
-  sha1 = '0e134a3761833a3c28c79331e806f64f985a9eec',
+  id = 'org.apache.mina:mina-core:2.0.7',
+  sha1 = 'c878e2aa82de748474a624ec3933e4604e446dec',
   license = 'Apache2.0',
   exclude = EXCLUDE,
 )
 
 maven_jar(
   name = 'sshd',
-  id = 'org.apache.sshd:sshd-core:0.6.0',
-  sha1 = '2b9a119dd77a1decec78b0c511ba400c8655e96e',
+  id = 'org.apache.sshd:sshd-core:0.9.0.201311081',
+  sha1 = '38f7ac8602e70fa05fdc6147d204198e9cefe5bc',
   license = 'Apache2.0',
   deps = [':core'],
   exclude = EXCLUDE,
+  repository = GERRIT,
 )