Merge "Buck: Update Python version prerequisite: 2.7 is required"
diff --git a/Documentation/config-labels.txt b/Documentation/config-labels.txt
index 0fe12ca..aaeb834 100644
--- a/Documentation/config-labels.txt
+++ b/Documentation/config-labels.txt
@@ -183,14 +183,6 @@
 box will be either the lowest or highest score in the permissible range.
 
 
-[[label_abbreviation]]
-=== `label.Label-Name.abbreviation`
-
-An abbreviated name for a label shown as a compact column header, for
-example on project dashboards. Defaults to all the uppercase characters
-in the label name, e.g. `Label-Name` is abbreviated by default as `LN`.
-
-
 [[label_function]]
 === `label.Label-Name.function`
 
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 5d7fe95..f95c546 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -2923,7 +2923,7 @@
 
 [[comment-input]]
 === CommentInput
-The `CommitInput` entity contains information for creating an inline
+The `CommentInput` entity contains information for creating an inline
 comment.
 
 [options="header",width="50%",cols="1,^1,5"]
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 5c5746b..fc55411 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -21,6 +21,7 @@
 
 import com.google.common.base.Joiner;
 import com.google.common.primitives.Chars;
+import com.google.gerrit.acceptance.AcceptanceTestRequestScope.Context;
 import com.google.gerrit.extensions.api.GerritApi;
 import com.google.gerrit.extensions.api.changes.RevisionApi;
 import com.google.gerrit.extensions.common.ChangeInfo;
@@ -128,9 +129,9 @@
     userSession = new RestSession(server, user);
     initSsh(admin);
     db = reviewDbProvider.open();
-    atrScope.set(atrScope.newContext(reviewDbProvider, sshSession,
-        identifiedUserFactory.create(Providers.of(db), admin.getId())));
-    sshSession = new SshSession(server, admin);
+    Context ctx = newRequestContext(admin);
+    atrScope.set(ctx);
+    sshSession = ctx.getSession();
     project = new Project.NameKey("p");
     createProject(sshSession, project.get());
     git = cloneProject(sshSession.getUrl() + "/" + project.get());
@@ -194,6 +195,19 @@
     return gApi.changes().id(id).get(s);
   }
 
+  protected List<ChangeInfo> query(String q) throws RestApiException {
+    return gApi.changes().query(q).get();
+  }
+
+  private Context newRequestContext(TestAccount account) {
+    return atrScope.newContext(reviewDbProvider, new SshSession(server, admin),
+        identifiedUserFactory.create(Providers.of(db), account.getId()));
+  }
+
+  protected Context setApiUser(TestAccount account) {
+    return atrScope.set(newRequestContext(account));
+  }
+
   protected static Gson newGson() {
     return OutputFormat.JSON_COMPACT.newGson();
   }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
index a01525a..c79b198 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/api/change/ChangeIT.java
@@ -186,9 +186,9 @@
   @Test
   public void queryChangesNoResults() throws Exception {
     createChange();
-    List<ChangeInfo> results = gApi.changes().query("status:open").get();
+    List<ChangeInfo> results = query("status:open");
     assertEquals(1, results.size());
-    results = gApi.changes().query("status:closed").get();
+    results = query("status:closed");
     assertTrue(results.isEmpty());
   }
 
