Merge "Merge branch 'stable-3.3'"
diff --git a/Documentation/rest-api-changes.txt b/Documentation/rest-api-changes.txt
index 096b068..3b9320c 100644
--- a/Documentation/rest-api-changes.txt
+++ b/Documentation/rest-api-changes.txt
@@ -6467,6 +6467,8 @@
Only set if link:#current-revision[the current revision] is requested
(in which case it will only contain a key for the current revision) or
if link:#all-revisions[all revisions] are requested.
+|`meta_rev_id` |optional|
+The SHA1 of the NoteDb meta ref.
|`tracking_ids` |optional|
A list of link:#tracking-id-info[TrackingIdInfo] entities describing
references to external tracking systems. Only set if
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index 677fc6d..49bc7e5 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -4211,8 +4211,10 @@
|`branches` |optional|
A list of branches that should be initially created. +
For the branch names the `refs/heads/` prefix can be omitted. +
-The first entry of the list will be the default branch (ie. the target +
-of the `HEAD` symbolic ref).
+The first entry of the list will be the default branch (ie. the target
+of the `HEAD` symbolic ref). +
+Branches in the Gerrit internal ref space are not allowed, such as
+refs/groups/, refs/changes/, etc...
|`owners` |optional|
A list of groups that should be assigned as project owner. +
Each group in the list must be specified as
diff --git a/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java b/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
index 969ffa5..162654d 100644
--- a/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
+++ b/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
@@ -37,6 +37,7 @@
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.QueryOptions;
+import com.google.gerrit.index.RefState;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.query.DataSource;
import com.google.gerrit.index.query.Predicate;
@@ -354,7 +355,7 @@
// Ref-state.
if (fields.contains(ChangeField.REF_STATE.getName())) {
- cd.setRefStates(getByteArray(source, ChangeField.REF_STATE.getName()));
+ cd.setRefStates(RefState.parseStates(getByteArray(source, ChangeField.REF_STATE.getName())));
}
// Ref-state-pattern.
diff --git a/java/com/google/gerrit/extensions/common/ChangeInfo.java b/java/com/google/gerrit/extensions/common/ChangeInfo.java
index 7ed2f95..528efe3 100644
--- a/java/com/google/gerrit/extensions/common/ChangeInfo.java
+++ b/java/com/google/gerrit/extensions/common/ChangeInfo.java
@@ -70,6 +70,7 @@
public String submissionId;
public Integer cherryPickOfChange;
public Integer cherryPickOfPatchSet;
+ public String metaRevId;
/**
* Whether the change contains conflicts.
diff --git a/java/com/google/gerrit/extensions/common/FileInfo.java b/java/com/google/gerrit/extensions/common/FileInfo.java
index 32c5bd5..510c2ad 100644
--- a/java/com/google/gerrit/extensions/common/FileInfo.java
+++ b/java/com/google/gerrit/extensions/common/FileInfo.java
@@ -34,8 +34,8 @@
&& Objects.equals(oldPath, fileInfo.oldPath)
&& Objects.equals(linesInserted, fileInfo.linesInserted)
&& Objects.equals(linesDeleted, fileInfo.linesDeleted)
- && Objects.equals(sizeDelta, fileInfo.sizeDelta)
- && Objects.equals(size, fileInfo.size);
+ && sizeDelta == fileInfo.sizeDelta
+ && size == fileInfo.size;
}
return false;
}
diff --git a/java/com/google/gerrit/index/RefState.java b/java/com/google/gerrit/index/RefState.java
index 956dcab..ed38de9 100644
--- a/java/com/google/gerrit/index/RefState.java
+++ b/java/com/google/gerrit/index/RefState.java
@@ -20,8 +20,7 @@
import com.google.auto.value.AutoValue;
import com.google.common.base.Splitter;
-import com.google.common.collect.MultimapBuilder;
-import com.google.common.collect.SetMultimap;
+import com.google.common.collect.ImmutableSetMultimap;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.entities.Project;
import com.google.gerrit.git.ObjectIds;
@@ -33,10 +32,10 @@
@AutoValue
public abstract class RefState {
- public static SetMultimap<Project.NameKey, RefState> parseStates(Iterable<byte[]> states) {
+ public static ImmutableSetMultimap<Project.NameKey, RefState> parseStates(
+ Iterable<byte[]> states) {
RefState.check(states != null, null);
- SetMultimap<Project.NameKey, RefState> result =
- MultimapBuilder.hashKeys().hashSetValues().build();
+ ImmutableSetMultimap.Builder<Project.NameKey, RefState> result = ImmutableSetMultimap.builder();
for (byte[] b : states) {
RefState.check(b != null, null);
String s = new String(b, UTF_8);
@@ -44,7 +43,7 @@
RefState.check(parts.size() == 3 && !parts.get(0).isEmpty() && !parts.get(1).isEmpty(), s);
result.put(Project.nameKey(parts.get(0)), RefState.create(parts.get(1), parts.get(2)));
}
- return result;
+ return result.build();
}
public static RefState create(String ref, String sha) {
diff --git a/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/java/com/google/gerrit/lucene/LuceneChangeIndex.java
index f4b9e69..c3d4440 100644
--- a/java/com/google/gerrit/lucene/LuceneChangeIndex.java
+++ b/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -49,6 +49,7 @@
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.QueryOptions;
+import com.google.gerrit.index.RefState;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.query.FieldBundle;
import com.google.gerrit.index.query.Predicate;
@@ -702,7 +703,7 @@
}
private void decodeRefStates(ListMultimap<String, IndexableField> doc, ChangeData cd) {
- cd.setRefStates(copyAsBytes(doc.get(REF_STATE_FIELD)));
+ cd.setRefStates(RefState.parseStates(copyAsBytes(doc.get(REF_STATE_FIELD))));
}
private void decodeRefStatePatterns(ListMultimap<String, IndexableField> doc, ChangeData cd) {
diff --git a/java/com/google/gerrit/server/change/ChangeJson.java b/java/com/google/gerrit/server/change/ChangeJson.java
index f292245..a461b59 100644
--- a/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/java/com/google/gerrit/server/change/ChangeJson.java
@@ -54,6 +54,7 @@
import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.RefNames;
import com.google.gerrit.entities.SubmitRecord;
import com.google.gerrit.entities.SubmitRecord.Status;
import com.google.gerrit.entities.SubmitRequirement;
@@ -75,6 +76,7 @@
import com.google.gerrit.extensions.common.SubmitRequirementInfo;
import com.google.gerrit.extensions.common.TrackingIdInfo;
import com.google.gerrit.extensions.restapi.Url;
+import com.google.gerrit.index.RefState;
import com.google.gerrit.index.query.QueryResult;
import com.google.gerrit.metrics.Description;
import com.google.gerrit.metrics.Description.Units;
@@ -570,6 +572,15 @@
out.totalCommentCount = cd.totalCommentCount();
out.unresolvedCommentCount = cd.unresolvedCommentCount();
+ if (cd.getRefStates() != null) {
+ String metaName = RefNames.changeMetaRef(cd.getId());
+ Optional<RefState> metaState =
+ cd.getRefStates().values().stream().filter(r -> r.ref().equals(metaName)).findAny();
+
+ // metaState should always be there, but it doesn't hurt to be extra careful.
+ metaState.ifPresent(rs -> out.metaRevId = rs.id().getName());
+ }
+
if (user.isIdentifiedUser()) {
Collection<String> stars = cd.stars(user.getAccountId());
out.starred = stars.contains(StarredChangesUtil.DEFAULT_LABEL) ? true : null;
diff --git a/java/com/google/gerrit/server/index/change/ChangeField.java b/java/com/google/gerrit/server/index/change/ChangeField.java
index 5f2525e..59600e0 100644
--- a/java/com/google/gerrit/server/index/change/ChangeField.java
+++ b/java/com/google/gerrit/server/index/change/ChangeField.java
@@ -57,7 +57,6 @@
import com.google.gerrit.entities.converter.PatchSetProtoConverter;
import com.google.gerrit.entities.converter.ProtoConverter;
import com.google.gerrit.index.FieldDef;
-import com.google.gerrit.index.RefState;
import com.google.gerrit.index.SchemaUtil;
import com.google.gerrit.json.OutputFormat;
import com.google.gerrit.proto.Protos;
@@ -66,9 +65,7 @@
import com.google.gerrit.server.StarredChangesUtil;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.index.change.StalenessChecker.RefStatePattern;
-import com.google.gerrit.server.notedb.ChangeNotes;
import com.google.gerrit.server.notedb.ReviewerStateInternal;
-import com.google.gerrit.server.notedb.RobotCommentNotes;
import com.google.gerrit.server.project.SubmitRuleOptions;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
@@ -982,27 +979,9 @@
.buildRepeatable(
cd -> {
List<byte[]> result = new ArrayList<>();
- Project.NameKey project = cd.change().getProject();
-
- cd.editRefs()
- .values()
- .forEach(r -> result.add(RefState.of(r).toByteArray(project)));
- cd.starRefs()
- .values()
- .forEach(r -> result.add(RefState.of(r.ref()).toByteArray(allUsers(cd))));
-
- ChangeNotes notes = cd.notes();
- result.add(
- RefState.create(notes.getRefName(), notes.getMetaId()).toByteArray(project));
- notes.getRobotComments(); // Force loading robot comments.
- RobotCommentNotes robotNotes = notes.getRobotCommentNotes();
- result.add(
- RefState.create(robotNotes.getRefName(), robotNotes.getMetaId())
- .toByteArray(project));
- cd.draftRefs()
- .values()
- .forEach(r -> result.add(RefState.of(r).toByteArray(allUsers(cd))));
-
+ cd.getRefStates()
+ .entries()
+ .forEach(e -> result.add(e.getValue().toByteArray(e.getKey())));
return result;
});
diff --git a/java/com/google/gerrit/server/index/change/StalenessChecker.java b/java/com/google/gerrit/server/index/change/StalenessChecker.java
index 7e50104..ad5cc2b 100644
--- a/java/com/google/gerrit/server/index/change/StalenessChecker.java
+++ b/java/com/google/gerrit/server/index/change/StalenessChecker.java
@@ -93,7 +93,7 @@
return StalenessCheckResult.stale("Document %s missing from index", id);
}
ChangeData cd = result.get();
- return check(repoManager, id, parseStates(cd), parsePatterns(cd));
+ return check(repoManager, id, cd.getRefStates(), parsePatterns(cd));
}
/**
@@ -127,10 +127,6 @@
return StalenessCheckResult.notStale();
}
- private SetMultimap<Project.NameKey, RefState> parseStates(ChangeData cd) {
- return RefState.parseStates(cd.getRefStates());
- }
-
private ListMultimap<Project.NameKey, RefStatePattern> parsePatterns(ChangeData cd) {
return parsePatterns(cd.getRefStatePatterns());
}
diff --git a/java/com/google/gerrit/server/query/change/ChangeData.java b/java/com/google/gerrit/server/query/change/ChangeData.java
index 6de263e..05ecc61 100644
--- a/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -26,13 +26,16 @@
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+import com.google.common.collect.SetMultimap;
import com.google.common.primitives.Ints;
import com.google.gerrit.common.Nullable;
+import com.google.gerrit.common.UsedAt;
import com.google.gerrit.entities.Account;
import com.google.gerrit.entities.AttentionSetUpdate;
import com.google.gerrit.entities.Change;
@@ -43,6 +46,7 @@
import com.google.gerrit.entities.PatchSet;
import com.google.gerrit.entities.PatchSetApproval;
import com.google.gerrit.entities.Project;
+import com.google.gerrit.entities.Project.NameKey;
import com.google.gerrit.entities.RefNames;
import com.google.gerrit.entities.RobotComment;
import com.google.gerrit.entities.SubmitRecord;
@@ -50,6 +54,7 @@
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.index.RefState;
import com.google.gerrit.server.ApprovalsUtil;
import com.google.gerrit.server.ChangeMessagesUtil;
import com.google.gerrit.server.CommentsUtil;
@@ -69,6 +74,7 @@
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MergeUtil;
import com.google.gerrit.server.notedb.ChangeNotes;
+import com.google.gerrit.server.notedb.RobotCommentNotes;
import com.google.gerrit.server.patch.DiffSummary;
import com.google.gerrit.server.patch.DiffSummaryKey;
import com.google.gerrit.server.patch.PatchListCache;
@@ -308,7 +314,7 @@
private Integer totalCommentCount;
private LabelTypes labelTypes;
private Optional<Timestamp> mergedOn;
- private ImmutableList<byte[]> refStates;
+ private ImmutableSetMultimap<NameKey, RefState> refStates;
private ImmutableList<byte[]> refStatePatterns;
@Inject
@@ -1169,12 +1175,38 @@
}
}
- public ImmutableList<byte[]> getRefStates() {
+ public SetMultimap<NameKey, RefState> getRefStates() {
+ if (refStates == null) {
+ if (!lazyLoad) {
+ return ImmutableSetMultimap.of();
+ }
+
+ ImmutableSetMultimap.Builder<NameKey, RefState> result = ImmutableSetMultimap.builder();
+ editRefs().values().forEach(r -> result.put(project, RefState.of(r)));
+ starRefs().values().forEach(r -> result.put(allUsersName, RefState.of(r.ref())));
+
+ // TODO: instantiating the notes is too much. We don't want to parse NoteDb, we just want the
+ // refs.
+ result.put(project, RefState.create(notes().getRefName(), notes().getMetaId()));
+ notes().getRobotComments(); // Force loading robot comments.
+ RobotCommentNotes robotNotes = notes().getRobotCommentNotes();
+ result.put(project, RefState.create(robotNotes.getRefName(), robotNotes.getMetaId()));
+ draftRefs().values().forEach(r -> result.put(allUsersName, RefState.of(r)));
+
+ refStates = result.build();
+ }
+
return refStates;
}
+ @UsedAt(UsedAt.Project.GOOGLE)
public void setRefStates(Iterable<byte[]> refStates) {
- this.refStates = ImmutableList.copyOf(refStates);
+ // TODO(hanwen): remove Google use, and drop this method.
+ setRefStates(RefState.parseStates(refStates));
+ }
+
+ public void setRefStates(ImmutableSetMultimap<Project.NameKey, RefState> refStates) {
+ this.refStates = refStates;
}
public ImmutableList<byte[]> getRefStatePatterns() {
diff --git a/java/com/google/gerrit/server/restapi/project/CreateProject.java b/java/com/google/gerrit/server/restapi/project/CreateProject.java
index a5a0034..faab241 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateProject.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateProject.java
@@ -208,4 +208,18 @@
}
return normalizedBranches;
}
+
+ static class ValidBranchListener implements ProjectCreationValidationListener {
+ @Override
+ public void validateNewProject(CreateProjectArgs args) throws ValidationException {
+ for (String branch : args.branch) {
+ if (RefNames.isGerritRef(branch)) {
+ throw new ValidationException(
+ String.format(
+ "Cannot create a project with branch %s. Branches in the Gerrit internal refs namespace are not allowed",
+ branch));
+ }
+ }
+ }
+ }
}
diff --git a/java/com/google/gerrit/server/restapi/project/Module.java b/java/com/google/gerrit/server/restapi/project/Module.java
index 517b888..9217077 100644
--- a/java/com/google/gerrit/server/restapi/project/Module.java
+++ b/java/com/google/gerrit/server/restapi/project/Module.java
@@ -29,6 +29,7 @@
import com.google.gerrit.server.config.GerritConfigListener;
import com.google.gerrit.server.project.RefValidationHelper;
import com.google.gerrit.server.restapi.change.CherryPickCommit;
+import com.google.gerrit.server.validators.ProjectCreationValidationListener;
public class Module extends RestApiModule {
@@ -47,6 +48,8 @@
DynamicMap.mapOf(binder(), LABEL_KIND);
DynamicSet.bind(binder(), GerritConfigListener.class).to(SetParent.class);
+ DynamicSet.bind(binder(), ProjectCreationValidationListener.class)
+ .to(CreateProject.ValidBranchListener.class);
create(PROJECT_KIND).to(CreateProject.class);
put(PROJECT_KIND).to(PutProject.class);
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/ChangeMetaIT.java b/javatests/com/google/gerrit/acceptance/rest/change/ChangeMetaIT.java
new file mode 100644
index 0000000..3787a35
--- /dev/null
+++ b/javatests/com/google/gerrit/acceptance/rest/change/ChangeMetaIT.java
@@ -0,0 +1,58 @@
+// Copyright (C) 2017 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.change;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.entities.RefNames.changeMetaRef;
+
+import com.google.common.collect.Iterables;
+import com.google.gerrit.acceptance.AbstractDaemonTest;
+import com.google.gerrit.acceptance.PushOneCommit;
+import com.google.gerrit.entities.Change;
+import com.google.gerrit.extensions.common.ChangeInfo;
+import org.eclipse.jgit.lib.Repository;
+import org.junit.Test;
+
+/** Test handling of the NoteDb commit hash in the GetChange endpoint */
+public class ChangeMetaIT extends AbstractDaemonTest {
+ @Test
+ public void metaSha1_fromIndex() throws Exception {
+ PushOneCommit.Result result = createChange();
+ String changeId = result.getChangeId();
+
+ try (AutoCloseable ignored = disableNoteDb()) {
+ ChangeInfo change =
+ Iterables.getOnlyElement(gApi.changes().query().withQuery("change:" + changeId).get());
+
+ try (Repository repo = repoManager.openRepository(project)) {
+ assertThat(change.metaRevId)
+ .isEqualTo(
+ repo.exactRef(changeMetaRef(Change.id(change._number))).getObjectId().getName());
+ }
+ }
+ }
+
+ @Test
+ public void metaSha1_fromNoteDb() throws Exception {
+ PushOneCommit.Result result = createChange();
+ String changeId = result.getChangeId();
+ ChangeInfo before = gApi.changes().id(changeId).get();
+ try (Repository repo = repoManager.openRepository(project)) {
+ assertThat(before.metaRevId)
+ .isEqualTo(
+ repo.exactRef(changeMetaRef(Change.id(before._number))).getObjectId().getName());
+ }
+ }
+}
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java b/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
index 10fd65f..e5c5952 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
@@ -53,6 +53,7 @@
import com.google.gerrit.extensions.restapi.Url;
import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gerrit.server.project.ProjectState;
+import com.google.gerrit.server.validators.ValidationException;
import com.google.inject.Inject;
import java.util.Collections;
import java.util.Optional;
@@ -328,6 +329,21 @@
}
@Test
+ public void createProjectWithInvalidBranch() throws Exception {
+ String newProjectName = name("newProject");
+ ProjectInput in = new ProjectInput();
+ in.name = newProjectName;
+ in.createEmptyCommit = true;
+ in.branches = ImmutableList.of("refs/heads/test", "refs/changes/34/1234");
+ Throwable thrown =
+ assertThrows(ResourceConflictException.class, () -> gApi.projects().create(in));
+ assertThat(thrown).hasCauseThat().isInstanceOf(ValidationException.class);
+ assertThat(thrown)
+ .hasMessageThat()
+ .contains("Cannot create a project with branch refs/changes/34/1234");
+ }
+
+ @Test
public void createProjectWithCapability() throws Exception {
projectOperations
.allProjectsForUpdate()
diff --git a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_html.ts b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_html.ts
index fae920d..99094d2 100644
--- a/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_html.ts
+++ b/polygerrit-ui/app/elements/change/gr-confirm-submit-dialog/gr-confirm-submit-dialog_html.ts
@@ -53,7 +53,7 @@
<template is="dom-if" if="[[change.is_private]]">
<p>
<iron-icon
- icon="gr-icons:error"
+ icon="gr-icons:warning"
class="warningBeforeSubmit"
></iron-icon>
<strong>Heads Up!</strong>
@@ -63,7 +63,7 @@
<template is="dom-if" if="[[change.unresolved_comment_count]]">
<p>
<iron-icon
- icon="gr-icons:error"
+ icon="gr-icons:warning"
class="warningBeforeSubmit"
></iron-icon>
[[_computeUnresolvedCommentsWarning(change)]]
@@ -80,7 +80,7 @@
</template>
<template is="dom-if" if="[[_computeHasChangeEdit(change)]]">
<iron-icon
- icon="gr-icons:error"
+ icon="gr-icons:warning"
class="warningBeforeSubmit"
></iron-icon>
Your unpublished edit will not be submitted. Did you forget to click