Merge "ApprovalCopier: Remove option to exclude approvals from copying"
diff --git a/Documentation/config-project-config.txt b/Documentation/config-project-config.txt
index 91a054a..71af331 100644
--- a/Documentation/config-project-config.txt
+++ b/Documentation/config-project-config.txt
@@ -321,7 +321,7 @@
 
 - 'action': defines the link:#submit-type[submit type].  Valid
 values are 'fast forward only', 'merge if necessary', 'rebase if necessary',
-'merge always' and 'cherry pick'.  The default is 'merge if necessary'.
+'rebase always', 'merge always' and 'cherry pick'.  The default is 'merge if necessary'.
 
 - 'matchAuthorToCommitterDate': Defines whether to the author date will be changed to match the
 submitter date upon submit, so that git log shows when the change was submitted instead of when the
@@ -494,8 +494,9 @@
 [[fast_forward_only]]
 * Fast Forward Only
 +
-With this method no merge commits are produced. All merges must
-be handled on the client, prior to uploading to Gerrit for review.
+With this method Gerrit does not create merge commits on submitting a
+change. Merge commits may still be submitted, but they must be created
+on the client prior to uploading to Gerrit for review.
 +
 To submit a change, the change must be a strict superset of the
 destination branch.  That is, the change must already contain the
@@ -545,7 +546,7 @@
 branch, then the branch is fast-forwarded to the change.  If not,
 then the change is automatically rebased and then the branch is
 fast-forwarded to the change.
-
++
 When Gerrit tries to do a merge, by default the merge will only
 succeed if there is no path conflict.  A path conflict occurs when
 the same file has also been changed on the other side of the merge.
@@ -557,7 +558,7 @@
 if fast forward is possible AND like Cherry Pick it ensures footers such as
 Change-Id, Reviewed-On, and others are present in resulting commit that is
 merged.
-
++
 Thus, Rebase Always can be considered similar to Cherry Pick, but with
 the important distinction that Rebase Always does not ignore dependencies.
 
diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt
index cc3ac42..bee723e 100644
--- a/Documentation/user-search.txt
+++ b/Documentation/user-search.txt
@@ -53,7 +53,7 @@
 +
 Amount of time that has expired since the change was last updated
 with a review comment or new patch set.  The age must be specified
-to include a unit suffix, for example `age:2d`:
+to include a unit suffix, for example `-age:2d`:
 +
 * s, sec, second, seconds
 * m, min, minute, minutes
@@ -63,6 +63,10 @@
 * mon, month, months (`1 month` is treated as `30 days`)
 * y, year, years (`1 year` is treated as `365 days`)
 
+`age` can be used both forward and backward looking: `age:2d`
+means 'everything older than 2 days' while `-age:2d` means
+'everything with an age of at most 2 days'.
+
 [[assignee]]
 assignee:'USER'::
 +
diff --git a/WORKSPACE b/WORKSPACE
index fa72a35..ece7706 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -752,8 +752,8 @@
 # Keep this version of Soy synchronized with the version used in Gitiles.
 maven_jar(
     name = "soy",
-    artifact = "com.google.template:soy:2019-04-18",
-    sha1 = "5750208855562d74f29eee39ee497d5cf6df1490",
+    artifact = "com.google.template:soy:2019-07-14",
+    sha1 = "547dee679bac6011126f3a54619d3aec336216d0",
 )
 
 maven_jar(
diff --git a/java/com/google/gerrit/httpd/raw/IndexServlet.java b/java/com/google/gerrit/httpd/raw/IndexServlet.java
index 4c9fc3b..a0b41b21 100644
--- a/java/com/google/gerrit/httpd/raw/IndexServlet.java
+++ b/java/com/google/gerrit/httpd/raw/IndexServlet.java
@@ -74,11 +74,7 @@
       ImmutableMap<String, Object> templateData =
           IndexHtmlUtil.templateData(
               gerritApi, canonicalUrl, cdnPath, faviconPath, parameterMap, urlOrdainer);
-      renderer =
-          soySauce
-              .renderTemplate("com.google.gerrit.httpd.raw.Index")
-              .setExpectedContentKind(SanitizedContent.ContentKind.HTML)
-              .setData(templateData);
+      renderer = soySauce.renderTemplate("com.google.gerrit.httpd.raw.Index").setData(templateData);
     } catch (URISyntaxException | RestApiException e) {
       throw new IOException(e);
     }