@@ -196,7 +196,7 @@
   public void queryChangesOneTerm() throws Exception {
     PushOneCommit.Result r1 = createChange();
     PushOneCommit.Result r2 = createChange();
-    List<ChangeInfo> results = gApi.changes().query("status:open").get();
+    List<ChangeInfo> results = query("status:open");
     assertEquals(2, results.size());
     assertEquals(r2.getChangeId(), results.get(0).changeId);
     assertEquals(r1.getChangeId(), results.get(1).changeId);
@@ -206,9 +206,7 @@
   public void queryChangesMultipleTerms() throws Exception {
     PushOneCommit.Result r1 = createChange();
     createChange();
-    List<ChangeInfo> results = gApi.changes()
-        .query("status:open " + r1.getChangeId())
-        .get();
+    List<ChangeInfo> results = query("status:open " + r1.getChangeId());
     assertEquals(r1.getChangeId(), Iterables.getOnlyElement(results).changeId);
   }
 
@@ -232,8 +230,7 @@
   @Test
   public void queryChangesNoOptions() throws Exception {
     PushOneCommit.Result r = createChange();
-    ChangeInfo result = Iterables.getOnlyElement(
-        gApi.changes().query(r.getChangeId()).get());
+    ChangeInfo result = Iterables.getOnlyElement(query(r.getChangeId()));
     assertNull(result.labels);
     assertNull(result.messages);
     assertNull(result.revisions);
@@ -256,4 +253,29 @@
     assertEquals(r.getPatchSetId().get(), rev._number);
     assertFalse(rev.actions.isEmpty());
   }
+
+  @Test
+  public void queryChangesOwnerWithDifferentUsers() throws Exception {
+    PushOneCommit.Result r = createChange();
+    assertEquals(r.getChangeId(),
+        Iterables.getOnlyElement(query("owner:self")).changeId);
+    setApiUser(user);
+    assertTrue(query("owner:self").isEmpty());
+  }
+
+  @Test
+  public void checkReviewedFlagBeforeAndAfterReview() throws Exception {
+    PushOneCommit.Result r = createChange();
+    AddReviewerInput in = new AddReviewerInput();
+    in.reviewer = user.email;
+    gApi.changes()
+        .id(r.getChangeId())
+        .addReviewer(in);
+
+    setApiUser(user);
+    assertNull(get(r.getChangeId()).reviewed);
+
+    revision(r).review(ReviewInput.recommend());
+    assertTrue(get(r.getChangeId()).reviewed);
+  }
 }
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 d63d195..f42a134 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
@@ -10,6 +10,10 @@
 
 acceptance_tests(
   srcs = OTHER_TESTS,
+  deps = [
+    ':submit_util',
+    '//lib/joda:joda-time',
+  ],
   labels = ['rest'],
 )
 
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java
index 6352b50..acf50db 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/ChangeMessagesIT.java
@@ -14,6 +14,8 @@
 
 package com.google.gerrit.acceptance.rest.change;
 
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
@@ -23,14 +25,55 @@
 import com.google.gerrit.extensions.common.ChangeInfo;
 import com.google.gerrit.extensions.common.ChangeMessageInfo;
 import com.google.gerrit.extensions.restapi.RestApiException;
+import com.google.gerrit.testutil.ConfigSuite;
 
 import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.lib.Config;
+import org.joda.time.DateTime;
+import org.joda.time.DateTimeUtils;
+import org.joda.time.DateTimeUtils.MillisProvider;
+import org.junit.After;
+import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.io.IOException;
 import java.util.Iterator;
+import java.util.concurrent.atomic.AtomicLong;
 
