Merge "Make it configurable whether the first user should become admin"
diff --git a/Documentation/dev-contributing.txt b/Documentation/dev-contributing.txt
index be6d025..e4a7218 100644
--- a/Documentation/dev-contributing.txt
+++ b/Documentation/dev-contributing.txt
@@ -344,6 +344,18 @@
 doubt, do not hesitate to ask on the developer
 link:https://groups.google.com/forum/#!forum/repo-discuss[mailing list].
 
+=== Upgrading Libraries
+
+Gerrit's library dependencies should only be upgraded if the new version contains
+something we need in Gerrit. This includes new features, API changes as well as bug
+or security fixes.
+An exception to this rule is that right after a new Gerrit release was branched
+off, all libraries should be upgraded to the latest version to prevent Gerrit
+from falling behind. Doing those upgrades should conclude at the latest two
+months after the branch was cut. This should happen on the master branch to ensure
+that they are vetted long enough before they go into a release and we can be sure
+that the update doesn't introduce a regression.
+
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountsUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountsUpdate.java
index 069380f..54e06de 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountsUpdate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountsUpdate.java
@@ -17,6 +17,7 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.gerrit.common.Nullable;
 import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.RefNames;
 import com.google.gerrit.reviewdb.server.ReviewDb;
@@ -42,7 +43,12 @@
 import org.eclipse.jgit.lib.RefUpdate.Result;
 import org.eclipse.jgit.lib.Repository;
 
-/** Updates accounts. */
+/**
+ * Updates accounts.
+ *
+ * <p>On updating accounts this class takes care to evict them from the account cache and thus
+ * triggers reindex for them.
+ */
 @Singleton
 public class AccountsUpdate {
   /**
@@ -50,9 +56,6 @@
    *
    * <p>The Gerrit server identity will be used as author and committer for all commits that update
    * the accounts.
-   *
-   * <p>On updating accounts this class takes care to evict them from the account cache and thus
-   * triggers reindex for them.
    */
   @Singleton
   public static class Server {
@@ -80,6 +83,37 @@
   }
 
   /**
+   * Factory to create an AccountsUpdate instance for updating accounts by the Gerrit server.
+   *
+   * <p>Using this class will not perform reindexing for the updated accounts and they will also not
+   * be evicted from the account cache.
+   *
+   * <p>The Gerrit server identity will be used as author and committer for all commits that update
+   * the accounts.
+   */
+  @Singleton
+  public static class ServerNoReindex {
+    private final GitRepositoryManager repoManager;
+    private final AllUsersName allUsersName;
+    private final Provider<PersonIdent> serverIdent;
+
+    @Inject
+    public ServerNoReindex(
+        GitRepositoryManager repoManager,
+        AllUsersName allUsersName,
+        @GerritPersonIdent Provider<PersonIdent> serverIdent) {
+      this.repoManager = repoManager;
+      this.allUsersName = allUsersName;
+      this.serverIdent = serverIdent;
+    }
+
+    public AccountsUpdate create() {
+      PersonIdent i = serverIdent.get();
+      return new AccountsUpdate(repoManager, null, allUsersName, i, i);
+    }
+  }
+
+  /**
    * Factory to create an AccountsUpdate instance for updating accounts by the current user.
    *
    * <p>The identity of the current user will be used as author for all commits that update the
@@ -119,19 +153,19 @@
   }
 
   private final GitRepositoryManager repoManager;
-  private final AccountCache accountCache;
+  @Nullable private final AccountCache accountCache;
   private final AllUsersName allUsersName;
   private final PersonIdent committerIdent;
   private final PersonIdent authorIdent;
 
   private AccountsUpdate(
       GitRepositoryManager repoManager,
-      AccountCache accountCache,
+      @Nullable AccountCache accountCache,
       AllUsersName allUsersName,
       PersonIdent committerIdent,
       PersonIdent authorIdent) {
     this.repoManager = checkNotNull(repoManager, "repoManager");
-    this.accountCache = checkNotNull(accountCache, "accountCache");
+    this.accountCache = accountCache;
     this.allUsersName = checkNotNull(allUsersName, "allUsersName");
     this.committerIdent = checkNotNull(committerIdent, "committerIdent");
     this.authorIdent = checkNotNull(authorIdent, "authorIdent");
@@ -146,7 +180,7 @@
   public void insert(ReviewDb db, Account account) throws OrmException, IOException {
     db.accounts().insert(ImmutableSet.of(account));
     createUserBranch(account);
-    accountCache.evict(account.getId());
+    evictAccount(account.getId());
   }
 
   /**
@@ -157,13 +191,13 @@
   public void upsert(ReviewDb db, Account account) throws OrmException, IOException {
     db.accounts().upsert(ImmutableSet.of(account));
     createUserBranchIfNeeded(account);
-    accountCache.evict(account.getId());
+    evictAccount(account.getId());
   }
 
   /** Updates the account. */
   public void update(ReviewDb db, Account account) throws OrmException, IOException {
     db.accounts().update(ImmutableSet.of(account));
-    accountCache.evict(account.getId());
+    evictAccount(account.getId());
   }
 
   /**
@@ -185,7 +219,7 @@
                   consumer.accept(a);
                   return a;
                 });
-    accountCache.evict(accountId);
+    evictAccount(accountId);
     return account;
   }
 
@@ -193,14 +227,14 @@
   public void delete(ReviewDb db, Account account) throws OrmException, IOException {
     db.accounts().delete(ImmutableSet.of(account));
     deleteUserBranch(account.getId());
-    accountCache.evict(account.getId());
+    evictAccount(account.getId());
   }
 
   /** Deletes the account. */
   public void deleteByKey(ReviewDb db, Account.Id accountId) throws OrmException, IOException {
     db.accounts().deleteKeys(ImmutableSet.of(accountId));
     deleteUserBranch(accountId);
-    accountCache.evict(accountId);
+    evictAccount(accountId);
   }
 
   private void createUserBranch(Account account) throws IOException {
@@ -294,4 +328,10 @@
       throw new IOException(String.format("Failed to delete ref %s: %s", refName, result.name()));
     }
   }
