Merge "PostReview: Add debug logs that are included into traces"
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 1e1b852..b764439 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -1281,7 +1281,7 @@
[[change.strictLabels]]change.strictLabels::
+
Reject invalid label votes: invalid labels or invalid values. This
-configuration option is provided for backwards compaitbility and may
+configuration option is provided for backwards compatibility and may
be removed in future gerrit versions.
+
Default is false.
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index 2b49e3e..77be4b3 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -1252,57 +1252,6 @@
}
----
-[[create-change]]
-=== Create Change for review.
-
-This endpoint is functionally equivalent to
-link:rest-api-changes.html#create-change[create change in the change
-API], but it has the project name in the URL, which is easier to route
-in sharded deployments.
-
-.Request
-----
- POST /projects/myProject/create.change HTTP/1.0
- Content-Type: application/json; charset=UTF-8
-
- {
- "subject" : "Let's support 100% Gerrit workflow direct in browser",
- "branch" : "master",
- "topic" : "create-change-in-browser",
- "status" : "NEW"
- }
-----
-
-As response a link:#change-info[ChangeInfo] entity is returned that describes
-the resulting change.
-
-.Response
-----
- HTTP/1.1 201 OK
- Content-Disposition: attachment
- Content-Type: application/json; charset=UTF-8
-
- )]}'
- {
- "id": "myProject~master~I8473b95934b5732ac55d26311a706c9c2bde9941",
- "project": "myProject",
- "branch": "master",
- "topic": "create-change-in-browser",
- "change_id": "I8473b95934b5732ac55d26311a706c9c2bde9941",
- "subject": "Let's support 100% Gerrit workflow direct in browser",
- "status": "NEW",
- "created": "2014-05-05 07:15:44.639000000",
- "updated": "2014-05-05 07:15:44.639000000",
- "mergeable": true,
- "insertions": 0,
- "deletions": 0,
- "_number": 4711,
- "owner": {
- "name": "John Doe"
- }
- }
-----
-
[[create-access-change]]
=== Create Access Rights Change for review.
--
diff --git a/WORKSPACE b/WORKSPACE
index 1a0fd4e..befde25 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -776,8 +776,8 @@
maven_jar(
name = "dropwizard-core",
- artifact = "io.dropwizard.metrics:metrics-core:4.0.5",
- sha1 = "b81ef162970cdb9f4512ee2da09715a856ff4c4c",
+ artifact = "io.dropwizard.metrics:metrics-core:4.0.7",
+ sha1 = "673899f605f52ca35836673ccfee97154a496a61",
)
# When updating Bouncy Castle, also update it in bazlets.
@@ -1024,8 +1024,8 @@
maven_jar(
name = "jackson-core",
- artifact = "com.fasterxml.jackson.core:jackson-core:2.9.8",
- sha1 = "0f5a654e4675769c716e5b387830d19b501ca191",
+ artifact = "com.fasterxml.jackson.core:jackson-core:2.10.0",
+ sha1 = "4e2c5fa04648ec9772c63e2101c53af6504e624e",
)
TESTCONTAINERS_VERSION = "1.12.3"
diff --git a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
index 20088ec..a390de3 100644
--- a/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
+++ b/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
@@ -18,6 +18,7 @@
import static com.google.gerrit.extensions.client.GeneralPreferencesInfo.EmailStrategy.DISABLED;
import static java.util.Objects.requireNonNull;
+import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.entities.Account;
@@ -92,13 +93,16 @@
* @throws EmailException
*/
public void send() throws EmailException {
- if (!notify.shouldNotify()) {
- return;
- }
-
if (!args.emailSender.isEnabled()) {
// Server has explicitly disabled email sending.
//
+ logger.atFine().log(
+ "Not sending '%s': Email sending is disabled by server config", messageClass);
+ return;
+ }
+
+ if (!notify.shouldNotify()) {
+ logger.atFine().log("Not sending '%s': Notify handling is NONE", messageClass);
return;
}
@@ -149,6 +153,7 @@
}
}
if (smtpRcptTo.isEmpty() && smtpRcptToPlaintextOnly.isEmpty()) {
+ logger.atFine().log("Not sending '%s': No SMTP recipients", messageClass);
return;
}
}
@@ -187,16 +192,26 @@
try {
validator.validateOutgoingEmail(va);
} catch (ValidationException e) {
+ logger.atFine().log(
+ "Not sending '%s': Rejected by outgoing email validator: %s",
+ messageClass, e.getMessage());
return;
}
}
+ Set<Address> intersection = Sets.intersection(smtpRcptTo, smtpRcptToPlaintextOnly);
+ if (!intersection.isEmpty()) {
+ logger.atSevere().log("Email '%s' will be sent twice to %s", messageClass, intersection);
+ }
+
if (!smtpRcptTo.isEmpty()) {
// Send multipart message
+ logger.atFine().log("Sending multipart '%s'", messageClass);
args.emailSender.send(va.smtpFromAddress, va.smtpRcptTo, va.headers, va.body, va.htmlBody);
}
if (!smtpRcptToPlaintextOnly.isEmpty()) {
+ logger.atFine().log("Sending plaintext '%s'", messageClass);
// Send plaintext message
Map<String, EmailHeader> shallowCopy = new HashMap<>();
shallowCopy.putAll(headers);
@@ -398,6 +413,7 @@
protected boolean shouldSendMessage() {
if (textBody.length() == 0) {
// If we have no message body, don't send.
+ logger.atFine().log("Not sending '%s': No message body", messageClass);
return false;
}
@@ -405,6 +421,7 @@
// If we have nobody to send this message to, then all of our
// selection filters previously for this type of message were
// unable to match a destination. Don't bother sending it.
+ logger.atFine().log("Not sending '%s': No recipients", messageClass);
return false;
}
@@ -414,6 +431,7 @@
&& rcptTo.contains(fromId)) {
// If the only recipient is also the sender, don't bother.
//
+ logger.atFine().log("Not sending '%s': Sender is only recipient", messageClass);
return false;
}
diff --git a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
index 9c45aaf..3322b68 100644
--- a/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
+++ b/java/com/google/gerrit/server/notedb/ChangeNotesParser.java
@@ -363,6 +363,15 @@
submissionId = parseSubmissionId(commit);
}
+ if (lastUpdatedOn == null || ts.after(lastUpdatedOn)) {
+ lastUpdatedOn = ts;
+ }
+
+ if (deletedPatchSets.contains(psId)) {
+ // Do not update PS details as PS was deleted and this meta data is of no relevance.
+ return;
+ }
+
// Parse mutable patch set fields first so they can be recorded in the PendingPatchSetFields.
parseDescription(psId, commit);
parseGroups(psId, commit);
@@ -410,10 +419,6 @@
previousWorkInProgressFooter = null;
parseWorkInProgress(commit);
-
- if (lastUpdatedOn == null || ts.after(lastUpdatedOn)) {
- lastUpdatedOn = ts;
- }
}
private String parseSubmissionId(ChangeNotesCommit commit) throws ConfigInvalidException {
@@ -487,10 +492,6 @@
throw parseException("patch set %s requires an identified user as uploader", psId.get());
}
if (patchSetCommitParsed(psId)) {
- if (deletedPatchSets.contains(psId)) {
- // Do not update PS details as PS was deleted and this meta data is of no relevance.
- return;
- }
ObjectId commitId = patchSets.get(psId).commitId().orElseThrow(IllegalStateException::new);
throw new ConfigInvalidException(
String.format(
diff --git a/java/com/google/gerrit/server/patch/PatchScriptBuilder.java b/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
index f3c8eab..71b35d1 100644
--- a/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
+++ b/java/com/google/gerrit/server/patch/PatchScriptBuilder.java
@@ -53,6 +53,7 @@
import org.eclipse.jgit.treewalk.TreeWalk;
class PatchScriptBuilder {
+
static final int MAX_CONTEXT = 5000000;
static final int BIG_FILE = 9000;
@@ -66,10 +67,6 @@
private ComparisonType comparisonType;
private ObjectId aId;
private ObjectId bId;
-
- private final Side a;
- private final Side b;
-
private List<Edit> edits;
private final FileTypeRegistry registry;
private final PatchListCache patchListCache;
@@ -77,8 +74,6 @@
@Inject
PatchScriptBuilder(FileTypeRegistry ftr, PatchListCache plc) {
- a = new Side();
- b = new Side();
registry = ftr;
patchListCache = plc;
}
@@ -124,11 +119,9 @@
boolean intralineFailure = false;
boolean intralineTimeout = false;
- a.path = oldName(content);
- b.path = newName(content);
-
- a.resolve(null, aId);
- b.resolve(a, bId);
+ SideResolver resolver = new SideResolver();
+ Side a = resolver.resolve(oldName(content), null, aId);
+ Side b = resolver.resolve(newName(content), a, bId);
edits = new ArrayList<>(content.getEdits());
ImmutableSet<Edit> editsDueToRebase = content.getEditsDueToRebase();
@@ -161,7 +154,7 @@
}
}
- correctForDifferencesInNewlineAtEnd();
+ correctForDifferencesInNewlineAtEnd(a, b);
if (comments != null) {
ensureCommentsVisible(comments);
@@ -193,7 +186,7 @@
//
context = MAX_CONTEXT;
- packContent(diffPrefs.ignoreWhitespace != Whitespace.IGNORE_NONE);
+ packContent(a, b, diffPrefs.ignoreWhitespace != Whitespace.IGNORE_NONE);
}
return new PatchScript(
@@ -267,7 +260,7 @@
}
}
- private void correctForDifferencesInNewlineAtEnd() {
+ private void correctForDifferencesInNewlineAtEnd(Side a, Side b) {
// a.src.size() is the size ignoring a newline at the end whereas a.size() considers it.
int aSize = a.src.size();
int bSize = b.src.size();
@@ -280,7 +273,7 @@
}
Optional<Edit> lastEdit = getLast(edits);
- if (isNewlineAtEndDeleted()) {
+ if (isNewlineAtEndDeleted(a, b)) {
Optional<Edit> lastLineEdit = lastEdit.filter(edit -> edit.getEndA() == aSize);
if (lastLineEdit.isPresent()) {
lastLineEdit.get().extendA();
@@ -288,7 +281,7 @@
Edit newlineEdit = new Edit(aSize, aSize + 1, bSize, bSize);
edits.add(newlineEdit);
}
- } else if (isNewlineAtEndAdded()) {
+ } else if (isNewlineAtEndAdded(a, b)) {
Optional<Edit> lastLineEdit = lastEdit.filter(edit -> edit.getEndB() == bSize);
if (lastLineEdit.isPresent()) {
lastLineEdit.get().extendB();
@@ -303,11 +296,11 @@
return list.isEmpty() ? Optional.empty() : Optional.ofNullable(list.get(list.size() - 1));
}
- private boolean isNewlineAtEndDeleted() {
+ private boolean isNewlineAtEndDeleted(Side a, Side b) {
return !a.src.isMissingNewlineAtEnd() && b.src.isMissingNewlineAtEnd();
}
- private boolean isNewlineAtEndAdded() {
+ private boolean isNewlineAtEndAdded(Side a, Side b) {
return a.src.isMissingNewlineAtEnd() && !b.src.isMissingNewlineAtEnd();
}
@@ -425,7 +418,7 @@
return last.getEndA() + (b - last.getEndB());
}
- private void packContent(boolean ignoredWhitespace) {
+ private void packContent(Side a, Side b, boolean ignoredWhitespace) {
EditList list = new EditList(edits, context, a.size(), b.size());
for (EditList.Hunk hunk : list.getHunks()) {
while (hunk.next()) {
@@ -459,16 +452,38 @@
}
}
- private class Side {
- String path;
- ObjectId id;
- FileMode mode;
- byte[] srcContent;
- Text src;
- MimeType mimeType = MimeUtil2.UNKNOWN_MIME_TYPE;
- DisplayMethod displayMethod = DisplayMethod.DIFF;
- PatchScript.FileMode fileMode = PatchScript.FileMode.FILE;
- final SparseFileContent dst = new SparseFileContent();
+ private static class Side {
+
+ final String path;
+ final ObjectId id;
+ final FileMode mode;
+ final byte[] srcContent;
+ final Text src;
+ final MimeType mimeType;
+ final DisplayMethod displayMethod;
+ final PatchScript.FileMode fileMode;
+ final SparseFileContent dst;
+
+ public Side(
+ String path,
+ ObjectId id,
+ FileMode mode,
+ byte[] srcContent,
+ Text src,
+ MimeType mimeType,
+ DisplayMethod displayMethod,
+ PatchScript.FileMode fileMode) {
+ this.path = path;
+ this.id = id;
+ this.mode = mode;
+ this.srcContent = srcContent;
+ this.src = src;
+ this.mimeType = mimeType;
+ this.displayMethod = displayMethod;
+ this.fileMode = fileMode;
+ dst = new SparseFileContent();
+ dst.setSize(size());
+ }
int size() {
if (src == null) {
@@ -488,58 +503,60 @@
String getSourceLine(int lineNumber) {
return lineNumber >= src.size() ? "" : src.getString(lineNumber);
}
+ }
- void resolve(Side other, ObjectId within) throws IOException {
+ private class SideResolver {
+
+ Side resolve(final String path, final Side other, final ObjectId within) throws IOException {
try {
- final boolean reuse;
- if (Patch.COMMIT_MSG.equals(path)) {
+ boolean isCommitMsg = Patch.COMMIT_MSG.equals(path);
+ boolean isMergeList = Patch.MERGE_LIST.equals(path);
+ if (isCommitMsg || isMergeList) {
if (comparisonType.isAgainstParentOrAutoMerge() && Objects.equals(aId, within)) {
- id = ObjectId.zeroId();
- src = Text.EMPTY;
- srcContent = Text.NO_BYTES;
- mode = FileMode.MISSING;
- displayMethod = DisplayMethod.NONE;
+ return createSide(
+ path,
+ ObjectId.zeroId(),
+ FileMode.MISSING,
+ Text.NO_BYTES,
+ Text.EMPTY,
+ MimeUtil2.UNKNOWN_MIME_TYPE,
+ DisplayMethod.NONE,
+ false);
} else {
- id = within;
- src = Text.forCommit(reader, within);
- srcContent = src.getContent();
+ Text src =
+ isCommitMsg
+ ? Text.forCommit(reader, within)
+ : Text.forMergeList(comparisonType, reader, within);
+ byte[] srcContent = src.getContent();
+ DisplayMethod displayMethod;
+ FileMode mode;
if (src == Text.EMPTY) {
mode = FileMode.MISSING;
displayMethod = DisplayMethod.NONE;
} else {
mode = FileMode.REGULAR_FILE;
+ displayMethod = DisplayMethod.DIFF;
}
+ return createSide(
+ path,
+ within,
+ mode,
+ srcContent,
+ src,
+ MimeUtil2.UNKNOWN_MIME_TYPE,
+ displayMethod,
+ false);
}
- reuse = false;
- } else if (Patch.MERGE_LIST.equals(path)) {
- if (comparisonType.isAgainstParentOrAutoMerge() && Objects.equals(aId, within)) {
- id = ObjectId.zeroId();
- src = Text.EMPTY;
- srcContent = Text.NO_BYTES;
- mode = FileMode.MISSING;
- displayMethod = DisplayMethod.NONE;
- } else {
- id = within;
- src = Text.forMergeList(comparisonType, reader, within);
- srcContent = src.getContent();
- if (src == Text.EMPTY) {
- mode = FileMode.MISSING;
- displayMethod = DisplayMethod.NONE;
- } else {
- mode = FileMode.REGULAR_FILE;
- }
- }
- reuse = false;
} else {
- final TreeWalk tw = find(within);
-
- id = tw != null ? tw.getObjectId(0) : ObjectId.zeroId();
- mode = tw != null ? tw.getFileMode(0) : FileMode.MISSING;
- reuse =
+ final TreeWalk tw = find(path, within);
+ ObjectId id = tw != null ? tw.getObjectId(0) : ObjectId.zeroId();
+ FileMode mode = tw != null ? tw.getFileMode(0) : FileMode.MISSING;
+ boolean reuse =
other != null
&& other.id.equals(id)
&& (other.mode == mode || isBothFile(other.mode, mode));
-
+ Text src = null;
+ byte[] srcContent;
if (reuse) {
srcContent = other.srcContent;
@@ -553,7 +570,8 @@
} else {
srcContent = Text.NO_BYTES;
}
-
+ MimeType mimeType = MimeUtil2.UNKNOWN_MIME_TYPE;
+ DisplayMethod displayMethod = DisplayMethod.DIFF;
if (reuse) {
mimeType = other.mimeType;
displayMethod = other.displayMethod;
@@ -565,33 +583,42 @@
displayMethod = DisplayMethod.IMG;
}
}
- }
-
- if (mode == FileMode.MISSING) {
- displayMethod = DisplayMethod.NONE;
- }
-
- if (!reuse) {
- if (srcContent == Text.NO_BYTES) {
- src = Text.EMPTY;
- } else {
- src = new Text(srcContent);
- }
- }
-
- dst.setSize(size());
-
- if (mode == FileMode.SYMLINK) {
- fileMode = PatchScript.FileMode.SYMLINK;
- } else if (mode == FileMode.GITLINK) {
- fileMode = PatchScript.FileMode.GITLINK;
+ return createSide(path, id, mode, srcContent, src, mimeType, displayMethod, reuse);
}
} catch (IOException err) {
throw new IOException("Cannot read " + within.name() + ":" + path, err);
}
}
- private TreeWalk find(ObjectId within)
+ private Side createSide(
+ String path,
+ ObjectId id,
+ FileMode mode,
+ byte[] srcContent,
+ Text src,
+ MimeType mimeType,
+ DisplayMethod displayMethod,
+ boolean reuse) {
+ if (!reuse) {
+ if (srcContent == Text.NO_BYTES) {
+ src = Text.EMPTY;
+ } else {
+ src = new Text(srcContent);
+ }
+ }
+ if (mode == FileMode.MISSING) {
+ displayMethod = DisplayMethod.NONE;
+ }
+ PatchScript.FileMode fileMode = PatchScript.FileMode.FILE;
+ if (mode == FileMode.SYMLINK) {
+ fileMode = PatchScript.FileMode.SYMLINK;
+ } else if (mode == FileMode.GITLINK) {
+ fileMode = PatchScript.FileMode.GITLINK;
+ }
+ return new Side(path, id, mode, srcContent, src, mimeType, displayMethod, fileMode);
+ }
+
+ private TreeWalk find(String path, ObjectId within)
throws MissingObjectException, IncorrectObjectTypeException, CorruptObjectException,
IOException {
if (path == null || within == null) {
diff --git a/java/com/google/gerrit/server/restapi/change/CreateChange.java b/java/com/google/gerrit/server/restapi/change/CreateChange.java
index 758cf47..acc6465 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateChange.java
@@ -170,28 +170,16 @@
BatchUpdate.Factory updateFactory, TopLevelResource parent, ChangeInput input)
throws IOException, InvalidChangeOperationException, RestApiException, UpdateException,
PermissionBackendException, ConfigInvalidException {
- if (Strings.isNullOrEmpty(input.project)) {
- throw new BadRequestException("project must be non-empty");
- }
-
- return execute(updateFactory, input, projectsCollection.parse(input.project));
- }
-
- /** Creates the changes in the given project. This is public for reuse in the project API. */
- public Response<ChangeInfo> execute(
- BatchUpdate.Factory updateFactory, ChangeInput input, ProjectResource projectResource)
- throws IOException, RestApiException, UpdateException, PermissionBackendException,
- ConfigInvalidException {
if (!user.get().isIdentifiedUser()) {
throw new AuthException("Authentication required");
}
-
- ProjectState projectState = projectResource.getProjectState();
- projectState.checkStatePermitsWrite();
-
IdentifiedUser me = user.get().asIdentifiedUser();
checkAndSanitizeChangeInput(input, me);
+ ProjectResource projectResource = projectsCollection.parse(input.project);
+ ProjectState projectState = projectResource.getProjectState();
+ projectState.checkStatePermitsWrite();
+
Project.NameKey project = projectResource.getNameKey();
contributorAgreements.check(project, user.get());
@@ -214,6 +202,10 @@
*/
private void checkAndSanitizeChangeInput(ChangeInput input, IdentifiedUser me)
throws RestApiException, PermissionBackendException, IOException {
+ if (Strings.isNullOrEmpty(input.project)) {
+ throw new BadRequestException("project must be non-empty");
+ }
+
if (Strings.isNullOrEmpty(input.branch)) {
throw new BadRequestException("branch must be non-empty");
}
diff --git a/java/com/google/gerrit/server/restapi/project/CreateChange.java b/java/com/google/gerrit/server/restapi/project/CreateChange.java
deleted file mode 100644
index de11ffe..0000000
--- a/java/com/google/gerrit/server/restapi/project/CreateChange.java
+++ /dev/null
@@ -1,70 +0,0 @@
-// Copyright (C) 2019 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.server.restapi.project;
-
-import com.google.common.base.Strings;
-import com.google.gerrit.exceptions.InvalidNameException;
-import com.google.gerrit.extensions.common.ChangeInfo;
-import com.google.gerrit.extensions.common.ChangeInput;
-import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.extensions.restapi.BadRequestException;
-import com.google.gerrit.extensions.restapi.Response;
-import com.google.gerrit.extensions.restapi.RestApiException;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.project.InvalidChangeOperationException;
-import com.google.gerrit.server.project.ProjectResource;
-import com.google.gerrit.server.update.BatchUpdate;
-import com.google.gerrit.server.update.RetryHelper;
-import com.google.gerrit.server.update.RetryingRestModifyView;
-import com.google.gerrit.server.update.UpdateException;
-import com.google.inject.Inject;
-import com.google.inject.Provider;
-import com.google.inject.Singleton;
-import java.io.IOException;
-import org.eclipse.jgit.errors.ConfigInvalidException;
-
-@Singleton
-public class CreateChange extends RetryingRestModifyView<ProjectResource, ChangeInput, ChangeInfo> {
- private final com.google.gerrit.server.restapi.change.CreateChange changeCreateChange;
- private final Provider<CurrentUser> user;
-
- @Inject
- public CreateChange(
- RetryHelper retryHelper,
- Provider<CurrentUser> user,
- com.google.gerrit.server.restapi.change.CreateChange changeCreateChange) {
- super(retryHelper);
- this.changeCreateChange = changeCreateChange;
- this.user = user;
- }
-
- @Override
- public Response<ChangeInfo> applyImpl(
- BatchUpdate.Factory updateFactory, ProjectResource rsrc, ChangeInput input)
- throws PermissionBackendException, IOException, ConfigInvalidException,
- InvalidChangeOperationException, InvalidNameException, UpdateException, RestApiException {
- if (!user.get().isIdentifiedUser()) {
- throw new AuthException("Authentication required");
- }
-
- if (!Strings.isNullOrEmpty(input.project)) {
- throw new BadRequestException("may not specify project");
- }
-
- input.project = rsrc.getName();
- return changeCreateChange.execute(updateFactory, input, rsrc);
- }
-}
diff --git a/java/com/google/gerrit/server/restapi/project/Module.java b/java/com/google/gerrit/server/restapi/project/Module.java
index 4ed21cc..2c76cbd 100644
--- a/java/com/google/gerrit/server/restapi/project/Module.java
+++ b/java/com/google/gerrit/server/restapi/project/Module.java
@@ -85,7 +85,6 @@
child(PROJECT_KIND, "branches").to(BranchesCollection.class);
create(BRANCH_KIND).to(CreateBranch.class);
- post(PROJECT_KIND, "create.change").to(CreateChange.class);
put(BRANCH_KIND).to(PutBranch.class);
get(BRANCH_KIND).to(GetBranch.class);
delete(BRANCH_KIND).to(DeleteBranch.class);
diff --git a/java/com/google/gerrit/server/submit/MergeOpRepoManager.java b/java/com/google/gerrit/server/submit/MergeOpRepoManager.java
index c2577e7..9a7ced5 100644
--- a/java/com/google/gerrit/server/submit/MergeOpRepoManager.java
+++ b/java/com/google/gerrit/server/submit/MergeOpRepoManager.java
@@ -42,10 +42,9 @@
import java.util.Map;
import java.util.Objects;
import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectInserter;
import org.eclipse.jgit.lib.ObjectReader;
-import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevFlag;
import org.eclipse.jgit.revwalk.RevSort;
@@ -130,19 +129,17 @@
}
public static class OpenBranch {
- final RefUpdate update;
final CodeReviewCommit oldTip;
MergeTip mergeTip;
OpenBranch(OpenRepo or, BranchNameKey name) throws IntegrationException {
try {
- update = or.repo.updateRef(name.branch());
- if (update.getOldObjectId() != null) {
- oldTip = or.rw.parseCommit(update.getOldObjectId());
+ Ref ref = or.getRepo().exactRef(name.branch());
+ if (ref != null) {
+ oldTip = or.rw.parseCommit(ref.getObjectId());
} else if (Objects.equals(or.repo.getFullBranch(), name.branch())
|| Objects.equals(RefNames.REFS_CONFIG, name.branch())) {
oldTip = null;
- update.setExpectedOldObjectId(ObjectId.zeroId());
} else {
throw new IntegrationException(
"The destination branch " + name + " does not exist anymore.");
diff --git a/javatests/com/google/gerrit/acceptance/rest/binding/ProjectsRestApiBindingsIT.java b/javatests/com/google/gerrit/acceptance/rest/binding/ProjectsRestApiBindingsIT.java
index 02557cc..b8ab752 100644
--- a/javatests/com/google/gerrit/acceptance/rest/binding/ProjectsRestApiBindingsIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/binding/ProjectsRestApiBindingsIT.java
@@ -70,7 +70,6 @@
RestCall.get("/projects/%s/statistics.git"),
RestCall.post("/projects/%s/index"),
RestCall.post("/projects/%s/gc"),
- RestCall.post("/projects/%s/create.change"),
RestCall.get("/projects/%s/children"),
RestCall.get("/projects/%s/branches"),
RestCall.post("/projects/%s/branches:delete"),
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/CreateChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/project/CreateChangeIT.java
deleted file mode 100644
index ca3707d..0000000
--- a/javatests/com/google/gerrit/acceptance/rest/project/CreateChangeIT.java
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright (C) 2019 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.project;
-
-import static com.google.common.truth.Truth8.assertThat;
-import static com.google.gerrit.entities.RefNames.REFS_HEADS;
-
-import com.google.gerrit.acceptance.AbstractDaemonTest;
-import com.google.gerrit.acceptance.RestResponse;
-import com.google.gerrit.extensions.api.projects.BranchInput;
-import com.google.gerrit.extensions.common.ChangeInput;
-import org.junit.Test;
-
-public class CreateChangeIT extends AbstractDaemonTest {
-
- // Just a basic test. The real functionality is tested under the restapi.change acceptance tests.
- @Test
- public void basic() throws Exception {
- BranchInput branchInput = new BranchInput();
- branchInput.ref = "foo";
- assertThat(gApi.projects().name(project.get()).branches().get().stream().map(i -> i.ref))
- .doesNotContain(REFS_HEADS + branchInput.ref);
- RestResponse r =
- adminRestSession.put(
- "/projects/" + project.get() + "/branches/" + branchInput.ref, branchInput);
- r.assertCreated();
-
- ChangeInput input = new ChangeInput();
- input.branch = "foo";
- input.subject = "subject";
- RestResponse cr = adminRestSession.post("/projects/" + project.get() + "/create.change", input);
- cr.assertCreated();
- }
-}
diff --git a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
index 5993206..145e914 100644
--- a/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
+++ b/javatests/com/google/gerrit/server/notedb/ChangeNotesTest.java
@@ -3062,6 +3062,38 @@
assertThat(newNotes(c).getUpdateCount()).isEqualTo(3);
}
+ @Test
+ public void createPatchSetAfterPatchSetDeletion() throws Exception {
+ Change c = newChange();
+ assertThat(newNotes(c).getChange().currentPatchSetId().get()).isEqualTo(1);
+
+ // Create PS2.
+ incrementCurrentPatchSetFieldOnly(c);
+ RevCommit commit = tr.commit().message("PS" + c.currentPatchSetId().get()).create();
+ ChangeUpdate update = newUpdate(c, changeOwner);
+ update.setCommit(rw, commit);
+ update.setGroups(ImmutableList.of(commit.name()));
+ update.commit();
+ assertThat(newNotes(c).getChange().currentPatchSetId().get()).isEqualTo(2);
+
+ // Delete PS2.
+ update = newUpdate(c, changeOwner);
+ update.setPatchSetState(PatchSetState.DELETED);
+ update.commit();
+ c = newNotes(c).getChange();
+ assertThat(c.currentPatchSetId().get()).isEqualTo(1);
+
+ // Create another PS2
+ incrementCurrentPatchSetFieldOnly(c);
+ commit = tr.commit().message("PS" + c.currentPatchSetId().get()).create();
+ update = newUpdate(c, changeOwner);
+ update.setPatchSetState(PatchSetState.PUBLISHED);
+ update.setCommit(rw, commit);
+ update.setGroups(ImmutableList.of(commit.name()));
+ update.commit();
+ assertThat(newNotes(c).getChange().currentPatchSetId().get()).isEqualTo(2);
+ }
+
private String readNote(ChangeNotes notes, ObjectId noteId) throws Exception {
ObjectId dataId = notes.revisionNoteMap.noteMap.getNote(noteId).getData();
return new String(rw.getObjectReader().open(dataId, OBJ_BLOB).getCachedBytes(), UTF_8);
diff --git a/polygerrit-ui/app/elements/gr-app-element.js b/polygerrit-ui/app/elements/gr-app-element.js
index 2a73be3..cf18241 100644
--- a/polygerrit-ui/app/elements/gr-app-element.js
+++ b/polygerrit-ui/app/elements/gr-app-element.js
@@ -427,7 +427,7 @@
_computePluginScreenName({plugin, screen}) {
if (!plugin || !screen) return '';
- return `${plugin.getPluginName()}-screen-${screen}`;
+ return `${plugin}-screen-${screen}`;
},
_logWelcome() {
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index c84a113..80462d3 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -1776,8 +1776,9 @@
opt_workInProgress, opt_baseChange, opt_baseCommit) {
return this._restApiHelper.send({
method: 'POST',
- url: `/projects/${encodeURIComponent(project)}/create.change`,
+ url: '/changes/',
body: {
+ project,
branch,
subject,
topic: opt_topic,
diff --git a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js
index 43b4a16..261ddda 100644
--- a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js
+++ b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea.js
@@ -20,32 +20,32 @@
const MAX_ITEMS_DROPDOWN = 10;
const ALL_SUGGESTIONS = [
+ {value: '😊', match: 'smile :)'},
+ {value: '👍', match: 'thumbs up'},
+ {value: '😄', match: 'laugh :D'},
+ {value: '🎉', match: 'party'},
+ {value: '😞', match: 'sad :('},
+ {value: '😂', match: 'tears :\')'},
+ {value: '🙏', match: 'pray'},
+ {value: '😐', match: 'neutral :|'},
+ {value: '😮', match: 'shock :O'},
+ {value: '👎', match: 'thumbs down'},
+ {value: '😎', match: 'cool |;)'},
+ {value: '😕', match: 'confused'},
+ {value: '👌', match: 'ok'},
+ {value: '🔥', match: 'fire'},
+ {value: '👊', match: 'fistbump'},
{value: '💯', match: '100'},
{value: '💔', match: 'broken heart'},
{value: '🍺', match: 'beer'},
{value: '✔', match: 'check'},
- {value: '😎', match: 'cool |;)'},
- {value: '😕', match: 'confused'},
+ {value: '😋', match: 'tongue'},
{value: '😭', match: 'crying :\'('},
- {value: '🔥', match: 'fire'},
- {value: '👊', match: 'fistbump'},
{value: '🐨', match: 'koala'},
- {value: '😄', match: 'laugh :D'},
{value: '🤓', match: 'glasses'},
{value: '😆', match: 'grin'},
- {value: '😐', match: 'neutral :|'},
- {value: '👌', match: 'ok'},
- {value: '🎉', match: 'party'},
{value: '💩', match: 'poop'},
- {value: '🙏', match: 'pray'},
- {value: '😞', match: 'sad :('},
- {value: '😮', match: 'shock :O'},
- {value: '😊', match: 'smile :)'},
{value: '😢', match: 'tear'},
- {value: '😂', match: 'tears :\')'},
- {value: '😋', match: 'tongue'},
- {value: '👍', match: 'thumbs up'},
- {value: '👎', match: 'thumbs down'},
{value: '😒', match: 'unamused'},
{value: '😉', match: 'wink ;)'},
{value: '🍷', match: 'wine'},
diff --git a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html
index d9078b6..4c59f50 100644
--- a/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-textarea/gr-textarea_test.html
@@ -179,9 +179,10 @@
element._determineSuggestions(emojiText);
assert.isTrue(formatSpy.called);
assert.isTrue(formatSpy.lastCall.calledWithExactly(
- [{dataValue: '😢', value: '😢', match: 'tear', text: '😢 tear'},
- {dataValue: '😂', value: '😂', match: 'tears :\')',
- text: '😂 tears :\')'}]));
+ [{dataValue: '😂', value: '😂', match: 'tears :\')',
+ text: '😂 tears :\')'},
+ {dataValue: '😢', value: '😢', match: 'tear', text: '😢 tear'},
+ ]));
});
test('_formatSuggestions', () => {
diff --git a/polygerrit-ui/app/styles/fonts.css b/polygerrit-ui/app/styles/fonts.css
index 41aec27..c837492 100644
--- a/polygerrit-ui/app/styles/fonts.css
+++ b/polygerrit-ui/app/styles/fonts.css
@@ -34,7 +34,7 @@
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
- src: local('Roboto'), local('RobotoMono-Regular'),
+ src: local('Roboto'), local('Roboto-Regular'),
url('../fonts/Roboto-Regular.woff2') format('woff2'),
url('../fonts/Roboto-Regular.woff') format('woff');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;