+@RunWith(ConfigSuite.class)
 public class ChangeMessagesIT extends AbstractDaemonTest {
+  private String systemTimeZone;
+  private volatile long clockStepMs;
+
+  @ConfigSuite.Config
+  public static Config noteDbEnabled() {
+    Config cfg = new Config();
+    cfg.setBoolean("notedb", null, "write", true);
+    cfg.setBoolean("notedb", "changeMessages", "read", true);
+    return cfg;
+  }
+
+  @Before
+  public void setTimeForTesting() {
+    systemTimeZone = System.setProperty("user.timezone", "US/Eastern");
+    clockStepMs = MILLISECONDS.convert(1, SECONDS);
+    final AtomicLong clockMs = new AtomicLong(
+        new DateTime(2009, 9, 30, 17, 0, 0).getMillis());
+
+    DateTimeUtils.setCurrentMillisProvider(new MillisProvider() {
+      @Override
+      public long getMillis() {
+        return clockMs.getAndAdd(clockStepMs);
+      }
+    });
+  }
+
+  @After
+  public void resetTime() {
+    DateTimeUtils.setCurrentMillisSystem();
+    System.setProperty("user.timezone", systemTimeZone);
+  }
 
   @Test
   public void messagesNotReturnedByDefault() throws Exception {
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/LabelType.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/LabelType.java
index 28629b9..337aefd 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/LabelType.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/LabelType.java
@@ -60,7 +60,7 @@
     return name;
   }
 
-  public static String defaultAbbreviation(String name) {
+  private static String getAbbreviation(String name) {
     StringBuilder abbr = new StringBuilder();
     for (int i = 0; i < name.length(); i++) {
       char c = name.charAt(i);
@@ -128,7 +128,7 @@
     values = sortValues(valueList);
     defaultValue = 0;
 
-    abbreviation = defaultAbbreviation(name);
+    abbreviation = getAbbreviation(name);
     functionName = "MaxWithBlock";
 
     maxNegative = Short.MIN_VALUE;
@@ -151,14 +151,6 @@
     return psa.getLabelId().get().equalsIgnoreCase(name);
   }
 
-  public String getAbbreviation() {
-    return abbreviation;
-  }
-
-  public void setAbbreviation(String abbreviation) {
-    this.abbreviation = abbreviation;
-  }
-
   public String getFunctionName() {
     return functionName;
   }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/CacheBasedWebSession.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/CacheBasedWebSession.java
index b472bbd..d315fa3 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/CacheBasedWebSession.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/CacheBasedWebSession.java
@@ -68,7 +68,8 @@
     this.anonymousProvider = anonymousProvider;
     this.identified = identified;
 
-    if (!GitSmartHttpTools.isGitClient(request)) {
+    if (request.getRequestURI() == null
+        || !GitSmartHttpTools.isGitClient(request)) {
       String cookie = readCookie();
       if (cookie != null) {
         key = new Key(cookie);
@@ -184,6 +185,10 @@
   }
 
   private void saveCookie() {
+    if (response == null) {
+      return;
+    }
+
     final String token;
     final int ageSeconds;
 
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/H2CacheBasedWebSession.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/H2CacheBasedWebSession.java
index 3dfb9fb..4b68f02 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/H2CacheBasedWebSession.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/H2CacheBasedWebSession.java
@@ -17,6 +17,7 @@
 import static java.util.concurrent.TimeUnit.MINUTES;
 
 import com.google.common.cache.Cache;
+import com.google.gerrit.common.Nullable;
 import com.google.gerrit.extensions.registration.DynamicItem;
 import com.google.gerrit.httpd.WebSessionManager.Val;
 import com.google.gerrit.server.AnonymousUser;
@@ -56,7 +57,7 @@
   @Inject
   H2CacheBasedWebSession(
       HttpServletRequest request,
-      HttpServletResponse response,
+      @Nullable HttpServletResponse response,
       WebSessionManagerFactory managerFactory,
       @Named(WebSessionManager.CACHE_NAME) Cache<String, Val> cache,
       AuthConfig authConfig,
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
index 215736e..907760b 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/plugins/HttpPluginServlet.java
@@ -83,6 +83,13 @@
   private static final long serialVersionUID = 1L;
   private static final Logger log
       = LoggerFactory.getLogger(HttpPluginServlet.class);
+  private static final Comparator<JarEntry> JAR_ENTRY_COMPARATOR_BY_NAME =
+      new Comparator<JarEntry>() {
+        @Override
+        public int compare(JarEntry a, JarEntry b) {
+          return a.getName().compareTo(b.getName());
+        }
+      };
 
   private final MimeUtilFileTypeRegistry mimeUtil;
   private final Provider<String> webUrl;
@@ -369,18 +376,9 @@
         }
       }
     }
-    Collections.sort(cmds, new Comparator<JarEntry>() {
-      @Override
-      public int compare(JarEntry a, JarEntry b) {
-        return a.getName().compareTo(b.getName());
-      }
-    });
-    Collections.sort(docs, new Comparator<JarEntry>() {
-      @Override
-      public int compare(JarEntry a, JarEntry b) {
-        return a.getName().compareTo(b.getName());
-      }
-    });
+
+    Collections.sort(cmds, JAR_ENTRY_COMPARATOR_BY_NAME);
+    Collections.sort(docs, JAR_ENTRY_COMPARATOR_BY_NAME);
 
     StringBuilder md = new StringBuilder();
     md.append(String.format("# Plugin %s #\n", pluginName));
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 80ddfe9..653310d 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
@@ -717,6 +717,7 @@
       List<ChangeMessage> msgs) throws OrmException {
     // Sort messages to keep the most recent ones at the beginning.
     msgs = ChangeNotes.MESSAGE_BY_TIME.sortedCopy(msgs);
+    Collections.reverse(msgs);
 
     Account.Id changeOwnerId = cd.change().getOwner();
     for (ChangeMessage cm : msgs) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java
index b9b5621..8902af9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeUtil.java
@@ -38,6 +38,7 @@
 import com.google.inject.assistedinject.AssistedInject;
 
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.LargeObjectException;
 import org.eclipse.jgit.errors.MissingObjectException;
 import org.eclipse.jgit.errors.NoMergeBaseException;
 import org.eclipse.jgit.errors.NoMergeBaseException.MergeBaseFailureReason;
@@ -395,6 +396,9 @@
     final ThreeWayMerger m = newThreeWayMerger(repo, createDryRunInserter());
     try {
       return m.merge(new AnyObjectId[] {mergeTip, toMerge});
+    } catch (LargeObjectException e) {
+      log.warn("Cannot merge due to LargeObjectException: " + toMerge.name());
+      return false;
     } catch (NoMergeBaseException e) {
       return false;
     } catch (IOException e) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
index c4c1b7f..a905385 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
@@ -130,7 +130,6 @@
   private static final String KEY_LOCAL_DEFAULT = "local-default";
 
   private static final String LABEL = "label";
-  private static final String KEY_ABBREVIATION = "abbreviation";
   private static final String KEY_FUNCTION = "function";
   private static final String KEY_DEFAULT_VALUE = "defaultValue";
   private static final String KEY_COPY_MIN_SCORE = "copyMinScore";
@@ -673,10 +672,6 @@
             "Invalid label \"%s\"", name)));
         continue;
       }