+
+  private void evictAccount(Account.Id accountId) throws IOException {
+    if (accountCache != null) {
+      accountCache.evict(accountId);
+    }
+  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/externalids/ExternalIdsUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/externalids/ExternalIdsUpdate.java
index 9beae0e..2985504 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/externalids/ExternalIdsUpdate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/externalids/ExternalIdsUpdate.java
@@ -136,6 +136,47 @@
   }
 
   /**
+   * Factory to create an ExternalIdsUpdate instance for updating external IDs by the Gerrit server.
+   *
+   * <p>Using this class no reindex will be performed for the affected accounts and they will also
+   * not be evicted from the account cache.
+   *
+   * <p>The Gerrit server identity will be used as author and committer for all commits that update
+   * the external IDs.
+   */
+  @Singleton
+  public static class ServerNoReindex {
+    private final GitRepositoryManager repoManager;
+    private final AllUsersName allUsersName;
+    private final MetricMaker metricMaker;
+    private final ExternalIds externalIds;
+    private final ExternalIdCache externalIdCache;
+    private final Provider<PersonIdent> serverIdent;
+
+    @Inject
+    public ServerNoReindex(
+        GitRepositoryManager repoManager,
+        AllUsersName allUsersName,
+        MetricMaker metricMaker,
+        ExternalIds externalIds,
+        ExternalIdCache externalIdCache,
+        @GerritPersonIdent Provider<PersonIdent> serverIdent) {
+      this.repoManager = repoManager;
+      this.allUsersName = allUsersName;
+      this.metricMaker = metricMaker;
+      this.externalIds = externalIds;
+      this.externalIdCache = externalIdCache;
+      this.serverIdent = serverIdent;
+    }
+
+    public ExternalIdsUpdate create() {
+      PersonIdent i = serverIdent.get();
+      return new ExternalIdsUpdate(
+          repoManager, null, allUsersName, metricMaker, externalIds, externalIdCache, i, i);
+    }
+  }
+
+  /**
    * Factory to create an ExternalIdsUpdate instance for updating external IDs by the current user.
    *
    * <p>The identity of the current user will be used as author for all commits that update the
@@ -204,7 +245,7 @@
   private static final Retryer<RefsMetaExternalIdsUpdate> RETRYER = retryerBuilder().build();
 
   private final GitRepositoryManager repoManager;
-  private final AccountCache accountCache;
+  @Nullable private final AccountCache accountCache;
   private final AllUsersName allUsersName;
   private final ExternalIds externalIds;
   private final ExternalIdCache externalIdCache;
@@ -216,7 +257,7 @@
 
   private ExternalIdsUpdate(
       GitRepositoryManager repoManager,
-      AccountCache accountCache,
+      @Nullable AccountCache accountCache,
       AllUsersName allUsersName,
       MetricMaker metricMaker,
       ExternalIds externalIds,
@@ -239,7 +280,7 @@
   @VisibleForTesting
   public ExternalIdsUpdate(
       GitRepositoryManager repoManager,
-      AccountCache accountCache,
+      @Nullable AccountCache accountCache,
       AllUsersName allUsersName,
       MetricMaker metricMaker,
       ExternalIds externalIds,
@@ -375,7 +416,7 @@
               }
             });
     externalIdCache.onRemoveByKeys(u.oldRev(), u.newRev(), accountId, extIdKeys);
-    accountCache.evict(accountId);
+    evictAccount(accountId);
   }
 
   /**
@@ -434,7 +475,7 @@
               }
             });
     externalIdCache.onReplaceByKeys(u.oldRev(), u.newRev(), accountId, toDelete, toAdd);
-    accountCache.evict(accountId);
+    evictAccount(accountId);
   }
 
   /**
@@ -726,7 +767,17 @@
     return ins.insert(OBJ_TREE, new byte[] {});
   }
 
+  private void evictAccount(Account.Id accountId) throws IOException {
+    if (accountCache != null) {
+      accountCache.evict(accountId);
+    }
+  }
+
   private void evictAccounts(Collection<ExternalId> extIds) throws IOException {
+    if (accountCache == null) {
+      return;
+    }
+
     for (Account.Id id : extIds.stream().map(ExternalId::accountId).collect(toSet())) {
       accountCache.evict(id);
     }