@@ -87,7 +83,7 @@
     rsp.setContentType("text/html");
     rsp.setStatus(SC_OK);
     try (OutputStream w = rsp.getOutputStream()) {
-      w.write(renderer.render().get().getBytes(UTF_8));
+      w.write(renderer.renderHtml().get().toString().getBytes(UTF_8));
     }
   }
 }
diff --git a/java/com/google/gerrit/server/account/AccountsUpdate.java b/java/com/google/gerrit/server/account/AccountsUpdate.java
index cd47945..2920cef 100644
--- a/java/com/google/gerrit/server/account/AccountsUpdate.java
+++ b/java/com/google/gerrit/server/account/AccountsUpdate.java
@@ -330,7 +330,7 @@
               ExternalIdNotes extIdNotes =
                   createExternalIdNotes(r, accountConfig.getExternalIdsRev(), accountId, update);
               UpdatedAccount updatedAccounts =
-                  new UpdatedAccount(allUsersName, externalIds, message, accountConfig, extIdNotes);
+                  new UpdatedAccount(externalIds, message, accountConfig, extIdNotes);
               updatedAccounts.setCreated(true);
               return updatedAccounts;
             })
@@ -390,7 +390,7 @@
           ExternalIdNotes extIdNotes =
               createExternalIdNotes(r, accountConfig.getExternalIdsRev(), accountId, update);
           UpdatedAccount updatedAccounts =
-              new UpdatedAccount(allUsersName, externalIds, message, accountConfig, extIdNotes);
+              new UpdatedAccount(externalIds, message, accountConfig, extIdNotes);
           return updatedAccounts;
         });
   }