-      String abbr = rc.getString(LABEL, name, KEY_ABBREVIATION);
-      if (abbr != null) {
-        label.setAbbreviation(abbr);
-      }
 
       String functionName = Objects.firstNonNull(
           rc.getString(LABEL, name, KEY_FUNCTION), "MaxWithBlock");
@@ -1050,15 +1045,6 @@
       LabelType label = e.getValue();
       toUnset.remove(name);
       rc.setString(LABEL, name, KEY_FUNCTION, label.getFunctionName());
-
-      if (!LabelType.defaultAbbreviation(name)
-          .equals(label.getAbbreviation())) {
-        rc.setString(
-            LABEL, name, KEY_ABBREVIATION, label.getAbbreviation());
-      } else {
-        rc.unset(LABEL, name, KEY_ABBREVIATION);
-      }
-
       rc.setInt(LABEL, name, KEY_DEFAULT_VALUE, label.getDefaultValue());
       if (label.isCopyMinScore()) {
         rc.setBoolean(LABEL, name, KEY_COPY_MIN_SCORE, true);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
index db9b29d..173060b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/EqualsLabelPredicate.java
@@ -103,12 +103,6 @@
         return lt;
       }
     }
-
-    for (LabelType lt : types.getLabelTypes()) {
-      if (toFind.equalsIgnoreCase(lt.getAbbreviation())) {
-        return lt;
-      }
-    }
     return null;
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllProjectsCreator.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllProjectsCreator.java
index 7712650..1d8c126 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllProjectsCreator.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/AllProjectsCreator.java
@@ -215,7 +215,6 @@
         new LabelValue((short) 0, "No score"),
         new LabelValue((short) -1, "I would prefer this is not merged as is"),
         new LabelValue((short) -2, "This shall not be merged")));
-    type.setAbbreviation("CR");
     type.setCopyMinScore(true);
     c.getLabelSections().put(type.getName(), type);
     return type;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_77.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_77.java
