Merge "PolyGerrit: replace event.path with event.composedPath" into stable-3.3
diff --git a/.bazelrc b/.bazelrc
index 604a64e..71feca1 100644
--- a/.bazelrc
+++ b/.bazelrc
@@ -1,4 +1,4 @@
-build --workspace_status_command="python ./tools/workspace_status.py"
+build --workspace_status_command="python3 ./tools/workspace_status.py"
build --repository_cache=~/.gerritcodereview/bazel-cache/repository
build --action_env=PATH
build --disk_cache=~/.gerritcodereview/bazel-cache/cas
diff --git a/.bazelversion b/.bazelversion
index fcdb2e1..6aba2b2 100644
--- a/.bazelversion
+++ b/.bazelversion
@@ -1 +1 @@
-4.0.0
+4.2.0
diff --git a/Documentation/dev-bazel.txt b/Documentation/dev-bazel.txt
index e6b7950..d5ed4ed 100644
--- a/Documentation/dev-bazel.txt
+++ b/Documentation/dev-bazel.txt
@@ -19,7 +19,7 @@
* A Linux or macOS system (Windows is not supported at this time)
* A JDK for Java 8|11|...
-* Python 2 or 3
+* Python 3
* link:https://github.com/nodesource/distributions/blob/master/README.md[Node.js (including npm),role=external,window=_blank]
* Bower (`npm install -g bower`)
* link:https://docs.bazel.build/versions/master/install.html[Bazel,role=external,window=_blank] -launched with
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index a68c38b..f6e63d7 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -74,9 +74,9 @@
manifest fields:
----
- Implementation-Title: Example plugin showing examples
- Implementation-Version: 1.0
- Implementation-Vendor: Example, Inc.
+Implementation-Title: Example plugin showing examples
+Implementation-Version: 1.0
+Implementation-Vendor: Example, Inc.
----
=== ApiType
@@ -88,7 +88,7 @@
loading a plugin that needs the plugin API.
----
- Gerrit-ApiType: plugin
+Gerrit-ApiType: plugin
----
=== Explicit Registration
@@ -103,9 +103,9 @@
`@Listen` and `@Export("")` annotations.
----
- Gerrit-Module: tld.example.project.CoreModuleClassName
- Gerrit-SshModule: tld.example.project.SshModuleClassName
- Gerrit-HttpModule: tld.example.project.HttpModuleClassName
+Gerrit-Module: tld.example.project.CoreModuleClassName
+Gerrit-SshModule: tld.example.project.SshModuleClassName
+Gerrit-HttpModule: tld.example.project.HttpModuleClassName
----
=== Batch runtime
@@ -120,7 +120,7 @@
offline reindexing task.
----
- Gerrit-BatchModule: tld.example.project.CoreModuleClassName
+Gerrit-BatchModule: tld.example.project.CoreModuleClassName
----
In this runtime, only the module designated by `Gerrit-BatchModule` is
@@ -132,7 +132,7 @@
A plugin can optionally provide its own plugin name.
----
- Gerrit-PluginName: replication
+Gerrit-PluginName: replication
----
This is useful for plugins that contribute plugin-owned capabilities that
@@ -218,7 +218,7 @@
with no down time.
----
- Gerrit-ReloadMode: restart
+Gerrit-ReloadMode: restart
----
In either mode ('restart' or 'reload') any plugin or extension can
@@ -261,7 +261,7 @@
credentials and possibly verify connectivity to validate them.
----
- Gerrit-InitStep: tld.example.project.MyInitStep
+Gerrit-InitStep: tld.example.project.MyInitStep
----
MyInitStep needs to follow the standard Gerrit InitStep syntax
@@ -278,37 +278,37 @@
[source,java]
----
- public class MyInitStep implements InitStep {
- private final String pluginName;
- private final ConsoleUI ui;
- private final AllProjectsConfig allProjectsConfig;
+public class MyInitStep implements InitStep {
+ private final String pluginName;
+ private final ConsoleUI ui;
+ private final AllProjectsConfig allProjectsConfig;
- @Inject
- public MyInitStep(@PluginName String pluginName, ConsoleUI ui,
- AllProjectsConfig allProjectsConfig) {
- this.pluginName = pluginName;
- this.ui = ui;
- this.allProjectsConfig = allProjectsConfig;
- }
-
- @Override
- public void run() throws Exception {
- }
-
- @Override
- public void postRun() throws Exception {
- ui.message("\n");
- ui.header(pluginName + " Integration");
- boolean enabled = ui.yesno(true, "By default enabled for all projects");
- Config cfg = allProjectsConfig.load().getConfig();
- if (enabled) {
- cfg.setBoolean("plugin", pluginName, "enabled", enabled);
- } else {
- cfg.unset("plugin", pluginName, "enabled");
- }
- allProjectsConfig.save(pluginName, "Initialize " + pluginName + " Integration");
- }
+ @Inject
+ public MyInitStep(@PluginName String pluginName, ConsoleUI ui,
+ AllProjectsConfig allProjectsConfig) {
+ this.pluginName = pluginName;
+ this.ui = ui;
+ this.allProjectsConfig = allProjectsConfig;
}
+
+ @Override
+ public void run() throws Exception {
+ }
+
+ @Override
+ public void postRun() throws Exception {
+ ui.message("\n");
+ ui.header(pluginName + " Integration");
+ boolean enabled = ui.yesno(true, "By default enabled for all projects");
+ Config cfg = allProjectsConfig.load().getConfig();
+ if (enabled) {
+ cfg.setBoolean("plugin", pluginName, "enabled", enabled);
+ } else {
+ cfg.unset("plugin", pluginName, "enabled");
+ }
+ allProjectsConfig.save(pluginName, "Initialize " + pluginName + " Integration");
+ }
+}
----
Bear in mind that the Plugin's InitStep class will be loaded but
@@ -706,9 +706,12 @@
in between, leading to the final operator name. An example
registration looks like this:
- bind(ChangeOperatorFactory.class)
- .annotatedWith(Exports.named("sample"))
- .to(SampleOperator.class);
+[source,java]
+----
+bind(ChangeOperatorFactory.class)
+ .annotatedWith(Exports.named("sample"))
+ .to(SampleOperator.class);
+----
If this is registered in the `myplugin` plugin, then the resulting
operator will be named `sample_myplugin`.
@@ -736,7 +739,7 @@
----
[[search_operands]]
-=== Search Operands ===
+== Search Operands
Plugins can define new search operands to extend change searching.
Plugin methods implementing search operands (returning a
@@ -748,31 +751,33 @@
a module's `configure()` method in the plugin.
The new operand, when used in a search would appear as:
- operatorName:operandName_pluginName
+ `operatorName:operandName_pluginName`
A sample `ChangeHasOperandFactory` class implementing, and registering, a
new `has:sample_pluginName` operand is shown below:
-====
- public class SampleHasOperand implements ChangeHasOperandFactory {
- public static class Module extends AbstractModule {
- @Override
- protected void configure() {
- bind(ChangeHasOperandFactory.class)
- .annotatedWith(Exports.named("sample")
- .to(SampleHasOperand.class);
- }
- }
-
+[source, java]
+----
+public class SampleHasOperand implements ChangeHasOperandFactory {
+ public static class Module extends AbstractModule {
@Override
- public Predicate<ChangeData> create(ChangeQueryBuilder builder)
- throws QueryParseException {
- return new HasSamplePredicate();
+ protected void configure() {
+ bind(ChangeHasOperandFactory.class)
+ .annotatedWith(Exports.named("sample")
+ .to(SampleHasOperand.class);
}
-====
+ }
+
+ @Override
+ public Predicate<ChangeData> create(ChangeQueryBuilder builder)
+ throws QueryParseException {
+ return new HasSamplePredicate();
+ }
+}
+----
[[command_options]]
-=== Command Options ===
+== Command Options
Plugins can provide additional options for each of the gerrit ssh and the
REST API commands by implementing the DynamicBean interface and registering
@@ -801,6 +806,64 @@
logger.atSevere().log("Say Hello in the Log %s", arg);
}
}
+}
+----
+
+Plugins can receive a bean object for each of the gerrit ssh and the REST API
+commands by implementing BeanParseListener interface and registering it to a
+command class name in the plugin module's `configure()` method. The below
+example shows a plugin that always limits the number of projects returned
+by the ls-projects SSH command.
+
+[source, java]
+----
+protected static class PluginModule extends AbstractModule {
+ @Override
+ public void configure() {
+ bind(DynamicOptions.DynamicBean.class)
+ .annotatedWith(Exports.named(ListProjectsCommand.class))
+ .to(ListProjectsCommandBeanListener.class);
+ }
+
+ protected static class ListProjectsCommandBeanListener
+ implements DynamicOptions.BeanParseListener {
+ @Override
+ public void onBeanParseStart(String plugin, Object bean) {
+ ListProjectsCommand command = (ListProjectsCommand) bean;
+ command.impl.setLimit(1);
+ }
+
+ @Override
+ public void onBeanParseEnd(String plugin, Object bean) {}
+ }
+}
+----
+
+The below example shows a plugin that always limits the number of projects
+returned by the /projects/ REST API.
+
+[source, java]
+----
+protected static class PluginModule extends AbstractModule {
+ @Override
+ public void configure() {
+ bind(DynamicOptions.DynamicBean.class)
+ .annotatedWith(Exports.named(ListProjects.class))
+ .to(ListProjectsBeanListener.class);
+ }
+
+ protected static class ListProjectsBeanListener
+ implements DynamicOptions.BeanParseListener {
+ @Override
+ public void onBeanParseStart(String plugin, Object bean) {
+ ListProjects listProjects = (ListProjects) bean;
+ listProjects.setLimit(1);
+ }
+
+ @Override
+ public void onBeanParseEnd(String plugin, Object bean) {}
+ }
+}
----
=== Calling Command Options ===
@@ -862,9 +925,9 @@
----
[[query_attributes]]
-=== Change Attributes ===
+== Change Attributes
-==== ChangePluginDefinedInfoFactory
+=== ChangePluginDefinedInfoFactory
Plugins can provide additional attributes to be returned from the Get Change and
Query Change APIs by implementing the `ChangePluginDefinedInfoFactory` interface
@@ -938,13 +1001,9 @@
}
----
-Example
+Example:
----
-
-ssh -p 29418 localhost gerrit query --myplugin-name--all "change:1" --format json
-
-Output:
-
+$ ssh -p 29418 localhost gerrit query --myplugin-name--all "change:1" --format json
{
"url" : "http://localhost:8080/1",
"plugins" : [
@@ -957,10 +1016,7 @@
...
}
-curl http://localhost:8080/changes/1?myplugin-name--all
-
-Output:
-
+$ curl http://localhost:8080/changes/1?myplugin-name--all
{
"_number": 1,
...
@@ -978,7 +1034,7 @@
Runtime exceptions generated by the implementors of ChangePluginDefinedInfoFactory
are encapsulated in PluginDefinedInfo objects which are part of SSH/REST query output.
-==== ChangeAttributeFactory
+=== ChangeAttributeFactory
Alternatively, there is also `ChangeAttributeFactory` which takes in one single
`ChangeData` at a time. `ChangePluginDefinedInfoFactory` should be preferred
@@ -1112,8 +1168,8 @@
`plugin.helloworld` subsection:
----
- [plugin "helloworld"]
- enabled = true
+[plugin "helloworld"]
+ enabled = true
----
Via the `com.google.gerrit.server.config.PluginConfigFactory` class a
@@ -1316,7 +1372,7 @@
Here and example of ref-updated JSON event payload with `instanceId`:
[source,json]
----
+----
{
"submitter": {
"name": "Administrator",
@@ -1333,7 +1389,7 @@
"eventCreatedOn": 1588849085,
"instanceId": "instance1"
}
----
+----
[[capabilities]]
== Plugin Owned Capabilities
@@ -1552,9 +1608,9 @@
Example config:
----
[extension-panels "CHANGE_SCREEN_BELOW_CHANGE_INFO_BLOCK"]
- panel = helloworld.change_id
- panel = myotherplugin
- panel = myplugin.my_panel_name
+ panel = helloworld.change_id
+ panel = myotherplugin
+ panel = myplugin.my_panel_name
----
@@ -1779,11 +1835,11 @@
can be accessed from any REST client, i. e.:
----
- curl -X POST -H "Content-Type: application/json" \
+$ curl -X POST -H "Content-Type: application/json" \
-d '{message: "François", french: true}' \
--user joe:secret \
http://host:port/a/changes/1/revisions/1/cookbook~say-hello
- "Bonjour François from change 1, patch set 1!"
+"Bonjour François from change 1, patch set 1!"
----
A special case is to bind an endpoint without a view name. This is
@@ -1880,7 +1936,6 @@
[source,java]
----
public class MyTopMenuExtension implements TopMenu {
-
@Override
public List<MenuEntry> getEntries() {
return Lists.newArrayList(
@@ -1897,7 +1952,6 @@
[source,java]
----
public class MyTopMenuExtension implements TopMenu {
-
@Override
public List<MenuEntry> getEntries() {
return Lists.newArrayList(
@@ -1915,17 +1969,17 @@
specific requests and add an menu item for this:
[source,java]
----
- new MenuItem("My Screen", "/plugins/myplugin/project/${projectName}");
----
+----
+new MenuItem("My Screen", "/plugins/myplugin/project/${projectName}");
+----
This also enables plugins to provide menu items for project aware
screens:
[source,java]
----
- new MenuItem("My Screen", "/x/my-screen/for/${projectName}");
----
+----
+new MenuItem("My Screen", "/x/my-screen/for/${projectName}");
+----
If no Guice modules are declared in the manifest, the top menu extension may use
auto-registration by providing an `@Listen` annotation:
@@ -2139,7 +2193,6 @@
bind(AccountExternalIdCreator.class)
.annotatedWith(UniqueAnnotations.create())
.to(MyExternalIdCreator.class);
-}
----
[[download-commands]]
@@ -2206,7 +2259,6 @@
@Listen
public class MyWeblinkPlugin implements PatchSetWebLink {
-
private String name = "MyLink";
private String placeHolderUrlProjectCommit = "http://my.tool.com/project=%s/commit=%s";
private String imageUrl = "http://placehold.it/16x16.gif";
@@ -2286,7 +2338,6 @@
import com.google.inject.servlet.ServletModule;
public class HttpModule extends ServletModule {
-
@Override
protected void configureServlets() {
serveRegex(URL_REGEX).with(LfsApiServlet.class);
@@ -2297,7 +2348,7 @@
import org.eclipse.jgit.lfs.server.s3.S3Repository;
public class S3LargeFileRepository extends S3Repository {
-...
+ ...
}
----
@@ -2347,8 +2398,8 @@
file. For example:
----
- [plugin "my-plugin"]
- metricsPrefix = my-metrics
+[plugin "my-plugin"]
+ metricsPrefix = my-metrics
----
will cause the metrics to be recorded under `my-metrics/${metric-name}`.
@@ -2371,6 +2422,7 @@
implementation, e.g. one that supports cluster setup with multiple
primary Gerrit nodes handling write operations.
+[source,java]
----
DynamicItem.bind(binder(), AccountPatchReviewStore.class)
.to(MultiMasterAccountPatchReviewStore.class);
@@ -2592,6 +2644,8 @@
@Override
public String intercept(String in) {
return pluginName + " mycommand";
+ }
+}
----
[[ssh-command-execution-interception]]
@@ -2625,7 +2679,8 @@
And then declare it in your SSH module:
[source, java]
----
- DynamicSet.bind(binder(), SshExecuteCommandInterceptor.class).to(SshExecuteCommandInterceptorImpl.class);
+DynamicSet.bind(binder(), SshExecuteCommandInterceptor.class)
+ .to(SshExecuteCommandInterceptorImpl.class);
----
[[pre-submit-evaluator]]
diff --git a/Documentation/intro-user.txt b/Documentation/intro-user.txt
index eb2025c..9909aac 100644
--- a/Documentation/intro-user.txt
+++ b/Documentation/intro-user.txt
@@ -459,7 +459,7 @@
push permission] on the destination branch.
The move operation will not update the change's parent and users will have
-to link:#rebase[rebase] the change. Also, merge commits cannot be moved.
+to link:#rebase[rebase] the change.
[[abandon]]
[[restore]]
diff --git a/Documentation/logs.txt b/Documentation/logs.txt
index 6624366..f072984 100644
--- a/Documentation/logs.txt
+++ b/Documentation/logs.txt
@@ -51,6 +51,11 @@
* `referer`: the `Referer` HTTP request header. This gives the site that
the client reports having been referred from.
* `client agent`: the client agent which sent the request.
+* `command status`: the overall result of the git command over HTTP. Currently
+ populated only for the transfer phase of `git-upload-pack` commands.
+ Possible values:
+** `-1`: The `git-upload-pack` transfer was ultimately not successful
+** `0`: The `git-upload-pack` transfer was ultimately successful
Example:
```
diff --git a/Documentation/replace_macros.py b/Documentation/replace_macros.py
index aaa9223..f270231 100755
--- a/Documentation/replace_macros.py
+++ b/Documentation/replace_macros.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# coding=utf-8
# Copyright (C) 2013 The Android Open Source Project
#
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index 92759b6..84da169 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -421,11 +421,11 @@
----
The `/projects/` URL also accepts a start integer in the `start`
-parameter. The results will skip `start` groups from project list.
+parameter. The results will skip `start` projects from project list.
Query 25 projects starting from index 50.
----
- GET /groups/?query=<query>&limit=25&start=50 HTTP/1.0
+ GET /projects/?query=<query>&limit=25&start=50 HTTP/1.0
----
[[project-query-options]]
@@ -2556,6 +2556,9 @@
Retrieves the branches and tags in which a change is included. As result
an link:rest-api-changes.html#included-in-info[IncludedInInfo] entity is returned.
+Branches that are not visible to the calling user according to the project's
+read permissions are filtered out from the result.
+
.Request
----
GET /projects/work%2Fmy-project/commits/a8a477efffbbf3b44169bb9a1d3a334cbbd9aa96/in HTTP/1.0
diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt
index 0c1ec2d..b22788a 100644
--- a/Documentation/user-search.txt
+++ b/Documentation/user-search.txt
@@ -305,8 +305,9 @@
+
Slash ('/') is used path separator.
+
-More examples:
-* `-file:^path/.*` - changes that do not modify files from `path/`,
+*More examples:*
+
+* `-file:^path/.*` - changes that do not modify files from `path/`.
* `file:{^~(path/.*)}` - changes that modify files not from `path/` (but may
contain files from `path/`).
@@ -555,14 +556,17 @@
author:'AUTHOR'::
+
Changes where 'AUTHOR' is the author of the current patch set. 'AUTHOR' may be
-the author's exact email address, or part of the name or email address.
+the author's exact email address, or part of the name or email address. The
+special case of `author:self` will find changes authored by the caller.
[[committer]]
committer:'COMMITTER'::
+
Changes where 'COMMITTER' is the committer of the current patch set.
'COMMITTER' may be the committer's exact email address, or part of the name or
-email address.
+email address. The special case of `committer:self` will find changes committed
+by the caller.
+
[[submittable]]
submittable:'SUBMIT_STATUS'::
diff --git a/WORKSPACE b/WORKSPACE
index b530e4e..6fe7af5 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1144,8 +1144,8 @@
bower_archive(
name = "codemirror-minified",
package = "Dominator008/codemirror-minified",
- sha1 = "a1ddf3a6dcc6817597eacc52688cfe5083ded4cd",
- version = "5.59.1",
+ sha1 = "904bae2a8716087fd21e92324e8a136a0c4a99b7",
+ version = "5.62.2",
)
# bower test stuff
diff --git a/contrib/check-valid-commit.py b/contrib/check-valid-commit.py
index 763ae3e..bb018f9 100755
--- a/contrib/check-valid-commit.py
+++ b/contrib/check-valid-commit.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
from __future__ import print_function
diff --git a/contrib/git-push-review b/contrib/git-push-review
index b995fc2..5a7f664 100755
--- a/contrib/git-push-review
+++ b/contrib/git-push-review
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (C) 2014 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/contrib/populate-fixture-data.py b/contrib/populate-fixture-data.py
index 4c6769c..e51e29d 100755
--- a/contrib/populate-fixture-data.py
+++ b/contrib/populate-fixture-data.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (C) 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java b/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java
index f6e5de3..e7354ab 100644
--- a/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java
+++ b/java/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImpl.java
@@ -138,6 +138,9 @@
throws IOException, ConfigInvalidException {
try (MetaDataUpdate metaDataUpdate = metaDataUpdateFactory.create(nameKey)) {
ProjectConfig projectConfig = projectConfigFactory.read(metaDataUpdate);
+ if (projectUpdate.removeAllAccessSections()) {
+ projectConfig.getAccessSections().forEach(as -> projectConfig.remove(as));
+ }
removePermissions(projectConfig, projectUpdate.removedPermissions());
addCapabilities(projectConfig, projectUpdate.addedCapabilities());
addPermissions(projectConfig, projectUpdate.addedPermissions());
diff --git a/java/com/google/gerrit/acceptance/testsuite/project/TestProjectUpdate.java b/java/com/google/gerrit/acceptance/testsuite/project/TestProjectUpdate.java
index ea20931..9a9a21a 100644
--- a/java/com/google/gerrit/acceptance/testsuite/project/TestProjectUpdate.java
+++ b/java/com/google/gerrit/acceptance/testsuite/project/TestProjectUpdate.java
@@ -294,7 +294,8 @@
return new AutoValue_TestProjectUpdate.Builder()
.nameKey(nameKey)
.allProjectsName(allProjectsName)
- .projectUpdater(projectUpdater);
+ .projectUpdater(projectUpdater)
+ .removeAllAccessSections(false);
}
/** Builder for {@link TestProjectUpdate}. */
@@ -314,6 +315,16 @@
abstract ImmutableMap.Builder<TestPermissionKey, Boolean> exclusiveGroupPermissionsBuilder();
+ abstract Builder removeAllAccessSections(boolean value);
+
+ /**
+ * Removes all access sections. Useful when testing against a specific set of access sections or
+ * permissions.
+ */
+ public Builder removeAllAccessSections() {
+ return removeAllAccessSections(true);
+ }
+
/** Adds a permission to be included in this update. */
public Builder add(TestPermission testPermission) {
addedPermissionsBuilder().add(testPermission);
@@ -418,6 +429,8 @@
abstract ThrowingConsumer<TestProjectUpdate> projectUpdater();
+ abstract boolean removeAllAccessSections();
+
boolean hasCapabilityUpdates() {
return !addedCapabilities().isEmpty()
|| removedPermissions().stream().anyMatch(k -> k.section().equals(GLOBAL_CAPABILITIES));
diff --git a/java/com/google/gerrit/httpd/GitOverHttpServlet.java b/java/com/google/gerrit/httpd/GitOverHttpServlet.java
index 6e22704..3e2d3ef 100644
--- a/java/com/google/gerrit/httpd/GitOverHttpServlet.java
+++ b/java/com/google/gerrit/httpd/GitOverHttpServlet.java
@@ -14,11 +14,15 @@
package com.google.gerrit.httpd;
+import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
+import static org.eclipse.jgit.http.server.GitSmartHttpTools.sendError;
+
import com.google.common.annotations.VisibleForTesting;
import com.google.common.cache.Cache;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
+import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.data.Capable;
import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.registration.DynamicSet;
@@ -54,6 +58,7 @@
import com.google.inject.TypeLiteral;
import com.google.inject.name.Named;
import java.io.IOException;
+import java.text.MessageFormat;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collections;
@@ -70,10 +75,13 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
+import org.eclipse.jgit.errors.PackProtocolException;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.http.server.GitServlet;
import org.eclipse.jgit.http.server.GitSmartHttpTools;
+import org.eclipse.jgit.http.server.HttpServerText;
import org.eclipse.jgit.http.server.ServletUtils;
+import org.eclipse.jgit.http.server.UploadPackErrorHandler;
import org.eclipse.jgit.http.server.resolver.AsIsFileService;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
@@ -100,6 +108,23 @@
private static final String ID_CACHE = "adv_bases";
public static final String URL_REGEX;
+ public static final String GIT_COMMAND_STATUS_HEADER = "X-git-command-status";
+
+ private enum GIT_COMMAND_STATUS {
+ OK(0),
+ FAIL(-1);
+
+ private final int exitStatus;
+
+ GIT_COMMAND_STATUS(int exitStatus) {
+ this.exitStatus = exitStatus;
+ }
+
+ @Override
+ public String toString() {
+ return Integer.toString(exitStatus);
+ }
+ }
static {
StringBuilder url = new StringBuilder();
@@ -212,12 +237,14 @@
Resolver resolver,
UploadFactory upload,
UploadFilter uploadFilter,
+ GerritUploadPackErrorHandler uploadPackErrorHandler,
ReceivePackFactory<HttpServletRequest> receive,
ReceiveFilter receiveFilter) {
setRepositoryResolver(resolver);
setAsIsFileService(AsIsFileService.DISABLED);
setUploadPackFactory(upload);
+ setUploadPackErrorHandler(uploadPackErrorHandler);
addUploadPackFilter(uploadFilter);
setReceivePackFactory(receive);
@@ -456,6 +483,35 @@
public void destroy() {}
}
+ static class GerritUploadPackErrorHandler implements UploadPackErrorHandler {
+ private static final FluentLogger logger = FluentLogger.forEnclosingClass();
+
+ @Override
+ public void upload(HttpServletRequest req, HttpServletResponse rsp, UploadPackRunnable r)
+ throws IOException {
+ rsp.setHeader(GIT_COMMAND_STATUS_HEADER, GIT_COMMAND_STATUS.FAIL.toString());
+ try {
+ r.upload();
+ rsp.setHeader(GIT_COMMAND_STATUS_HEADER, GIT_COMMAND_STATUS.OK.toString());
+ } catch (ServiceMayNotContinueException e) {
+ if (!e.isOutput() && !rsp.isCommitted()) {
+ rsp.reset();
+ sendError(req, rsp, e.getStatusCode(), e.getMessage());
+ }
+ } catch (Throwable e) {
+ logger.atSevere().withCause(e).log(
+ MessageFormat.format(
+ HttpServerText.get().internalErrorDuringUploadPack,
+ ServletUtils.getRepository(req)));
+ if (!rsp.isCommitted()) {
+ rsp.reset();
+ String msg = e instanceof PackProtocolException ? e.getMessage() : null;
+ sendError(req, rsp, SC_INTERNAL_SERVER_ERROR, msg);
+ }
+ }
+ }
+ }
+
static class ReceiveFactory implements ReceivePackFactory<HttpServletRequest> {
private final AsyncReceiveCommits.Factory factory;
private final Provider<CurrentUser> userProvider;
diff --git a/java/com/google/gerrit/httpd/plugins/PluginServletContext.java b/java/com/google/gerrit/httpd/plugins/PluginServletContext.java
index 40083e4..5a8fa31 100644
--- a/java/com/google/gerrit/httpd/plugins/PluginServletContext.java
+++ b/java/com/google/gerrit/httpd/plugins/PluginServletContext.java
@@ -199,6 +199,11 @@
String v = Version.getVersion();
return "Gerrit Code Review/" + (v != null ? v : "dev");
}
+
+ @Override
+ public String getVirtualServerName() {
+ return null;
+ }
}
interface API {
@@ -255,5 +260,7 @@
int getMinorVersion();
String getServerInfo();
+
+ String getVirtualServerName();
}
}
diff --git a/java/com/google/gerrit/pgm/http/jetty/HttpLog.java b/java/com/google/gerrit/pgm/http/jetty/HttpLog.java
index 4e4c93b..27a53c2 100644
--- a/java/com/google/gerrit/pgm/http/jetty/HttpLog.java
+++ b/java/com/google/gerrit/pgm/http/jetty/HttpLog.java
@@ -14,6 +14,8 @@
package com.google.gerrit.pgm.http.jetty;
+import static com.google.gerrit.httpd.GitOverHttpServlet.GIT_COMMAND_STATUS_HEADER;
+
import com.google.common.base.Strings;
import com.google.gerrit.httpd.GetUserFilter;
import com.google.gerrit.httpd.restapi.LogRedactUtil;
@@ -51,6 +53,7 @@
protected static final String P_LATENCY = "Latency";
protected static final String P_REFERER = "Referer";
protected static final String P_USER_AGENT = "User-Agent";
+ protected static final String P_COMMAND_STATUS = "Command-Status";
private final AsyncAppender async;
@@ -113,6 +116,7 @@
set(event, P_LATENCY, System.currentTimeMillis() - req.getTimeStamp());
set(event, P_REFERER, req.getHeader("Referer"));
set(event, P_USER_AGENT, req.getHeader("User-Agent"));
+ set(event, P_COMMAND_STATUS, rsp.getHeader(GIT_COMMAND_STATUS_HEADER));
async.append(event);
}
diff --git a/java/com/google/gerrit/pgm/http/jetty/HttpLogJsonLayout.java b/java/com/google/gerrit/pgm/http/jetty/HttpLogJsonLayout.java
index 95a5b07..807b311 100644
--- a/java/com/google/gerrit/pgm/http/jetty/HttpLogJsonLayout.java
+++ b/java/com/google/gerrit/pgm/http/jetty/HttpLogJsonLayout.java
@@ -14,6 +14,7 @@
package com.google.gerrit.pgm.http.jetty;
+import static com.google.gerrit.pgm.http.jetty.HttpLog.P_COMMAND_STATUS;
import static com.google.gerrit.pgm.http.jetty.HttpLog.P_CONTENT_LENGTH;
import static com.google.gerrit.pgm.http.jetty.HttpLog.P_HOST;
import static com.google.gerrit.pgm.http.jetty.HttpLog.P_LATENCY;
@@ -50,6 +51,7 @@
public String latency;
public String referer;
public String userAgent;
+ public String commandStatus;
public HttpJsonLogEntry(LoggingEvent event) {
this.host = getMdcString(event, P_HOST);
@@ -64,6 +66,7 @@
this.latency = getMdcString(event, P_LATENCY);
this.referer = getMdcString(event, P_REFERER);
this.userAgent = getMdcString(event, P_USER_AGENT);
+ this.commandStatus = getMdcString(event, P_COMMAND_STATUS);
}
}
}
diff --git a/java/com/google/gerrit/pgm/http/jetty/HttpLogLayout.java b/java/com/google/gerrit/pgm/http/jetty/HttpLogLayout.java
index 268f59f..b83fcc5 100644
--- a/java/com/google/gerrit/pgm/http/jetty/HttpLogLayout.java
+++ b/java/com/google/gerrit/pgm/http/jetty/HttpLogLayout.java
@@ -71,6 +71,9 @@
buf.append(' ');
dq_opt(buf, event, HttpLog.P_USER_AGENT);
+ buf.append(' ');
+ dq_opt(buf, event, HttpLog.P_COMMAND_STATUS);
+
buf.append('\n');
return buf.toString();
}
diff --git a/java/com/google/gerrit/server/DynamicOptions.java b/java/com/google/gerrit/server/DynamicOptions.java
index 41dc082..ebf4ec6 100644
--- a/java/com/google/gerrit/server/DynamicOptions.java
+++ b/java/com/google/gerrit/server/DynamicOptions.java
@@ -213,7 +213,7 @@
Class<?> beanClass =
(bean instanceof BeanReceiver)
? ((BeanReceiver) bean).getExportedBeanReceiver()
- : getClass();
+ : bean.getClass();
for (String plugin : dynamicBeans.plugins()) {
Provider<DynamicBean> provider =
dynamicBeans.byPlugin(plugin).get(beanClass.getCanonicalName());
diff --git a/java/com/google/gerrit/server/change/IncludedIn.java b/java/com/google/gerrit/server/change/IncludedIn.java
index 3c66c2c..c06ce82 100644
--- a/java/com/google/gerrit/server/change/IncludedIn.java
+++ b/java/com/google/gerrit/server/change/IncludedIn.java
@@ -14,6 +14,12 @@
package com.google.gerrit.server.change;
+import static com.google.common.collect.ImmutableList.toImmutableList;
+import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet;
+import static java.util.Comparator.naturalOrder;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
import com.google.gerrit.entities.Project;
@@ -23,13 +29,18 @@
import com.google.gerrit.extensions.restapi.ResourceConflictException;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.server.git.GitRepositoryManager;
+import com.google.gerrit.server.permissions.PermissionBackend;
+import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.plugincontext.PluginSetContext;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
+import java.util.Collection;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
@@ -37,17 +48,21 @@
@Singleton
public class IncludedIn {
private final GitRepositoryManager repoManager;
+ private final PermissionBackend permissionBackend;
private final PluginSetContext<ExternalIncludedIn> externalIncludedIn;
@Inject
IncludedIn(
- GitRepositoryManager repoManager, PluginSetContext<ExternalIncludedIn> externalIncludedIn) {
+ GitRepositoryManager repoManager,
+ PermissionBackend permissionBackend,
+ PluginSetContext<ExternalIncludedIn> externalIncludedIn) {
this.repoManager = repoManager;
+ this.permissionBackend = permissionBackend;
this.externalIncludedIn = externalIncludedIn;
}
public IncludedInInfo apply(Project.NameKey project, String revisionId)
- throws RestApiException, IOException {
+ throws RestApiException, IOException, PermissionBackendException {
try (Repository r = repoManager.openRepository(project);
RevWalk rw = new RevWalk(r)) {
rw.setRetainBody(false);
@@ -61,18 +76,48 @@
}
IncludedInResolver.Result d = IncludedInResolver.resolve(r, rw, rev);
+
+ // Filter branches and tags according to their visbility by the user
+ ImmutableSortedSet<String> filteredBranches =
+ sortedShortNames(filterReadableRefs(project, d.branches()));
+ ImmutableSortedSet<String> filteredTags =
+ sortedShortNames(filterReadableRefs(project, d.tags()));
+
ListMultimap<String, String> external = MultimapBuilder.hashKeys().arrayListValues().build();
externalIncludedIn.runEach(
ext -> {
ListMultimap<String, String> extIncludedIns =
- ext.getIncludedIn(project.get(), rev.name(), d.tags(), d.branches());
+ ext.getIncludedIn(project.get(), rev.name(), filteredBranches, filteredTags);
if (extIncludedIns != null) {
external.putAll(extIncludedIns);
}
});
return new IncludedInInfo(
- d.branches(), d.tags(), (!external.isEmpty() ? external.asMap() : null));
+ filteredBranches, filteredTags, (!external.isEmpty() ? external.asMap() : null));
}
}
+
+ /**
+ * Filter readable branches or tags according to the caller's refs visibility.
+ *
+ * @param project specific Gerrit project.
+ * @param inputRefs a list of branches (in short name) as strings
+ */
+ private Collection<String> filterReadableRefs(
+ Project.NameKey project, ImmutableList<Ref> inputRefs)
+ throws IOException, PermissionBackendException {
+ PermissionBackend.ForProject perm = permissionBackend.currentUser().project(project);
+ try (Repository repo = repoManager.openRepository(project)) {
+ return perm.filter(inputRefs, repo, RefFilterOptions.defaults()).stream()
+ .map(Ref::getName)
+ .collect(toImmutableList());
+ }
+ }
+
+ private ImmutableSortedSet<String> sortedShortNames(Collection<String> refs) {
+ return refs.stream()
+ .map(Repository::shortenRefName)
+ .collect(toImmutableSortedSet(naturalOrder()));
+ }
}
diff --git a/java/com/google/gerrit/server/change/IncludedInResolver.java b/java/com/google/gerrit/server/change/IncludedInResolver.java
index 09ca258..3891700 100644
--- a/java/com/google/gerrit/server/change/IncludedInResolver.java
+++ b/java/com/google/gerrit/server/change/IncludedInResolver.java
@@ -14,13 +14,11 @@
package com.google.gerrit.server.change;
-import static com.google.common.collect.ImmutableSortedSet.toImmutableSortedSet;
import static java.util.Comparator.comparing;
-import static java.util.Comparator.naturalOrder;
import static java.util.stream.Collectors.toList;
import com.google.auto.value.AutoValue;
-import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
@@ -171,13 +169,12 @@
* Returns the short names of refs which are as well in the matchingRefs list as well as in the
* allRef list.
*/
- private static ImmutableSortedSet<String> getMatchingRefNames(
+ private static ImmutableList<Ref> getMatchingRefNames(
Set<String> matchingRefs, Collection<Ref> allRefs) {
return allRefs.stream()
- .map(Ref::getName)
- .filter(matchingRefs::contains)
- .map(Repository::shortenRefName)
- .collect(toImmutableSortedSet(naturalOrder()));
+ .filter(r -> matchingRefs.contains(r.getName()))
+ .distinct()
+ .collect(ImmutableList.toImmutableList());
}
/** Parse commit of ref and store the relation between ref and commit. */
@@ -211,8 +208,8 @@
@AutoValue
public abstract static class Result {
- public abstract ImmutableSortedSet<String> branches();
+ public abstract ImmutableList<Ref> branches();
- public abstract ImmutableSortedSet<String> tags();
+ public abstract ImmutableList<Ref> tags();
}
}
diff --git a/java/com/google/gerrit/server/events/EventGsonProvider.java b/java/com/google/gerrit/server/events/EventGsonProvider.java
index 688507b..ab51518 100644
--- a/java/com/google/gerrit/server/events/EventGsonProvider.java
+++ b/java/com/google/gerrit/server/events/EventGsonProvider.java
@@ -30,7 +30,7 @@
.registerTypeAdapter(Supplier.class, new SupplierSerializer())
.registerTypeAdapter(Supplier.class, new SupplierDeserializer())
.registerTypeAdapter(Change.Key.class, new ChangeKeyAdapter())
- .registerTypeAdapter(Project.NameKey.class, new ProjectNameKeyAdapter())
+ .registerTypeHierarchyAdapter(Project.NameKey.class, new ProjectNameKeyAdapter())
.create();
}
}
diff --git a/java/com/google/gerrit/server/git/PermissionAwareReadOnlyRefDatabase.java b/java/com/google/gerrit/server/git/PermissionAwareReadOnlyRefDatabase.java
index b7dc2b3..7950dc6 100644
--- a/java/com/google/gerrit/server/git/PermissionAwareReadOnlyRefDatabase.java
+++ b/java/com/google/gerrit/server/git/PermissionAwareReadOnlyRefDatabase.java
@@ -14,7 +14,6 @@
package com.google.gerrit.server.git;
-import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;
import com.google.common.base.Preconditions;
@@ -95,20 +94,11 @@
return Iterables.getOnlyElement(result);
}
+ // WARNING: This method is deprecated in JGit's RefDatabase and it will be removed on master.
+ // Do not add any logic here but rather enrich the getRefsByPrefix method below.
@Override
public Map<String, Ref> getRefs(String prefix) throws IOException {
- List<Ref> refs = getDelegate().getRefDatabase().getRefsByPrefix(prefix);
- if (refs.isEmpty()) {
- return Collections.emptyMap();
- }
-
- Collection<Ref> result;
- try {
- result = forProject.filter(refs, getDelegate(), RefFilterOptions.defaults());
- } catch (PermissionBackendException e) {
- throw new IOException("", e);
- }
- return buildPrefixRefMap(prefix, result);
+ return buildPrefixRefMap(prefix, getRefsByPrefix(prefix));
}
private Map<String, Ref> buildPrefixRefMap(String prefix, Collection<Ref> refs) {
@@ -125,26 +115,18 @@
@Override
public List<Ref> getRefsByPrefix(String prefix) throws IOException {
- Map<String, Ref> coarseRefs;
- int lastSlash = prefix.lastIndexOf('/');
- if (lastSlash == -1) {
- coarseRefs = getRefs(ALL);
- } else {
- coarseRefs = getRefs(prefix.substring(0, lastSlash + 1));
+ List<Ref> refs = getDelegate().getRefDatabase().getRefsByPrefix(prefix);
+ if (refs.isEmpty()) {
+ return Collections.emptyList();
}
- List<Ref> result;
- if (lastSlash + 1 == prefix.length()) {
- result = coarseRefs.values().stream().collect(toList());
- } else {
- String p = prefix.substring(lastSlash + 1);
- result =
- coarseRefs.entrySet().stream()
- .filter(e -> e.getKey().startsWith(p))
- .map(e -> e.getValue())
- .collect(toList());
+ Collection<Ref> result;
+ try {
+ result = forProject.filter(refs, getDelegate(), RefFilterOptions.defaults());
+ } catch (PermissionBackendException e) {
+ throw new IOException("", e);
}
- return Collections.unmodifiableList(result);
+ return result.stream().collect(Collectors.toList());
}
@Override
diff --git a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
index 8d92347..6d234ac7e 100644
--- a/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
+++ b/java/com/google/gerrit/server/git/receive/ReceiveCommits.java
@@ -2693,7 +2693,7 @@
try (TraceTimer traceTimer = newTimer("readChangesForReplace")) {
replaceByChange.values().stream()
.map(r -> r.ontoChange)
- .map(id -> notesFactory.create(project.getNameKey(), id))
+ .map(id -> notesFactory.create(repo, project.getNameKey(), id))
.forEach(notes -> replaceByChange.get(notes.getChangeId()).notes = notes);
}
}
diff --git a/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java b/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
index 9b5b4d4..e81160a 100644
--- a/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
+++ b/java/com/google/gerrit/server/notedb/AbstractChangeNotes.java
@@ -118,9 +118,10 @@
private ObjectId revision;
private boolean loaded;
- protected AbstractChangeNotes(Args args, Change.Id changeId) {
+ protected AbstractChangeNotes(Args args, Change.Id changeId, @Nullable ObjectId metaSha1) {
this.args = requireNonNull(args);
this.changeId = requireNonNull(changeId);
+ this.revision = metaSha1;
}
public Change.Id getChangeId() {
@@ -133,6 +134,15 @@
}
public T load() {
+ try (Repository repo = args.repoManager.openRepository(getProjectName())) {
+ load(repo);
+ return self();
+ } catch (IOException e) {
+ throw new StorageException(e);
+ }
+ }
+
+ public T load(Repository repo) {
if (loaded) {
return self();
}
@@ -141,10 +151,9 @@
throw new StorageException("Reading from NoteDb is disabled");
}
try (Timer0.Context timer = args.metrics.readLatency.start();
- Repository repo = args.repoManager.openRepository(getProjectName());
// Call openHandle even if reading is disabled, to trigger
// auto-rebuilding before this object may get passed to a ChangeUpdate.
- LoadHandle handle = openHandle(repo)) {
+ LoadHandle handle = openHandle(repo, revision)) {
revision = handle.id();
onLoad(handle);
loaded = true;
@@ -166,15 +175,16 @@
* <p>Implementations may override this method to provide auto-rebuilding behavior.
*
* @param repo open repository.
+ * @param id version SHA1 of the change notes to load
* @return handle for reading the entity.
* @throws NoSuchChangeException change does not exist.
* @throws IOException a repo-level error occurred.
*/
- protected LoadHandle openHandle(Repository repo) throws NoSuchChangeException, IOException {
- return openHandle(repo, readRef(repo));
- }
-
- protected LoadHandle openHandle(Repository repo, ObjectId id) {
+ protected LoadHandle openHandle(Repository repo, @Nullable ObjectId id)
+ throws NoSuchChangeException, IOException {
+ if (id == null) {
+ id = readRef(repo);
+ }
return new LoadHandle(repo, id);
}
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotes.java b/java/com/google/gerrit/server/notedb/ChangeNotes.java
index c35d815..047aa0c 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotes.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotes.java
@@ -66,12 +66,14 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Stream;
import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
@@ -110,9 +112,23 @@
return createChecked(c.getProject(), c.getId());
}
- public ChangeNotes createChecked(Project.NameKey project, Change.Id changeId) {
+ public ChangeNotes createChecked(
+ Repository repo,
+ Project.NameKey project,
+ Change.Id changeId,
+ @Nullable ObjectId metaRevId) {
Change change = newChange(project, changeId);
- return new ChangeNotes(args, change, true, null).load();
+ return new ChangeNotes(args, change, true, null, metaRevId).load(repo);
+ }
+
+ public ChangeNotes createChecked(
+ Project.NameKey project, Change.Id changeId, @Nullable ObjectId metaRevId) {
+ Change change = newChange(project, changeId);
+ return new ChangeNotes(args, change, true, null, metaRevId).load();
+ }
+
+ public ChangeNotes createChecked(Project.NameKey project, Change.Id changeId) {
+ return createChecked(project, changeId, null);
}
public static Change newChange(Project.NameKey project, Change.Id changeId) {
@@ -125,6 +141,11 @@
return new ChangeNotes(args, newChange(project, changeId), true, null).load();
}
+ public ChangeNotes create(Repository repository, Project.NameKey project, Change.Id changeId) {
+ checkArgument(project != null, "project is required");
+ return new ChangeNotes(args, newChange(project, changeId), true, null).load(repository);
+ }
+
/**
* Create change notes for a change that was loaded from index. This method should only be used
* when database access is harmful and potentially stale data from the index is acceptable.
@@ -179,13 +200,14 @@
}
public List<ChangeNotes> create(
+ Repository repo,
Project.NameKey project,
Collection<Change.Id> changeIds,
Predicate<ChangeNotes> predicate) {
List<ChangeNotes> notes = new ArrayList<>();
for (Change.Id cid : changeIds) {
try {
- ChangeNotes cn = create(project, cid);
+ ChangeNotes cn = create(repo, project, cid);
if (cn.getChange() != null && predicate.test(cn)) {
notes.add(cn);
}
@@ -198,6 +220,28 @@
return notes;
}
+ /* TODO: This is now unused in the Gerrit code-base, however it is kept in the code
+ /* because it is a public method in a stable branch.
+ * It can be removed in master branch where we have more flexibility to change the API
+ * interface.
+ */
+ public List<ChangeNotes> create(
+ Project.NameKey project,
+ Collection<Change.Id> changeIds,
+ Predicate<ChangeNotes> predicate) {
+ try (Repository repo = args.repoManager.openRepository(project)) {
+ return create(repo, project, changeIds, predicate);
+ } catch (RepositoryNotFoundException e) {
+ // The repository does not exist, hence it does not contain
+ // any change.
+ } catch (IOException e) {
+ logger.atWarning().withCause(e).log(
+ "Unable to open project=%s when trying to retrieve changeId=%s from NoteDb",
+ project, changeIds);
+ }
+ return Collections.emptyList();
+ }
+
public ListMultimap<Project.NameKey, ChangeNotes> create(Predicate<ChangeNotes> predicate)
throws IOException {
ListMultimap<Project.NameKey, ChangeNotes> m =
@@ -339,14 +383,23 @@
private ImmutableListMultimap<PatchSet.Id, PatchSetApproval> approvals;
private ImmutableSet<Comment.Key> commentKeys;
- @VisibleForTesting
- public ChangeNotes(Args args, Change change, boolean shouldExist, @Nullable RefCache refs) {
- super(args, change.getId());
+ public ChangeNotes(
+ Args args,
+ Change change,
+ boolean shouldExist,
+ @Nullable RefCache refs,
+ @Nullable ObjectId metaSha1) {
+ super(args, change.getId(), metaSha1);
this.change = new Change(change);
this.shouldExist = shouldExist;
this.refs = refs;
}
+ @VisibleForTesting
+ public ChangeNotes(Args args, Change change, boolean shouldExist, @Nullable RefCache refs) {
+ this(args, change, shouldExist, refs, null);
+ }
+
public Change getChange() {
return change;
}
diff --git a/java/com/google/gerrit/server/notedb/DraftCommentNotes.java b/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
index 9b403e8..4988406 100644
--- a/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
+++ b/java/com/google/gerrit/server/notedb/DraftCommentNotes.java
@@ -59,7 +59,7 @@
}
DraftCommentNotes(Args args, Change.Id changeId, Account.Id author, @Nullable Ref ref) {
- super(args, changeId);
+ super(args, changeId, null);
this.author = requireNonNull(author);
this.ref = ref;
if (ref != null) {
diff --git a/java/com/google/gerrit/server/notedb/RobotCommentNotes.java b/java/com/google/gerrit/server/notedb/RobotCommentNotes.java
index fe05643..d53b2ca 100644
--- a/java/com/google/gerrit/server/notedb/RobotCommentNotes.java
+++ b/java/com/google/gerrit/server/notedb/RobotCommentNotes.java
@@ -47,7 +47,7 @@
@Inject
RobotCommentNotes(Args args, @Assisted Change change) {
- super(args, change.getId());
+ super(args, change.getId(), null);
this.change = change;
}
diff --git a/java/com/google/gerrit/server/permissions/DefaultRefFilter.java b/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
index eca30b6..03d3b63 100644
--- a/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
+++ b/java/com/google/gerrit/server/permissions/DefaultRefFilter.java
@@ -142,7 +142,7 @@
return ImmutableList.of();
}
if (RefNames.isRefsChanges(refName)) {
- boolean isChangeRefVisisble = canSeeSingleChangeRef(refName);
+ boolean isChangeRefVisisble = canSeeSingleChangeRef(repo, refName);
if (isChangeRefVisisble) {
logger.atFinest().log("Change ref %s is visible", refName);
return refs;
@@ -466,7 +466,8 @@
* with refs-in-wants is used as that enables Gerrit to skip traditional advertisement of all
* visible refs.
*/
- private boolean canSeeSingleChangeRef(String refName) throws PermissionBackendException {
+ private boolean canSeeSingleChangeRef(Repository repo, String refName)
+ throws PermissionBackendException {
// We are treating just a single change ref. We are therefore not going through regular ref
// filtering, but use NoteDb directly. This makes it so that we can always serve this ref
// even if the change is not part of the set of most recent changes that
@@ -480,7 +481,7 @@
}
ChangeNotes notes;
try {
- notes = changeNotesFactory.create(projectState.getNameKey(), cId);
+ notes = changeNotesFactory.create(repo, projectState.getNameKey(), cId);
} catch (StorageException e) {
throw new PermissionBackendException("can't construct change notes", e);
}
diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index 2021b2b..04e6d49 100644
--- a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -1417,6 +1417,12 @@
private Predicate<ChangeData> getAuthorOrCommitterFullTextPredicate(
String who, Function<String, Predicate<ChangeData>> fullPredicateFunc)
throws QueryParseException {
+ if (isSelf(who)) {
+ IdentifiedUser me = args.getIdentifiedUser();
+ List<Predicate<ChangeData>> predicates =
+ me.getEmailAddresses().stream().map(fullPredicateFunc).collect(toList());
+ return Predicate.or(predicates);
+ }
Set<String> parts = SchemaUtil.getNameParts(who);
if (parts.isEmpty()) {
throw error("invalid value");
diff --git a/java/com/google/gerrit/server/query/change/InternalChangeQuery.java b/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
index 6605c23..1012f4a 100644
--- a/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
+++ b/java/com/google/gerrit/server/query/change/InternalChangeQuery.java
@@ -193,6 +193,7 @@
List<ChangeNotes> notes =
notesFactory.create(
+ repo,
branch.project(),
changeIds,
cn -> {
diff --git a/java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java b/java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java
index 67b5870..517fbdf 100644
--- a/java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java
+++ b/java/com/google/gerrit/server/restapi/change/ChangeIncludedIn.java
@@ -22,6 +22,7 @@
import com.google.gerrit.server.PatchSetUtil;
import com.google.gerrit.server.change.ChangeResource;
import com.google.gerrit.server.change.IncludedIn;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.io.IOException;
@@ -38,7 +39,8 @@
}
@Override
- public Response<IncludedInInfo> apply(ChangeResource rsrc) throws RestApiException, IOException {
+ public Response<IncludedInInfo> apply(ChangeResource rsrc)
+ throws RestApiException, IOException, PermissionBackendException {
PatchSet ps = psUtil.current(rsrc.getNotes());
return Response.ok(includedIn.apply(rsrc.getProject(), ps.commitId().name()));
}
diff --git a/java/com/google/gerrit/server/restapi/change/Move.java b/java/com/google/gerrit/server/restapi/change/Move.java
index 577174f..ecfb96d 100644
--- a/java/com/google/gerrit/server/restapi/change/Move.java
+++ b/java/com/google/gerrit/server/restapi/change/Move.java
@@ -191,9 +191,6 @@
RevWalk revWalk = new RevWalk(repo)) {
RevCommit currPatchsetRevCommit =
revWalk.parseCommit(psUtil.current(ctx.getNotes()).commitId());
- if (currPatchsetRevCommit.getParentCount() > 1) {
- throw new ResourceConflictException("Merge commit cannot be moved");
- }
ObjectId refId = repo.resolve(input.destinationBranch);
// Check if destination ref exists in project repo
diff --git a/java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java b/java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java
index a4a82ce..e566858 100644
--- a/java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java
+++ b/java/com/google/gerrit/server/restapi/project/CommitIncludedIn.java
@@ -20,6 +20,7 @@
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.server.change.IncludedIn;
+import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.gerrit.server.project.CommitResource;
import com.google.inject.Inject;
import com.google.inject.Singleton;
@@ -36,7 +37,8 @@
}
@Override
- public Response<IncludedInInfo> apply(CommitResource rsrc) throws RestApiException, IOException {
+ public Response<IncludedInInfo> apply(CommitResource rsrc)
+ throws RestApiException, IOException, PermissionBackendException {
RevCommit commit = rsrc.getCommit();
Project.NameKey project = rsrc.getProjectState().getNameKey();
return Response.ok(includedIn.apply(project, commit.getId().getName()));
diff --git a/java/com/google/gerrit/truth/NullAwareCorrespondence.java b/java/com/google/gerrit/truth/NullAwareCorrespondence.java
index 687ad94..5b107a6 100644
--- a/java/com/google/gerrit/truth/NullAwareCorrespondence.java
+++ b/java/com/google/gerrit/truth/NullAwareCorrespondence.java
@@ -7,15 +7,6 @@
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
-// Copyright (C) 2020 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
diff --git a/javatests/com/google/gerrit/acceptance/api/project/CommitIT.java b/javatests/com/google/gerrit/acceptance/api/project/CommitIT.java
index 80e04c0..2356327 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/CommitIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/CommitIT.java
@@ -15,7 +15,10 @@
package com.google.gerrit.acceptance.api.project;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.GitUtil.pushHead;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
+import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
import static java.util.stream.Collectors.toList;
import static org.eclipse.jgit.lib.Constants.R_TAGS;
@@ -26,6 +29,7 @@
import com.google.gerrit.acceptance.testsuite.project.ProjectOperations;
import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.entities.Permission;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.extensions.api.changes.CherryPickInput;
import com.google.gerrit.extensions.api.changes.IncludedInInfo;
import com.google.gerrit.extensions.api.changes.ReviewInput;
@@ -96,6 +100,53 @@
}
@Test
+ public void includedInMergedChange_filtersOutNonVisibleBranches() throws Exception {
+ Result baseChange = createAndSubmitChange("refs/for/master");
+
+ createBranch(BranchNameKey.create(project, "test-branch-1"));
+ createBranch(BranchNameKey.create(project, "test-branch-2"));
+ createAndSubmitChange("refs/for/test-branch-1");
+ createAndSubmitChange("refs/for/test-branch-2");
+
+ assertThat(getIncludedIn(baseChange.getCommit().getId()).branches)
+ .containsExactly("master", "test-branch-1", "test-branch-2");
+
+ projectOperations
+ .project(project)
+ .forUpdate()
+ .add(block(Permission.READ).ref("refs/heads/test-branch-1").group(REGISTERED_USERS))
+ .update();
+
+ assertThat(getIncludedIn(baseChange.getCommit().getId()).branches)
+ .containsExactly("master", "test-branch-2");
+ }
+
+ @Test
+ public void includedInMergedChange_filtersOutNonVisibleTags() throws Exception {
+ String tagBase = "tag_base";
+ String tagBranch1 = "tag_1";
+
+ Result baseChange = createAndSubmitChange("refs/for/master");
+ createLightWeightTag(tagBase);
+ assertThat(getIncludedIn(baseChange.getCommit().getId()).tags).containsExactly(tagBase);
+
+ createBranch(BranchNameKey.create(project, "test-branch-1"));
+ createAndSubmitChange("refs/for/test-branch-1");
+ createLightWeightTag(tagBranch1);
+ assertThat(getIncludedIn(baseChange.getCommit().getId()).tags)
+ .containsExactly(tagBase, tagBranch1);
+
+ projectOperations
+ .project(project)
+ .forUpdate()
+ // Tag permissions are controlled by read permissions on branches. Blocking read permission
+ // on test-branch-1 so that tagBranch1 becomes non-visible
+ .add(block(Permission.READ).ref("refs/heads/test-branch-1").group(REGISTERED_USERS))
+ .update();
+ assertThat(getIncludedIn(baseChange.getCommit().getId()).tags).containsExactly(tagBase);
+ }
+
+ @Test
public void cherryPickWithoutMessage() throws Exception {
String branch = "foo";
@@ -192,4 +243,15 @@
assertThat(actual.email).isEqualTo(expected.email());
assertThat(actual.name).isEqualTo(expected.fullName());
}
+
+ private Result createAndSubmitChange(String branch) throws Exception {
+ Result r = createChange(branch);
+ approve(r.getChangeId());
+ gApi.changes().id(r.getChangeId()).current().submit();
+ return r;
+ }
+
+ private void createLightWeightTag(String tagName) throws Exception {
+ pushHead(testRepo, RefNames.REFS_TAGS + tagName, false, false);
+ }
}
diff --git a/javatests/com/google/gerrit/acceptance/rest/DynamicOptionsBeanParseListenerIT.java b/javatests/com/google/gerrit/acceptance/rest/DynamicOptionsBeanParseListenerIT.java
new file mode 100644
index 0000000..39f1e8d
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/DynamicOptionsBeanParseListenerIT.java
@@ -0,0 +1,67 @@
+// Copyright (C) 2021 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;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.RestResponse;
+import com.google.gerrit.extensions.annotations.Exports;
+import com.google.gerrit.json.OutputFormat;
+import com.google.gerrit.server.DynamicOptions;
+import com.google.gerrit.server.restapi.project.ListProjects;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import com.google.inject.AbstractModule;
+import java.util.Map;
+import org.junit.Test;
+
+public class DynamicOptionsBeanParseListenerIT extends AbstractDaemonTest {
+ private static final Gson GSON = OutputFormat.JSON.newGson();
+
+ @Test
+ public void testBeanParseListener() throws Exception {
+ createProjectOverAPI("project1", project, true, null);
+ createProjectOverAPI("project2", project, true, null);
+ try (AutoCloseable ignored = installPlugin("my-plugin", PluginModule.class)) {
+ assertThat(getProjects(adminRestSession.get("/projects/"))).hasSize(1);
+ }
+ }
+
+ protected Map<String, Object> getProjects(RestResponse res) throws Exception {
+ res.assertOK();
+ return GSON.fromJson(res.getReader(), new TypeToken<Map<String, Object>>() {}.getType());
+ }
+
+ protected static class ListProjectsBeanListener implements DynamicOptions.BeanParseListener {
+ @Override
+ public void onBeanParseStart(String plugin, Object bean) {
+ ListProjects listProjects = (ListProjects) bean;
+ listProjects.setLimit(1);
+ }
+
+ @Override
+ public void onBeanParseEnd(String plugin, Object bean) {}
+ }
+
+ protected static class PluginModule extends AbstractModule {
+ @Override
+ public void configure() {
+ bind(DynamicOptions.DynamicBean.class)
+ .annotatedWith(Exports.named(ListProjects.class))
+ .to(ListProjectsBeanListener.class);
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/MoveChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/MoveChangeIT.java
index d5881ea..987d646 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/MoveChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/MoveChangeIT.java
@@ -169,11 +169,9 @@
BranchNameKey newBranch =
BranchNameKey.create(r1.getChange().change().getProject(), "moveTest");
createBranch(newBranch);
- ResourceConflictException thrown =
- assertThrows(
- ResourceConflictException.class,
- () -> move(GitUtil.getChangeId(testRepo, c).get(), newBranch.branch()));
- assertThat(thrown).hasMessageThat().contains("Merge commit cannot be moved");
+ String changeId = GitUtil.getChangeId(testRepo, c).get();
+ move(changeId, newBranch.branch());
+ assertThat(gApi.changes().id(changeId).get().branch).isEqualTo("moveTest");
}
@Test
diff --git a/javatests/com/google/gerrit/acceptance/ssh/DynamicOptionsBeanParseListenerIT.java b/javatests/com/google/gerrit/acceptance/ssh/DynamicOptionsBeanParseListenerIT.java
new file mode 100644
index 0000000..0afdbc6
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/ssh/DynamicOptionsBeanParseListenerIT.java
@@ -0,0 +1,69 @@
+// Copyright (C) 2021 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.ssh;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.NoHttpd;
+import com.google.gerrit.acceptance.UseSsh;
+import com.google.gerrit.extensions.annotations.Exports;
+import com.google.gerrit.server.DynamicOptions;
+import com.google.gerrit.sshd.commands.ListProjectsCommand;
+import com.google.inject.AbstractModule;
+import java.util.Arrays;
+import java.util.List;
+import org.junit.Test;
+
+@NoHttpd
+@UseSsh
+public class DynamicOptionsBeanParseListenerIT extends AbstractDaemonTest {
+
+ @Test
+ public void testBeanParseListener() throws Exception {
+ createProjectOverAPI("project1", project, true, null);
+ createProjectOverAPI("project2", project, true, null);
+ try (AutoCloseable ignored = installPlugin("my-plugin", PluginModule.class)) {
+ String output = adminSshSession.exec("gerrit ls-projects");
+ adminSshSession.assertSuccess();
+ assertThat(getProjects(output)).hasSize(1);
+ }
+ }
+
+ protected List<String> getProjects(String sshOutput) {
+ return Arrays.asList(sshOutput.split("\n"));
+ }
+
+ protected static class ListProjectsCommandBeanListener
+ implements DynamicOptions.BeanParseListener {
+ @Override
+ public void onBeanParseStart(String plugin, Object bean) {
+ ListProjectsCommand command = (ListProjectsCommand) bean;
+ command.impl.setLimit(1);
+ }
+
+ @Override
+ public void onBeanParseEnd(String plugin, Object bean) {}
+ }
+
+ protected static class PluginModule extends AbstractModule {
+ @Override
+ public void configure() {
+ bind(DynamicOptions.DynamicBean.class)
+ .annotatedWith(Exports.named(ListProjectsCommand.class))
+ .to(ListProjectsCommandBeanListener.class);
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java b/javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java
index 00d01d6..7543ba8 100644
--- a/javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java
+++ b/javatests/com/google/gerrit/acceptance/testsuite/project/ProjectOperationsImplTest.java
@@ -595,6 +595,15 @@
}
@Test
+ public void removeAllAccessSections() {
+ projectOperations.allProjectsForUpdate().removeAllAccessSections().update();
+
+ assertThat(projectOperations.project(allProjects).getConfig())
+ .sectionValues("access")
+ .isEmpty();
+ }
+
+ @Test
public void updatingCapabilitiesNotAllowedForNonAllProjects() throws Exception {
Project.NameKey key = projectOperations.newProject().create();
assertThrows(
diff --git a/javatests/com/google/gerrit/integration/git/GitProtocolV2IT.java b/javatests/com/google/gerrit/integration/git/GitProtocolV2IT.java
index 76ce956..3068003 100644
--- a/javatests/com/google/gerrit/integration/git/GitProtocolV2IT.java
+++ b/javatests/com/google/gerrit/integration/git/GitProtocolV2IT.java
@@ -98,10 +98,7 @@
.group(SystemGroupBackend.REGISTERED_USERS))
.update();
- // Set protocol.version=2 in target repository
- execute(
- ImmutableList.of("git", "config", "protocol.version", "2"),
- sitePaths.site_path.resolve("git").resolve(project.get() + Constants.DOT_GIT).toFile());
+ setProtocolV2(project);
// Retrieve HTTP url
String url = config.getString("gerrit", null, "canonicalweburl");
@@ -217,14 +214,7 @@
Project.NameKey allRefsVisibleProject = Project.nameKey("all-refs-visible");
gApi.projects().create(allRefsVisibleProject.get());
- // Set protocol.version=2 in target repository
- execute(
- ImmutableList.of("git", "config", "protocol.version", "2"),
- sitePaths
- .site_path
- .resolve("git")
- .resolve(allRefsVisibleProject.get() + Constants.DOT_GIT)
- .toFile());
+ setProtocolV2(allRefsVisibleProject);
// Set up project permission to allow reading all refs
projectOperations
@@ -280,14 +270,7 @@
Project.NameKey privateProject = Project.nameKey("private-project");
gApi.projects().create(privateProject.get());
- // Set protocol.version=2 in target repository
- execute(
- ImmutableList.of("git", "config", "protocol.version", "2"),
- sitePaths
- .site_path
- .resolve("git")
- .resolve(privateProject.get() + Constants.DOT_GIT)
- .toFile());
+ setProtocolV2(privateProject);
// Disallow general read permissions for anonymous users
projectOperations
@@ -356,6 +339,12 @@
UTF_8));
}
+ private void setProtocolV2(Project.NameKey projectName) throws Exception {
+ execute(
+ ImmutableList.of("git", "config", "protocol.version", "2"),
+ sitePaths.site_path.resolve("git").resolve(projectName.get() + Constants.DOT_GIT).toFile());
+ }
+
private static void assertGitProtocolV2Refs(String commit, String out) {
assertThat(out).contains("git< version 2");
assertThat(out).contains("refs/changes/01/1/1");
diff --git a/javatests/com/google/gerrit/server/change/IncludedInResolverTest.java b/javatests/com/google/gerrit/server/change/IncludedInResolverTest.java
index 19c479d..b69a894 100644
--- a/javatests/com/google/gerrit/server/change/IncludedInResolverTest.java
+++ b/javatests/com/google/gerrit/server/change/IncludedInResolverTest.java
@@ -17,9 +17,13 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.entities.RefNames.REFS_TAGS;
+import com.google.common.truth.Correspondence;
+import com.google.gerrit.truth.NullAwareCorrespondence;
import org.eclipse.jgit.internal.storage.dfs.DfsRepositoryDescription;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTag;
@@ -112,8 +116,12 @@
IncludedInResolver.Result detail = resolve(commit_v2_5);
// Check that only tags and branches which refer the tip are returned
- assertThat(detail.tags()).containsExactly(TAG_2_5, TAG_2_5_ANNOTATED, TAG_2_5_ANNOTATED_TWICE);
- assertThat(detail.branches()).containsExactly(BRANCH_2_5);
+ assertThat(detail.tags())
+ .comparingElementsUsing(hasShortName())
+ .containsExactly(TAG_2_5, TAG_2_5_ANNOTATED, TAG_2_5_ANNOTATED_TWICE);
+ assertThat(detail.branches())
+ .comparingElementsUsing(hasShortName())
+ .containsExactly(BRANCH_2_5);
}
@Test
@@ -123,6 +131,7 @@
// Check whether all tags and branches are returned
assertThat(detail.tags())
+ .comparingElementsUsing(hasShortName())
.containsExactly(
TAG_1_0,
TAG_1_0_1,
@@ -133,6 +142,7 @@
TAG_2_5_ANNOTATED,
TAG_2_5_ANNOTATED_TWICE);
assertThat(detail.branches())
+ .comparingElementsUsing(hasShortName())
.containsExactly(BRANCH_MASTER, BRANCH_1_0, BRANCH_1_3, BRANCH_2_0, BRANCH_2_5);
}
@@ -143,8 +153,11 @@
// Check whether all succeeding tags and branches are returned
assertThat(detail.tags())
+ .comparingElementsUsing(hasShortName())
.containsExactly(TAG_1_3, TAG_2_5, TAG_2_5_ANNOTATED, TAG_2_5_ANNOTATED_TWICE);
- assertThat(detail.branches()).containsExactly(BRANCH_1_3, BRANCH_2_5);
+ assertThat(detail.branches())
+ .comparingElementsUsing(hasShortName())
+ .containsExactly(BRANCH_1_3, BRANCH_2_5);
}
private IncludedInResolver.Result resolve(RevCommit commit) throws Exception {
@@ -154,4 +167,9 @@
private RevTag tag(String name, RevObject dest) throws Exception {
return tr.update(REFS_TAGS + name, tr.tag(name, dest));
}
+
+ private static Correspondence<Ref, String> hasShortName() {
+ return NullAwareCorrespondence.transforming(
+ ref -> Repository.shortenRefName(ref.getName()), "has short name");
+ }
}
diff --git a/javatests/com/google/gerrit/server/events/EventDeserializerTest.java b/javatests/com/google/gerrit/server/events/EventDeserializerTest.java
index e0223e4..97f6e4e 100644
--- a/javatests/com/google/gerrit/server/events/EventDeserializerTest.java
+++ b/javatests/com/google/gerrit/server/events/EventDeserializerTest.java
@@ -22,6 +22,8 @@
import com.google.gerrit.entities.BranchNameKey;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.Project;
+import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.data.AccountAttribute;
import com.google.gerrit.server.data.ChangeAttribute;
import com.google.gerrit.server.data.RefUpdateAttribute;
@@ -240,6 +242,31 @@
assertSameChangeEvent(e, orig);
}
+ @Test
+ public void shouldSerializeAllProjectsToString() {
+ String allProjectsString = "foobar";
+ AllProjectsName allProjectsNameKey = new AllProjectsName(allProjectsString);
+
+ assertThat(gson.toJson(allProjectsNameKey))
+ .isEqualTo(String.format("\"%s\"", allProjectsString));
+ }
+
+ @Test
+ public void shouldSerializeAllUsersToString() {
+ String allUsersString = "foobar";
+ AllUsersName allUsersNameKey = new AllUsersName(allUsersString);
+
+ assertThat(gson.toJson(allUsersNameKey)).isEqualTo(String.format("\"%s\"", allUsersString));
+ }
+
+ @Test
+ public void shouldSerializeProjectNameKeyToString() {
+ String projectString = "foobar";
+ Project.NameKey projectNameKey = Project.nameKey(projectString);
+
+ assertThat(gson.toJson(projectNameKey)).isEqualTo(String.format("\"%s\"", projectString));
+ }
+
private <T> Supplier<T> createSupplier(T value) {
return Suppliers.memoize(() -> value);
}
diff --git a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index 02f514a..297977c 100644
--- a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -617,9 +617,14 @@
PersonIdent johnDoe = new PersonIdent("John Doe", "john.doe@example.com");
PersonIdent john = new PersonIdent("John", "john@example.com");
PersonIdent doeSmith = new PersonIdent("Doe Smith", "doe_smith@example.com");
+ Account ua = user.asIdentifiedUser().getAccount();
+ PersonIdent myself = new PersonIdent("I Am", ua.preferredEmail());
+ PersonIdent selfName = new PersonIdent("My Self", "my.self@example.com");
+
Change change1 = createChange(repo, johnDoe);
Change change2 = createChange(repo, john);
Change change3 = createChange(repo, doeSmith);
+ Change change4 = createChange(repo, selfName);
// Only email address.
assertQuery(searchOperator + "john.doe@example.com", change1);
@@ -635,6 +640,18 @@
assertQuery(searchOperator + "\"John <john.doe@example.com>\"");
assertQuery(searchOperator + "\"Doe John <john@example.com>\"");
assertQuery(searchOperator + "\"Doe John <doe_smith@example.com>\"");
+
+ // Partial name
+ assertQuery(searchOperator + "ohn");
+ assertQuery(searchOperator + "smith", change3);
+
+ // The string 'self' in the name should not be matched
+ assertQuery(searchOperator + "self");
+
+ // ':self' matches a change created with the current user's email address
+ Change change5 = createChange(repo, myself);
+ assertQuery(searchOperator + "me", change5);
+ assertQuery(searchOperator + "self", change5);
}
private void byAuthorOrCommitterFullText(String searchOperator) throws Exception {
diff --git a/modules/jgit b/modules/jgit
index 24d6d60..8470771 160000
--- a/modules/jgit
+++ b/modules/jgit
@@ -1 +1 @@
-Subproject commit 24d6d605388c82201092cf1699b51095299380a2
+Subproject commit 84707715108a65a366ef35f2ae04aabecd0b35f6
diff --git a/plugins/codemirror-editor b/plugins/codemirror-editor
index 7357ab4..c621796 160000
--- a/plugins/codemirror-editor
+++ b/plugins/codemirror-editor
@@ -1 +1 @@
-Subproject commit 7357ab473599d16ae33cc982bbd65472f08c2dd6
+Subproject commit c6217963e42322accc3f0bacb6540f8791f67ab0
diff --git a/plugins/gitiles b/plugins/gitiles
index 584360d..25580c3 160000
--- a/plugins/gitiles
+++ b/plugins/gitiles
@@ -1 +1 @@
-Subproject commit 584360d050f5adf8317ebc078a874da9c4316bd0
+Subproject commit 25580c3e60a58265b74ac5022b1dbde0e9ef008e
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.ts b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.ts
index e9a6158..880b397 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.ts
+++ b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config.ts
@@ -117,8 +117,8 @@
);
}
- _computeDisabled(editable: string) {
- return editable === 'false';
+ _computeDisabled(editable: boolean) {
+ return !editable;
}
_computeChecked(value = 'false') {
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_test.js b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_test.js
index 168984a..28ef2f8 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_test.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-plugin-config/gr-repo-plugin-config_test.js
@@ -39,8 +39,10 @@
});
test('_computeDisabled', () => {
- assert.isFalse(element._computeDisabled('true'));
- assert.isTrue(element._computeDisabled('false'));
+ assert.isFalse(element._computeDisabled(true));
+ assert.isTrue(element._computeDisabled(undefined));
+ assert.isTrue(element._computeDisabled(null));
+ assert.isTrue(element._computeDisabled(false));
});
test('_handleChange', () => {
@@ -75,7 +77,7 @@
test('ARRAY type option', () => {
element.pluginData = {
name: 'testName',
- config: {plugin: {value: 'test', type: 'ARRAY'}},
+ config: {plugin: {value: 'test', type: 'ARRAY', editable: true}},
};
flush();
@@ -90,7 +92,7 @@
test('BOOLEAN type option', () => {
element.pluginData = {
name: 'testName',
- config: {plugin: {value: 'true', type: 'BOOLEAN'}},
+ config: {plugin: {value: 'true', type: 'BOOLEAN', editable: true}},
};
flush();
@@ -109,7 +111,7 @@
test('INT/LONG/STRING type option', () => {
element.pluginData = {
name: 'testName',
- config: {plugin: {value: 'test', type: 'STRING'}},
+ config: {plugin: {value: 'test', type: 'STRING', editable: true}},
};
flush();
@@ -130,7 +132,9 @@
const permitted_values = ['test', 'newTest'];
element.pluginData = {
name: 'testName',
- config: {plugin: {value: 'test', type: 'LIST', permitted_values}},
+ config: {plugin:
+ {value: 'test', type: 'LIST', editable: true, permitted_values},
+ },
};
flush();
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.ts b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.ts
index d34dcc9..abbe316 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.ts
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.ts
@@ -314,6 +314,8 @@
// Fetch projects.
return this.projectSuggestions(predicate, expression);
+ case 'assignee':
+ case 'attention':
case 'author':
case 'cc':
case 'commentby':
diff --git a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.ts b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.ts
index be23cb3..5852135 100644
--- a/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.ts
+++ b/polygerrit-ui/app/elements/shared/gr-account-label/gr-account-label.ts
@@ -151,9 +151,11 @@
change: ChangeInfo,
force: boolean
) {
+ const hasAvatars = !!config?.plugin?.has_avatars;
return (
!hideAvatar &&
- !this._hasAttention(config, highlight, account, change, force)
+ !this._hasAttention(config, highlight, account, change, force) &&
+ hasAvatars
);
}
diff --git a/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_test.js b/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_test.js
index b5f068c..65a442b 100644
--- a/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_test.js
+++ b/polygerrit-ui/app/elements/shared/gr-tooltip/gr-tooltip_test.js
@@ -26,8 +26,9 @@
suite('gr-tooltip tests', () => {
let element;
- setup(() => {
+ setup(async () => {
element = basicFixture.instantiate();
+ await flush();
});
test('max-width is respected if set', () => {
diff --git a/tools/bzl/js.bzl b/tools/bzl/js.bzl
index 5214cec..dcc4739 100644
--- a/tools/bzl/js.bzl
+++ b/tools/bzl/js.bzl
@@ -28,7 +28,7 @@
else:
fail("repository %s not in {%s,%s}" % (repository, GERRIT, NPMJS))
- python = ctx.which("python")
+ python = ctx.which("python3")
script = ctx.path(ctx.attr._download_script)
args = [python, script, "-o", dest, "-u", url, "-v", sha1]
@@ -51,7 +51,7 @@
# for use in repo rules.
def _run_npm_binary_str(ctx, tarball, args):
- python_bin = ctx.which("python")
+ python_bin = ctx.which("python3")
return " ".join([
str(python_bin),
str(ctx.path(ctx.attr._run_npm)),
@@ -65,7 +65,7 @@
version_name = "%s__version.json" % ctx.name
cmd = [
- ctx.which("python"),
+ ctx.which("python3"),
ctx.path(ctx.attr._download_bower),
"-b",
"%s" % _run_npm_binary_str(ctx, ctx.attr._bower_archive, []),
@@ -320,7 +320,7 @@
app_path = app_path[app_path.index(pkg_dir) + len(pkg_dir):]
hermetic_npm_binary = " ".join([
- "python",
+ "python3",
"$p/" + ctx.file._run_npm.path,
"$p/" + ctx.file._bundler_archive.path,
"--inline-scripts",
@@ -371,7 +371,7 @@
if ctx.attr.split:
hermetic_npm_command = "export PATH && " + " ".join([
- "python",
+ "python3",
ctx.file._run_npm.path,
ctx.file._crisper_archive.path,
"--script-in-head=false",
diff --git a/tools/bzl/license-map.py b/tools/bzl/license-map.py
index 221ae2f..3e4fc92 100644
--- a/tools/bzl/license-map.py
+++ b/tools/bzl/license-map.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# reads bazel query XML files, to join target names with their licenses.
@@ -24,7 +24,7 @@
def read_file(filename):
"Reads file and returns its content"
- with open(filename) as fd:
+ with open(filename, encoding='utf-8') as fd:
return fd.read()
# List of files in package to which license is applied.
@@ -56,6 +56,8 @@
LicenseMapItem = namedtuple("LicenseMapItem",
["name", "safename", "packages", "license_text"])
+def print_utf8(str=""):
+ stdout.buffer.write((str + "\n").encode('utf-8'))
def load_xmls(xml_filenames):
"""Load xml files produced by bazel query
@@ -134,7 +136,7 @@
if args.asciidoctor:
# We don't want any blank line before "= Gerrit Code Review - Licenses"
- print("""= Gerrit Code Review - Licenses
+ print_utf8("""= Gerrit Code Review - Licenses
// DO NOT EDIT - GENERATED AUTOMATICALLY.
@@ -178,10 +180,10 @@
for data in xml_data + json_map_data:
name = data.name
safename = data.safename
- print()
- print("[[%s]]" % safename)
- print(name)
- print()
+ print_utf8()
+ print_utf8("[[%s]]" % safename)
+ print_utf8(name)
+ print_utf8()
for p in data.packages:
package_notice = ""
if p.licensed_files.kind == "OnlySpecificFiles":
@@ -189,20 +191,20 @@
elif p.licensed_files.kind == "AllFilesExceptSpecific":
package_notice = " - except the following file(s):"
- print("* " + get_package_display_name(p) + package_notice)
+ print_utf8("* " + get_package_display_name(p) + package_notice)
for file in p.licensed_files.files:
- print("** " + file)
- print()
- print("[[%s_license]]" % safename)
- print("----")
+ print_utf8("** " + file)
+ print_utf8()
+ print_utf8("[[%s_license]]" % safename)
+ print_utf8("----")
license_text = data.license_text
- print(data.license_text.rstrip("\r\n"))
- print()
- print("----")
- print()
+ print_utf8(data.license_text.rstrip("\r\n"))
+ print_utf8()
+ print_utf8("----")
+ print_utf8()
if args.asciidoctor:
- print("""
+ print_utf8("""
GERRIT
------
Part of link:index.html[Gerrit Code Review]
@@ -219,7 +221,7 @@
"""
result = []
for json_map in json_filenames:
- with open(json_map, 'r') as f:
+ with open(json_map, 'r', encoding='utf-8') as f:
licenses_list = json.load(f)
for license_id, license in licenses_list.items():
name = license["licenseName"]
diff --git a/tools/bzl/license.bzl b/tools/bzl/license.bzl
index cdb13d0..7b1375a 100644
--- a/tools/bzl/license.bzl
+++ b/tools/bzl/license.bzl
@@ -50,7 +50,7 @@
# post process the XML into our favorite format.
native.genrule(
name = "gen_license_txt_" + name,
- cmd = "python $(location //tools/bzl:license-map.py) %s %s %s > $@" % (" ".join(opts), " ".join(json_maps_locations), " ".join(xmls)),
+ cmd = "python3 $(location //tools/bzl:license-map.py) %s %s %s > $@" % (" ".join(opts), " ".join(json_maps_locations), " ".join(xmls)),
outs = [name + ".gen.txt"],
tools = tools,
**kwargs
diff --git a/tools/bzl/maven_jar.bzl b/tools/bzl/maven_jar.bzl
index d96ffc2..adea89e 100644
--- a/tools/bzl/maven_jar.bzl
+++ b/tools/bzl/maven_jar.bzl
@@ -141,7 +141,7 @@
binjar_path = ctx.path("/".join(["jar", binjar]))
binurl = url + ".jar"
- python = ctx.which("python")
+ python = ctx.which("python3")
script = ctx.path(ctx.attr._download_script)
args = [python, script, "-o", binjar_path, "-u", binurl]
diff --git a/tools/download_file.py b/tools/download_file.py
index 936bcef..2af2c07 100755
--- a/tools/download_file.py
+++ b/tools/download_file.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (C) 2013 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/tools/eclipse/project.py b/tools/eclipse/project.py
index acb5346..6cfa858 100755
--- a/tools/eclipse/project.py
+++ b/tools/eclipse/project.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (C) 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/tools/js/download_bower.py b/tools/js/download_bower.py
index d541b56..2a75fc1 100755
--- a/tools/js/download_bower.py
+++ b/tools/js/download_bower.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (C) 2015 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/tools/js/npm_pack.py b/tools/js/npm_pack.py
index 57f3166..33b38a0 100755
--- a/tools/js/npm_pack.py
+++ b/tools/js/npm_pack.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (C) 2015 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/tools/js/run_npm_binary.py b/tools/js/run_npm_binary.py
index bdee5ab..31f8a54 100644
--- a/tools/js/run_npm_binary.py
+++ b/tools/js/run_npm_binary.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (C) 2015 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/tools/maven/gerrit-acceptance-framework_pom.xml b/tools/maven/gerrit-acceptance-framework_pom.xml
index 4e20948..de2336e 100644
--- a/tools/maven/gerrit-acceptance-framework_pom.xml
+++ b/tools/maven/gerrit-acceptance-framework_pom.xml
@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-acceptance-framework</artifactId>
- <version>3.3.6-SNAPSHOT</version>
+ <version>3.3.8-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Gerrit Code Review - Acceptance Test Framework</name>
<description>Framework for Gerrit's acceptance tests</description>
diff --git a/tools/maven/gerrit-extension-api_pom.xml b/tools/maven/gerrit-extension-api_pom.xml
index 15cfc33..4a9bcb1 100644
--- a/tools/maven/gerrit-extension-api_pom.xml
+++ b/tools/maven/gerrit-extension-api_pom.xml
@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-extension-api</artifactId>
- <version>3.3.6-SNAPSHOT</version>
+ <version>3.3.8-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Gerrit Code Review - Extension API</name>
<description>API for Gerrit Extensions</description>
diff --git a/tools/maven/gerrit-plugin-api_pom.xml b/tools/maven/gerrit-plugin-api_pom.xml
index 37857cc..38b4536 100644
--- a/tools/maven/gerrit-plugin-api_pom.xml
+++ b/tools/maven/gerrit-plugin-api_pom.xml
@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-plugin-api</artifactId>
- <version>3.3.6-SNAPSHOT</version>
+ <version>3.3.8-SNAPSHOT</version>
<packaging>jar</packaging>
<name>Gerrit Code Review - Plugin API</name>
<description>API for Gerrit Plugins</description>
diff --git a/tools/maven/gerrit-war_pom.xml b/tools/maven/gerrit-war_pom.xml
index eb3f657..e848b43 100644
--- a/tools/maven/gerrit-war_pom.xml
+++ b/tools/maven/gerrit-war_pom.xml
@@ -2,7 +2,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.google.gerrit</groupId>
<artifactId>gerrit-war</artifactId>
- <version>3.3.6-SNAPSHOT</version>
+ <version>3.3.8-SNAPSHOT</version>
<packaging>war</packaging>
<name>Gerrit Code Review - WAR</name>
<description>Gerrit WAR</description>
diff --git a/tools/maven/mvn.py b/tools/maven/mvn.py
index 60e9f15..4ed5bf9 100755
--- a/tools/maven/mvn.py
+++ b/tools/maven/mvn.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (C) 2013 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/tools/util_test.py b/tools/util_test.py
index 1a389f5..ab1133b2 100644
--- a/tools/util_test.py
+++ b/tools/util_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (C) 2013 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/tools/version.py b/tools/version.py
index 2326757..d02fc26 100755
--- a/tools/version.py
+++ b/tools/version.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Copyright (C) 2014 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/tools/workspace_status.py b/tools/workspace_status.py
index 443c2f0..bedc051 100644
--- a/tools/workspace_status.py
+++ b/tools/workspace_status.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# This script will be run by bazel when the build process starts to
# generate key-value information that represents the status of the
diff --git a/tools/workspace_status_release.py b/tools/workspace_status_release.py
index 36535fb..b3e72ff 100644
--- a/tools/workspace_status_release.py
+++ b/tools/workspace_status_release.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# This is a variant of the `workspace_status.py` script that in addition to
# plain `git describe` implements a few heuristics to arrive at more to the
@@ -9,7 +9,7 @@
#
# To use it, simply add
#
-# --workspace_status_command="python ./tools/workspace_status_release.py"
+# --workspace_status_command="python3 ./tools/workspace_status_release.py"
#
# to your bazel command. So for example instead of
#
@@ -17,11 +17,11 @@
#
# use
#
-# bazel build --workspace_status_command="python ./tools/workspace_status_release.py" release.war
+# bazel build --workspace_status_command="python3 ./tools/workspace_status_release.py" release.war
#
# Alternatively, you can add
#
-# build --workspace_status_command="python ./tools/workspace_status_release.py"
+# build --workspace_status_command="python3 ./tools/workspace_status_release.py"
#
# to `.bazelrc` in your home directory.
#
@@ -150,7 +150,7 @@
'tools', 'workspace_status_release.py')
if os.path.isfile(workspace_status_script):
# directory has own workspace_status_command, so we use stamps from that
- for line in run(["python", workspace_status_script]).split('\n'):
+ for line in run(["python3", workspace_status_script]).split('\n'):
if re.search("^STABLE_[a-zA-Z0-9().:@/_ -]*$", line):
print(line)
else:
diff --git a/version.bzl b/version.bzl
index ef49388..661f6d5 100644
--- a/version.bzl
+++ b/version.bzl
@@ -2,4 +2,4 @@
# Used by :api_install and :api_deploy targets
# when talking to the destination repository.
#
-GERRIT_VERSION = "3.3.6-SNAPSHOT"
+GERRIT_VERSION = "3.3.8-SNAPSHOT"