@@ -561,7 +561,6 @@
   }
 
   private static class UpdatedAccount {
-    private final AllUsersName allUsersName;
     private final ExternalIds externalIds;
     private final String message;
     private final AccountConfig accountConfig;
@@ -570,13 +569,11 @@
     private boolean created;
 
     private UpdatedAccount(
-        AllUsersName allUsersName,
         ExternalIds externalIds,
         String message,
         AccountConfig accountConfig,
         ExternalIdNotes extIdNotes) {
       checkState(!Strings.isNullOrEmpty(message), "message for account update must be set");
-      this.allUsersName = requireNonNull(allUsersName);
       this.externalIds = requireNonNull(externalIds);
       this.message = requireNonNull(message);
       this.accountConfig = requireNonNull(accountConfig);
diff --git a/java/com/google/gerrit/server/git/meta/VersionedMetaData.java b/java/com/google/gerrit/server/git/meta/VersionedMetaData.java
index 3f6564f..f2180d7 100644
--- a/java/com/google/gerrit/server/git/meta/VersionedMetaData.java
+++ b/java/com/google/gerrit/server/git/meta/VersionedMetaData.java
@@ -505,7 +505,7 @@
                     .projectName(projectName.get())
                     .noteDbRefName(getRefName())
                     .revision(revision.name())
-                    .noteDbFileName(fileName)
+                    .noteDbFilePath(fileName)
                     .build());
         TreeWalk tw = TreeWalk.forPath(reader, fileName, revision.getTree())) {
       if (tw != null) {
@@ -585,7 +585,7 @@
             Metadata.builder()
                 .projectName(projectName.get())
                 .noteDbRefName(getRefName())
-                .noteDbFileName(fileName)
+                .noteDbFilePath(fileName)
                 .build())) {
       DirCacheEditor editor = newTree.editor();
       if (raw != null && 0 < raw.length) {
diff --git a/java/com/google/gerrit/server/logging/Metadata.java b/java/com/google/gerrit/server/logging/Metadata.java
index bc8678c..400f48f 100644
--- a/java/com/google/gerrit/server/logging/Metadata.java
+++ b/java/com/google/gerrit/server/logging/Metadata.java
@@ -56,6 +56,9 @@
   // The value of the @Export annotation which was used to register a plugin extension.
   public abstract Optional<String> exportValue();
 
+  // Path of a file in a repository.
+  public abstract Optional<String> filePath();
+
   // Garbage collector name.
   public abstract Optional<String> garbageCollectorName();
 
@@ -86,8 +89,8 @@
   // Boolean: one or more
   public abstract Optional<Boolean> multiple();
 
-  // Name of a metadata file in NoteDb.
-  public abstract Optional<String> noteDbFileName();
+  // Path of a metadata file in NoteDb.
+  public abstract Optional<String> noteDbFilePath();
 
   // Name of a metadata ref in NoteDb.
   public abstract Optional<String> noteDbRefName();
@@ -98,6 +101,9 @@
   // Name of a "table" in NoteDb (if set, always CHANGES).
   public abstract Optional<String> noteDbTable();
 
+  // The ID of a patch set.
+  public abstract Optional<Integer> patchSetId();
+
   // Plugin metadata that doesn't fit into any other category.
   public abstract ImmutableList<PluginMetadata> pluginMetadata();
 
@@ -154,6 +160,8 @@
 
     public abstract Builder exportValue(@Nullable String exportValue);
 
+    public abstract Builder filePath(@Nullable String filePath);
+
     public abstract Builder garbageCollectorName(@Nullable String garbageCollectorName);
 
     public abstract Builder gitOperation(@Nullable String gitOperation);
@@ -174,7 +182,7 @@
 
     public abstract Builder multiple(boolean multiple);
 
-    public abstract Builder noteDbFileName(@Nullable String noteDbFileName);
+    public abstract Builder noteDbFilePath(@Nullable String noteDbFilePath);
 
     public abstract Builder noteDbRefName(@Nullable String noteDbRefName);
 
@@ -182,6 +190,8 @@
 
     public abstract Builder noteDbTable(@Nullable String noteDbTable);
 
+    public abstract Builder patchSetId(int patchSetId);
+
     abstract ImmutableList.Builder<PluginMetadata> pluginMetadataBuilder();
 
     public Builder addPluginMetadata(PluginMetadata pluginMetadata) {
diff --git a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
index 4313473..e56a38f 100644
--- a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
+++ b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
@@ -34,7 +34,7 @@
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.validators.OutgoingEmailValidationListener;
 import com.google.gerrit.server.validators.ValidationException;
-import com.google.template.soy.data.SanitizedContent;
+import com.google.template.soy.jbcsrc.api.SoySauce;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.ArrayList;
@@ -54,6 +54,7 @@
 
 /** Sends an email to one or more interested parties. */
 public abstract class OutgoingEmail {
+  private static final String SOY_TEMPLATE_NAMESPACE = "com.google.gerrit.server.mail.template.";
   private static final FluentLogger logger = FluentLogger.forEnclosingClass();
 
   protected String messageClass;
@@ -536,21 +537,19 @@
     return args.instanceNameProvider.get();
   }
 
-  private String soyTemplate(String name, SanitizedContent.ContentKind kind) {
-    return args.soySauce
-        .renderTemplate("com.google.gerrit.server.mail.template." + name)
-        .setExpectedContentKind(kind)
-        .setData(soyContext)
-        .render()
-        .get();
-  }
-
+  /** Renders a soy template of kind="text". */
   protected String textTemplate(String name) {
-    return soyTemplate(name, SanitizedContent.ContentKind.TEXT);
+    return configureRenderer(name).renderText().get();
   }
 
+  /** Renders a soy template of kind="html". */
   protected String soyHtmlTemplate(String name) {
-    return soyTemplate(name, SanitizedContent.ContentKind.HTML);
+    return configureRenderer(name).renderHtml().get().toString();
+  }
+
+  /** Configures a soy renderer for the given template name and rendering data map. */
+  private SoySauce.Renderer configureRenderer(String templateName) {
+    return args.soySauce.renderTemplate(SOY_TEMPLATE_NAMESPACE + templateName).setData(soyContext);
   }
 
   protected void removeUser(Account user) {
diff --git a/java/com/google/gerrit/server/mail/send/ProjectWatch.java b/java/com/google/gerrit/server/mail/send/ProjectWatch.java
index 5ab1acb..06a2e0d 100644
--- a/java/com/google/gerrit/server/mail/send/ProjectWatch.java
+++ b/java/com/google/gerrit/server/mail/send/ProjectWatch.java
@@ -99,9 +99,9 @@
           try {
             add(matching, state.getNameKey(), nc);
           } catch (QueryParseException e) {
-            logger.atWarning().log(
-                "Project %s has invalid notify %s filter \"%s\": %s",
-                state.getName(), nc.getName(), nc.getFilter(), e.getMessage());
+            logger.atWarning().withCause(e).log(
+                "Project %s has invalid notify %s filter \"%s\"",
+                state.getName(), nc.getName(), nc.getFilter());
           }
         }
       }
@@ -232,7 +232,8 @@
       logger.atFine().log("The filter did not match for account %s; skip notification", accountId);
     } catch (QueryParseException e) {
       // Ignore broken filter expressions.
-      logger.atWarning().log("Account %s has invalid filter in project watch %s", accountId, key);
+      logger.atWarning().withCause(e).log(
+          "Account %s has invalid filter in project watch %s", accountId, key);
     }
     return false;
   }