index facfb75..a5166b6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_77.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_77.java
@@ -203,8 +203,7 @@
     Statement catStmt = ((JdbcSchema) db).getConnection().createStatement();
     try {
       ResultSet catRs = catStmt.executeQuery(
-          "SELECT category_id, name, abbreviated_name, function_name, "
-          + " copy_min_score"
+          "SELECT category_id, name, function_name, copy_min_score"
           + " FROM approval_categories"
           + " ORDER BY position, name");
       PreparedStatement valStmt = ((JdbcSchema) db).getConnection().prepareStatement(
@@ -224,7 +223,6 @@
           LegacyLabelType type =
               new LegacyLabelType(getLabelName(catRs.getString("name")), values);
           type.setId(id);
-          type.setAbbreviation(catRs.getString("abbreviated_name"));
           type.setFunctionName(catRs.getString("function_name"));
           type.setCopyMinScore("Y".equals(catRs.getString("copy_min_score")));
           types.add(type);
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaCreatorTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaCreatorTest.java
index 15a66ab..996aafa 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaCreatorTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaCreatorTest.java
@@ -128,7 +128,6 @@
     LabelType codeReview = getLabelTypes().byLabel("Code-Review");
     assertNotNull(codeReview);
     assertEquals("Code-Review", codeReview.getName());
-    assertEquals("CR", codeReview.getAbbreviation());
     assertEquals(0, codeReview.getDefaultValue());
     assertEquals("MaxWithBlock", codeReview.getFunctionName());
     assertTrue(codeReview.isCopyMinScore());
diff --git a/lib/codemirror/BUCK b/lib/codemirror/BUCK
index e56f51c..e8539c6 100644
--- a/lib/codemirror/BUCK
+++ b/lib/codemirror/BUCK
@@ -1,5 +1,6 @@
 include_defs('//lib/maven.defs')
 include_defs('//lib/codemirror/cm3.defs')
+include_defs('//lib/codemirror/closure.defs')
 
 VERSION = '28a638a984'
 SHA1 = '68f8f136092a5965778186fb401a33be34cf73ed'
@@ -8,6 +9,11 @@
 ZIP = 'codemirror-%s.zip' % VERSION
 TOP = 'codemirror-%s' % VERSION
 
+CLOSURE_COMPILER_ARGS = [
+  '--compilation_level SIMPLE_OPTIMIZATIONS',
+  '--warning_level QUIET'
+]
+
 genrule(
   name = 'css',
   cmd = ';'.join([
@@ -23,9 +29,8 @@
   out = 'cm3.css',
 )
 
-# TODO(sop) Minify with Closure JavaScript compiler.
 genrule(
-  name = 'js',
+  name = 'cm3-verbose',
   cmd = ';'.join([
       ':>$OUT',
       "echo '/** @license' >>$OUT",
@@ -36,7 +41,14 @@
      for n in CM3_JS]
   ),
   deps = [':zip'],
-  out = 'cm3.js',
+  out = 'cm3-verbose.js',
+)
+
+js_minify(
+  name = 'js',
+  generated = [':cm3-verbose'],
+  compiler_args = CLOSURE_COMPILER_ARGS,
+  out = 'cm3.js'
 )
 
 prebuilt_jar(
@@ -50,8 +62,13 @@
   name = 'jar',
   cmd = ';'.join([
     'cd $TMP',
-      'unzip -q $(location :zip) %s' %
-      ' '.join(['%s/mode/%s' % (TOP, n) for n in CM3_MODES]),
+    'unzip -q $(location :zip) %s' %
+    ' '.join(['%s/mode/%s' % (TOP, n) for n in CM3_MODES]),
+    ';'.join(['$(exe :js_minifier) ' +
+    ' '.join(CLOSURE_COMPILER_ARGS) +
+    ' --js_output_file %s/mode/%s.min --js %s/mode/%s'
+    % (TOP, n, TOP, n) for n in CM3_MODES]),
+    ';'.join(['mv %s/mode/%s.min %s/mode/%s' % (TOP, n, TOP, n) for n in CM3_MODES]),
     'mkdir net',
     'mv %s net/codemirror' % TOP,
     'mkdir net/codemirror/lib',
diff --git a/lib/codemirror/closure.defs b/lib/codemirror/closure.defs
new file mode 100644
index 0000000..e602b9f
--- /dev/null
+++ b/lib/codemirror/closure.defs
@@ -0,0 +1,50 @@
+# https://code.google.com/p/closure-compiler/wiki/BinaryDownloads?tm=2
+CLOSURE_VERSION = '20140407'
+CLOSURE_COMPILER_URL = 'http://dl.google.com/closure-compiler/compiler-%s.zip' % CLOSURE_VERSION
+COMPILER = 'compiler.jar'
+CLOSURE_COMPILER_SHA1 = 'eeb02bfd45eb4a080b66dd423eaee4bdd1d674e9'
+
+def js_minify(
+    name,
+    out,
+    compiler_args = [],
+    srcs = [],
+    generated = []):
+  cmd = ['$(exe :js_minifier) --js_output_file $OUT'] + compiler_args
+  if srcs:
+    cmd.append('$SRCS')
+  if generated:
+    cmd.extend(['$(location %s)' % n for n in generated])
+
+  genrule(
+    name = name,
+    cmd = ' '.join(cmd),
+    srcs = srcs,
+    out = out,
+)
+
+java_binary(
+  name = 'js_minifier',
+  main_class = 'com.google.javascript.jscomp.CommandLineRunner',
+  deps = [':compiler-jar']
+)
+
+prebuilt_jar(
+  name = 'compiler-jar',
+  binary_jar = ':compiler',
+)
+
+genrule(
+  name = 'compiler',
+  cmd = 'unzip -p $(location :closure-compiler-zip) %s >$OUT' % COMPILER,
+  out = COMPILER,
+)
+
+genrule(
+  name = 'closure-compiler-zip',
+  cmd = '$(exe //tools:download_file)' +
+    ' -o $OUT' +
+    ' -u ' + CLOSURE_COMPILER_URL +
+    ' -v ' + CLOSURE_COMPILER_SHA1,
+  out = 'closure-compiler.zip',
+)
diff --git a/lib/guice/BUCK b/lib/guice/BUCK
index 946bf9d..162ad07 100644
--- a/lib/guice/BUCK
+++ b/lib/guice/BUCK
@@ -1,6 +1,7 @@
 include_defs('//lib/maven.defs')
 
 VERSION = '4.0-beta'
+COOKIE_PATCH = '4.0-beta-98-g8d88344'
 EXCLUDE = [
   'META-INF/DEPENDENCIES',
   'META-INF/LICENSE',
@@ -41,8 +42,9 @@
 
 maven_jar(
   name = 'guice-servlet',
-  id = 'com.google.inject.extensions:guice-servlet:' + VERSION,
-  sha1 = '46b44984f254c0bf92d0c972fad1a70292ada28e',
+  id = 'com.google.inject.extensions:guice-servlet:' + COOKIE_PATCH,
+  repository = GERRIT,
+  sha1 = 'fa17d57a083fe9fc86b93f2dc37069573a2e65cd',
   license = 'Apache2.0',
   deps = [':guice'],
   exclude = EXCLUDE,
diff --git a/plugins/replication b/plugins/replication
index d92b6f2..3f67e32 160000
--- a/plugins/replication
+++ b/plugins/replication
@@ -1 +1 @@
-Subproject commit d92b6f2f92b06f0f189439029a2abb7107d23838
+Subproject commit 3f67e32dc01955cdec9bac64b19fc7cfcb20b816
diff --git a/tools/default.defs b/tools/default.defs
index b01e2d4..16f5d10 100644
--- a/tools/default.defs
+++ b/tools/default.defs
@@ -134,9 +134,9 @@
   java_binary(
     name = name,
     manifest_file = ':%s__manifest' % name,
+    merge_manifests = False,
     deps = [
       ':%s__plugin' % name,
-      ':%s__manifest' % name,
     ] + static_jars,
     visibility = visibility,
   )