diff --git a/java/com/google/gerrit/server/schema/BUILD b/java/com/google/gerrit/server/schema/BUILD
index a8020b1..aa552ed 100644
--- a/java/com/google/gerrit/server/schema/BUILD
+++ b/java/com/google/gerrit/server/schema/BUILD
@@ -15,6 +15,7 @@
         "//java/com/google/gerrit/metrics",
         "//java/com/google/gerrit/reviewdb:server",
         "//java/com/google/gerrit/server",
+        "//java/com/google/gerrit/server/logging",
         "//java/com/google/gerrit/server/util/time",
         "//lib:guava",
         "//lib/auto:auto-value",
diff --git a/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java b/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java
index d7dbf58..cb91dea 100644
--- a/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java
+++ b/java/com/google/gerrit/server/schema/JdbcAccountPatchReviewStore.java
@@ -34,6 +34,9 @@
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.config.ThreadSettingsConfig;
+import com.google.gerrit.server.logging.Metadata;
+import com.google.gerrit.server.logging.TraceContext;
+import com.google.gerrit.server.logging.TraceContext.TraceTimer;
 import java.nio.file.Path;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
@@ -212,7 +215,15 @@
 
   @Override
   public boolean markReviewed(PatchSet.Id psId, Account.Id accountId, String path) {
-    try (Connection con = ds.getConnection();
+    try (TraceTimer ignored =
+            TraceContext.newTimer(
+                "Mark file as reviewed",
+                Metadata.builder()
+                    .patchSetId(psId.get())
+                    .accountId(accountId.get())
+                    .filePath(path)
+                    .build());
+        Connection con = ds.getConnection();
         PreparedStatement stmt =
             con.prepareStatement(
                 "INSERT INTO account_patch_reviews "
@@ -239,7 +250,15 @@
       return;
     }
 
-    try (Connection con = ds.getConnection();
+    try (TraceTimer ignored =
+            TraceContext.newTimer(
+                "Mark files as reviewed",
+                Metadata.builder()
+                    .patchSetId(psId.get())
+                    .accountId(accountId.get())
+                    .resourceCount(paths.size())
+                    .build());
+        Connection con = ds.getConnection();
         PreparedStatement stmt =
             con.prepareStatement(
                 "INSERT INTO account_patch_reviews "
@@ -264,7 +283,15 @@
 
   @Override
   public void clearReviewed(PatchSet.Id psId, Account.Id accountId, String path) {
-    try (Connection con = ds.getConnection();
+    try (TraceTimer ignored =
+            TraceContext.newTimer(
+                "Clear reviewed flag",
+                Metadata.builder()
+                    .patchSetId(psId.get())
+                    .accountId(accountId.get())
+                    .filePath(path)
+                    .build());
+        Connection con = ds.getConnection();
         PreparedStatement stmt =
             con.prepareStatement(
                 "DELETE FROM account_patch_reviews "
@@ -282,7 +309,11 @@
 
   @Override
   public void clearReviewed(PatchSet.Id psId) {
-    try (Connection con = ds.getConnection();
+    try (TraceTimer ignored =
+            TraceContext.newTimer(
+                "Clear all reviewed flags of patch set",
+                Metadata.builder().patchSetId(psId.get()).build());
+        Connection con = ds.getConnection();
         PreparedStatement stmt =
             con.prepareStatement(
                 "DELETE FROM account_patch_reviews "
@@ -297,7 +328,11 @@
 
   @Override
   public void clearReviewed(Change.Id changeId) {
-    try (Connection con = ds.getConnection();
+    try (TraceTimer ignored =
+            TraceContext.newTimer(
+                "Clear all reviewed flags of change",
+                Metadata.builder().changeId(changeId.get()).build());
+        Connection con = ds.getConnection();
         PreparedStatement stmt =
             con.prepareStatement("DELETE FROM account_patch_reviews WHERE change_id = ?")) {
       stmt.setInt(1, changeId.get());
@@ -309,7 +344,11 @@
 
   @Override
   public Optional<PatchSetWithReviewedFiles> findReviewed(PatchSet.Id psId, Account.Id accountId) {
-    try (Connection con = ds.getConnection();
+    try (TraceTimer ignored =
+            TraceContext.newTimer(
+                "Find reviewed flags",
+                Metadata.builder().patchSetId(psId.get()).accountId(accountId.get()).build());
+        Connection con = ds.getConnection();
         PreparedStatement stmt =
             con.prepareStatement(
                 "SELECT patch_set_id, file_name FROM account_patch_reviews APR1 "
diff --git a/java/com/google/gerrit/util/cli/CmdLineParser.java b/java/com/google/gerrit/util/cli/CmdLineParser.java
index be043fd..d8b9b58 100644
--- a/java/com/google/gerrit/util/cli/CmdLineParser.java
+++ b/java/com/google/gerrit/util/cli/CmdLineParser.java
@@ -41,7 +41,6 @@
 import com.google.common.base.Strings;
 import com.google.common.collect.ListMultimap;
 import com.google.common.collect.Lists;
-import com.google.common.collect.MultimapBuilder;
 import com.google.common.flogger.FluentLogger;
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
@@ -313,16 +312,6 @@
     parser.parseArgument(tmp.toArray(new String[tmp.size()]));
   }
 
-  public void parseOptionMap(Map<String, String[]> parameters) throws CmdLineException {
-    ListMultimap<String, String> map = MultimapBuilder.hashKeys().arrayListValues().build();
-    for (Map.Entry<String, String[]> ent : parameters.entrySet()) {
-      for (String val : ent.getValue()) {
-        map.put(ent.getKey(), val);
-      }
-    }
-    parseOptionMap(map);
-  }
-
   public void parseOptionMap(ListMultimap<String, String> params) throws CmdLineException {
     logger.atFinest().log("Command-line parameters: %s", params.keySet());
     List<String> knownArgs = Lists.newArrayListWithCapacity(2 * params.size());
diff --git a/javatests/com/google/gerrit/acceptance/api/project/CheckProjectIT.java b/javatests/com/google/gerrit/acceptance/api/project/CheckProjectIT.java
index 96ba722..27dd16a 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/CheckProjectIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/CheckProjectIT.java
@@ -287,10 +287,12 @@
             .branch("HEAD")
             .commit()
             .message("A change")
+            .insertChangeId()
             .author(admin.newIdent())
             .committer(new PersonIdent(admin.newIdent(), testRepo.getDate()))
             .create();
     pushHead(testRepo, "refs/for/master");
+
     return commit;
   }
 
diff --git a/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java b/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java
index 3503675..66af8a4 100644
--- a/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java
+++ b/javatests/com/google/gerrit/acceptance/git/PushPermissionsIT.java
@@ -30,10 +30,8 @@
 import com.google.gerrit.common.data.GlobalCapability;
 import com.google.gerrit.common.data.Permission;
 import com.google.gerrit.extensions.api.changes.ReviewInput;
-import com.google.gerrit.extensions.client.InheritableBoolean;
 import com.google.gerrit.extensions.client.ProjectState;
 import com.google.gerrit.extensions.common.ChangeInput;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.server.project.ProjectConfig;
@@ -61,8 +59,6 @@
   public void setUp() throws Exception {
     try (ProjectConfigUpdate u = updateProject(allProjects)) {
       ProjectConfig cfg = u.getConfig();
-      cfg.getProject()
-          .setBooleanConfig(BooleanProjectConfig.REQUIRE_CHANGE_ID, InheritableBoolean.FALSE);
 
       // Remove push-related permissions, so they can be added back individually by test methods.
       removeAllBranchPermissions(
@@ -255,7 +251,8 @@
         .add(allow(Permission.PUSH).ref("refs/for/refs/heads/*").group(REGISTERED_USERS))
         .update();
 
-    ObjectId commit = testRepo.branch("HEAD").commit().create();
+    ObjectId commit =
+        testRepo.branch("HEAD").commit().message("test commit").insertChangeId().create();
     assertThat(push("HEAD:refs/for/master")).onlyRef("refs/for/master").isOk();
     gApi.changes().id(commit.name()).current().review(ReviewInput.approve());