Merge changes from topic "motd" into stable-2.16
* changes:
Document MessageOfTheDay extension
Add UI element to display messages of the day
Add MessageOfTheDay-entries to ServerInfo
diff --git a/Documentation/metrics.txt b/Documentation/metrics.txt
index 37d6d01..ce9f74e 100644
--- a/Documentation/metrics.txt
+++ b/Documentation/metrics.txt
@@ -56,6 +56,18 @@
=== HTTP
+==== Jetty
+
+* `http/server/jetty/threadpool/active_threads`: Active threads
+* `http/server/jetty/threadpool/idle_threads`: Idle threads
+* `http/server/jetty/threadpool/reserved_threads`: Reserved threads
+* `http/server/jetty/threadpool/max_pool_size`: Maximum thread pool size
+* `http/server/jetty/threadpool/min_pool_size`: Minimum thread pool size
+* `http/server/jetty/threadpool/pool_size`: Current thread pool size
+* `http/server/jetty/threadpool/queue_size`: Queued requests waiting for a thread
+
+==== REST API
+
* `http/server/error_count`: Rate of REST API error responses.
* `http/server/success_count`: Rate of REST API success responses.
* `http/server/rest_api/count`: Rate of REST API calls by view.
diff --git a/java/com/google/gerrit/pgm/http/jetty/JettyMetrics.java b/java/com/google/gerrit/pgm/http/jetty/JettyMetrics.java
index 92edf40..b6a2d38 100644
--- a/java/com/google/gerrit/pgm/http/jetty/JettyMetrics.java
+++ b/java/com/google/gerrit/pgm/http/jetty/JettyMetrics.java
@@ -28,42 +28,42 @@
JettyMetrics(JettyServer jetty, MetricMaker metrics) {
CallbackMetric0<Integer> minPoolSize =
metrics.newCallbackMetric(
- "httpd/jetty/threadpool/min_pool_size",
+ "http/server/jetty/threadpool/min_pool_size",
Integer.class,
new Description("Minimum thread pool size").setGauge());
CallbackMetric0<Integer> maxPoolSize =
metrics.newCallbackMetric(
- "httpd/jetty/threadpool/max_pool_size",
+ "http/server/jetty/threadpool/max_pool_size",
Integer.class,
new Description("Maximum thread pool size").setGauge());
CallbackMetric0<Integer> poolSize =
metrics.newCallbackMetric(
- "httpd/jetty/threadpool/pool_size",
+ "http/server/jetty/threadpool/pool_size",
Integer.class,
new Description("Current thread pool size").setGauge());
CallbackMetric0<Integer> idleThreads =
metrics.newCallbackMetric(
- "httpd/jetty/threadpool/idle_threads",
+ "http/server/jetty/threadpool/idle_threads",
Integer.class,
- new Description("Idle httpd threads").setGauge().setUnit("threads"));
+ new Description("Idle threads").setGauge().setUnit("threads"));
CallbackMetric0<Integer> busyThreads =
metrics.newCallbackMetric(
- "httpd/jetty/threadpool/active_threads",
+ "http/server/jetty/threadpool/active_threads",
Integer.class,
- new Description("Active httpd threads").setGauge().setUnit("threads"));
+ new Description("Active threads").setGauge().setUnit("threads"));
CallbackMetric0<Integer> reservedThreads =
metrics.newCallbackMetric(
- "httpd/jetty/threadpool/reserved_threads",
+ "http/server/jetty/threadpool/reserved_threads",
Integer.class,
- new Description("Reserved httpd threads").setGauge().setUnit("threads"));
+ new Description("Reserved threads").setGauge().setUnit("threads"));
CallbackMetric0<Integer> queueSize =
metrics.newCallbackMetric(
- "httpd/jetty/threadpool/queue_size",
+ "http/server/jetty/threadpool/queue_size",
Integer.class,
- new Description("Thread pool queue size").setGauge().setUnit("requests"));
+ new Description("Queued requests waiting for a thread").setGauge().setUnit("requests"));
CallbackMetric0<Boolean> lowOnThreads =
metrics.newCallbackMetric(
- "httpd/jetty/threadpool/is_low_on_threads",
+ "http/server/jetty/threadpool/is_low_on_threads",
Boolean.class,
new Description("Whether thread pool is low on threads").setGauge());
JettyServer.Metrics jettyMetrics = jetty.getMetrics();
diff --git a/java/com/google/gerrit/server/change/ChangeInserter.java b/java/com/google/gerrit/server/change/ChangeInserter.java
index 80d05df..4e69fbe 100644
--- a/java/com/google/gerrit/server/change/ChangeInserter.java
+++ b/java/com/google/gerrit/server/change/ChangeInserter.java
@@ -71,6 +71,7 @@
import com.google.gerrit.server.update.Context;
import com.google.gerrit.server.update.InsertChangeOp;
import com.google.gerrit.server.update.RepoContext;
+import com.google.gerrit.server.util.CommitMessageUtil;
import com.google.gerrit.server.util.RequestScopePropagator;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
@@ -88,7 +89,6 @@
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.ReceiveCommand;
-import org.eclipse.jgit.util.ChangeIdUtil;
public class ChangeInserter implements InsertChangeOp {
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
@@ -210,16 +210,9 @@
if (!idList.isEmpty()) {
return new Change.Key(idList.get(idList.size() - 1).trim());
}
- ObjectId changeId =
- ChangeIdUtil.computeChangeId(
- commit.getTree(),
- commit,
- commit.getAuthorIdent(),
- commit.getCommitterIdent(),
- commit.getShortMessage());
- StringBuilder changeIdStr = new StringBuilder();
- changeIdStr.append("I").append(ObjectId.toString(changeId));
- return new Change.Key(changeIdStr.toString());
+ // A Change-Id is generated for the review, but not appended to the commit message.
+ // This can happen if requireChangeId is false.
+ return CommitMessageUtil.generateKey();
}
public PatchSet.Id getPatchSetId() {
diff --git a/java/com/google/gerrit/server/git/meta/VersionedMetaData.java b/java/com/google/gerrit/server/git/meta/VersionedMetaData.java
index f8cfce5..a228b89 100644
--- a/java/com/google/gerrit/server/git/meta/VersionedMetaData.java
+++ b/java/com/google/gerrit/server/git/meta/VersionedMetaData.java
@@ -22,6 +22,7 @@
import com.google.gerrit.server.git.LockFailureException;
import com.google.gerrit.server.logging.TraceContext;
import com.google.gerrit.server.logging.TraceContext.TraceTimer;
+import com.google.gerrit.server.util.CommitMessageUtil;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
@@ -326,14 +327,8 @@
}
if (update.insertChangeId()) {
- ObjectId id =
- ChangeIdUtil.computeChangeId(
- res,
- getRevision(),
- commit.getAuthor(),
- commit.getCommitter(),
- commit.getMessage());
- commit.setMessage(ChangeIdUtil.insertId(commit.getMessage(), id));
+ commit.setMessage(
+ ChangeIdUtil.insertId(commit.getMessage(), CommitMessageUtil.generateChangeId()));
}
src = rw.parseCommit(inserter.insert(commit));
diff --git a/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java b/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java
index ebec9c5..d6daae1 100644
--- a/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java
+++ b/java/com/google/gerrit/server/notedb/rebuild/NoteDbMigrator.java
@@ -72,6 +72,7 @@
import com.google.gerrit.server.notedb.RepoSequence;
import com.google.gerrit.server.notedb.rebuild.ChangeRebuilder.NoPatchSetsException;
import com.google.gerrit.server.project.NoSuchChangeException;
+import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.update.ChainedReceiveCommands;
import com.google.gerrit.server.update.RefUpdateUtil;
import com.google.gerrit.server.util.ManualRequestContext;
@@ -164,6 +165,7 @@
private final MutableNotesMigration globalNotesMigration;
private final PrimaryStorageMigrator primaryStorageMigrator;
private final DynamicSet<NotesMigrationStateListener> listeners;
+ private final ProjectCache projectCache;
private int threads;
private ImmutableList<Project.NameKey> projects = ImmutableList.of();
@@ -193,7 +195,8 @@
WorkQueue workQueue,
MutableNotesMigration globalNotesMigration,
PrimaryStorageMigrator primaryStorageMigrator,
- DynamicSet<NotesMigrationStateListener> listeners) {
+ DynamicSet<NotesMigrationStateListener> listeners,
+ ProjectCache projectCache) {
// Reload gerrit.config/notedb.config on each migrator invocation, in case a previous
// migration in the same process modified the on-disk contents. This ensures the defaults for
// trial/autoMigrate get set correctly below.
@@ -213,6 +216,7 @@
this.globalNotesMigration = globalNotesMigration;
this.primaryStorageMigrator = primaryStorageMigrator;
this.listeners = listeners;
+ this.projectCache = projectCache;
this.trial = getTrialMode(cfg);
this.autoMigrate = getAutoMigrate(cfg);
}
@@ -400,6 +404,7 @@
changes,
progressOut,
stopAtState,
+ projectCache,
trial,
forceRebuild,
sequenceGap >= 0 ? sequenceGap : Sequences.getChangeSequenceGap(cfg),
@@ -429,6 +434,7 @@
private final ImmutableList<Change.Id> changes;
private final OutputStream progressOut;
private final NotesMigrationState stopAtState;
+ private final ProjectCache projectCache;
private final boolean trial;
private final boolean forceRebuild;
private final int sequenceGap;
@@ -455,6 +461,7 @@
ImmutableList<Change.Id> changes,
OutputStream progressOut,
NotesMigrationState stopAtState,
+ ProjectCache projectCache,
boolean trial,
boolean forceRebuild,
int sequenceGap,
@@ -489,6 +496,7 @@
this.changes = changes;
this.progressOut = progressOut;
this.stopAtState = stopAtState;
+ this.projectCache = projectCache;
this.trial = trial;
this.forceRebuild = forceRebuild;
this.sequenceGap = sequenceGap;
@@ -702,11 +710,13 @@
* of the NoteDb migration code, which is too risky to attempt in the stable branch where this bug
* had to be fixed.
*
- * <p>As of this writing, the only case where this happens is when a change has no patch sets.
+ * <p>As of this writing, there are only two cases where this happens: when a change has no patch
+ * sets, or the project doesn't exist.
*/
- private static boolean canSkipPrimaryStorageMigration(ReviewDb db, Change.Id id) {
+ private boolean canSkipPrimaryStorageMigration(ReviewDb db, Change.Id id) {
try {
- return Iterables.isEmpty(unwrapDb(db).patchSets().byChange(id));
+ return Iterables.isEmpty(unwrapDb(db).patchSets().byChange(id))
+ || projectCache.get(unwrapDb(db).changes().get(id).getProject()) == null;
} catch (Exception e) {
logger.atSevere().withCause(e).log(
"Error checking if change %s can be skipped, assuming no", id);
diff --git a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
index 1104761..5def390 100644
--- a/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CherryPickChange.java
@@ -57,6 +57,7 @@
import com.google.gerrit.server.submit.MergeIdenticalTreeException;
import com.google.gerrit.server.update.BatchUpdate;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.util.CommitMessageUtil;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
@@ -193,14 +194,8 @@
Timestamp now = TimeUtil.nowTs();
PersonIdent committerIdent = identifiedUser.newCommitterIdent(now, serverTimeZone);
- final ObjectId computedChangeId =
- ChangeIdUtil.computeChangeId(
- commitToCherryPick.getTree(),
- baseCommit,
- commitToCherryPick.getAuthorIdent(),
- committerIdent,
- input.message);
- String commitMessage = ChangeIdUtil.insertId(input.message, computedChangeId).trim() + '\n';
+ final ObjectId generatedChangeId = CommitMessageUtil.generateChangeId();
+ String commitMessage = ChangeIdUtil.insertId(input.message, generatedChangeId).trim() + '\n';
CodeReviewCommit cherryPickCommit;
ProjectState projectState = projectCache.checkedGet(dest.getParentKey());
@@ -235,7 +230,7 @@
final String idStr = idList.get(idList.size() - 1).trim();
changeKey = new Change.Key(idStr);
} else {
- changeKey = new Change.Key("I" + computedChangeId.name());
+ changeKey = new Change.Key("I" + generatedChangeId.name());
}
Branch.NameKey newDest = new Branch.NameKey(project, destRef.getName());
diff --git a/java/com/google/gerrit/server/restapi/change/CreateChange.java b/java/com/google/gerrit/server/restapi/change/CreateChange.java
index 565777e..f906f7c 100644
--- a/java/com/google/gerrit/server/restapi/change/CreateChange.java
+++ b/java/com/google/gerrit/server/restapi/change/CreateChange.java
@@ -70,6 +70,7 @@
import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestCollectionModifyView;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.util.CommitMessageUtil;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
@@ -275,8 +276,7 @@
// Add a Change-Id line if there isn't already one
String commitMessage = subject;
if (ChangeIdUtil.indexOfChangeId(commitMessage, "\n") == -1) {
- ObjectId treeId = mergeTip == null ? emptyTreeId(oi) : mergeTip.getTree();
- ObjectId id = ChangeIdUtil.computeChangeId(treeId, mergeTip, author, author, commitMessage);
+ ObjectId id = CommitMessageUtil.generateChangeId();
commitMessage = ChangeIdUtil.insertId(commitMessage, id);
}
diff --git a/java/com/google/gerrit/server/restapi/change/Revert.java b/java/com/google/gerrit/server/restapi/change/Revert.java
index 7309fde..1040f43 100644
--- a/java/com/google/gerrit/server/restapi/change/Revert.java
+++ b/java/com/google/gerrit/server/restapi/change/Revert.java
@@ -65,6 +65,7 @@
import com.google.gerrit.server.update.RetryHelper;
import com.google.gerrit.server.update.RetryingRestModifyView;
import com.google.gerrit.server.update.UpdateException;
+import com.google.gerrit.server.util.CommitMessageUtil;
import com.google.gerrit.server.util.time.TimeUtil;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
@@ -207,14 +208,8 @@
patch.getRevision().get());
}
- ObjectId computedChangeId =
- ChangeIdUtil.computeChangeId(
- parentToCommitToRevert.getTree(),
- commitToRevert,
- authorIdent,
- committerIdent,
- message);
- revertCommitBuilder.setMessage(ChangeIdUtil.insertId(message, computedChangeId, true));
+ ObjectId generatedChangeId = CommitMessageUtil.generateChangeId();
+ revertCommitBuilder.setMessage(ChangeIdUtil.insertId(message, generatedChangeId, true));
Change.Id changeId = new Change.Id(seq.nextChangeId());
ObjectId id = oi.insert(revertCommitBuilder);
@@ -246,7 +241,7 @@
bu.setRepository(git, revWalk, oi);
bu.insertChange(ins);
bu.addOp(changeId, new NotifyOp(changeToRevert, ins, input.notify, accountsToNotify));
- bu.addOp(changeToRevert.getId(), new PostRevertedMessageOp(computedChangeId));
+ bu.addOp(changeToRevert.getId(), new PostRevertedMessageOp(generatedChangeId));
bu.execute();
}
return changeId;
diff --git a/java/com/google/gerrit/server/util/CommitMessageUtil.java b/java/com/google/gerrit/server/util/CommitMessageUtil.java
index fa55597..ef54715 100644
--- a/java/com/google/gerrit/server/util/CommitMessageUtil.java
+++ b/java/com/google/gerrit/server/util/CommitMessageUtil.java
@@ -14,11 +14,28 @@
package com.google.gerrit.server.util;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import com.google.common.base.Strings;
import com.google.gerrit.extensions.restapi.BadRequestException;
+import com.google.gerrit.reviewdb.client.Change;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
/** Utility functions to manipulate commit messages. */
public class CommitMessageUtil {
+ private static final SecureRandom rng;
+
+ static {
+ try {
+ rng = SecureRandom.getInstance("SHA1PRNG");
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("Cannot create RNG for Change-Id generator", e);
+ }
+ }
private CommitMessageUtil() {}
@@ -37,4 +54,18 @@
wellFormedMessage = wellFormedMessage + "\n";
return wellFormedMessage;
}
+
+ public static ObjectId generateChangeId() {
+ byte[] rand = new byte[Constants.OBJECT_ID_STRING_LENGTH];
+ rng.nextBytes(rand);
+ String randomString = new String(rand, UTF_8);
+
+ try (ObjectInserter f = new ObjectInserter.Formatter()) {
+ return f.idFor(Constants.OBJ_COMMIT, Constants.encode(randomString));
+ }
+ }
+
+ public static Change.Key generateKey() {
+ return new Change.Key("I" + generateChangeId().name());
+ }
}
diff --git a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
index 398ed84..2acc3cd 100644
--- a/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
+++ b/javatests/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -1440,14 +1440,7 @@
@Test
public void pushSameCommitTwice() throws Exception {
- try (ProjectConfigUpdate u = updateProject(project)) {
- u.getConfig()
- .getProject()
- .setBooleanConfig(
- BooleanProjectConfig.CREATE_NEW_CHANGE_FOR_ALL_NOT_IN_TARGET,
- InheritableBoolean.TRUE);
- u.save();
- }
+ enableCreateNewChangeForAllNotInTarget();
PushOneCommit push =
pushFactory.create(
@@ -1469,14 +1462,7 @@
@Test
public void pushSameCommitTwiceWhenIndexFailed() throws Exception {
- try (ProjectConfigUpdate u = updateProject(project)) {
- u.getConfig()
- .getProject()
- .setBooleanConfig(
- BooleanProjectConfig.CREATE_NEW_CHANGE_FOR_ALL_NOT_IN_TARGET,
- InheritableBoolean.TRUE);
- u.save();
- }
+ enableCreateNewChangeForAllNotInTarget();
PushOneCommit push =
pushFactory.create(
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
index 08a76e4..6622bae 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/AbstractSubmit.java
@@ -59,7 +59,6 @@
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.extensions.webui.UiAction;
import com.google.gerrit.reviewdb.client.Account;
-import com.google.gerrit.reviewdb.client.BooleanProjectConfig;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.PatchSet;
@@ -574,14 +573,7 @@
// |
// C0 -- Master
//
- try (ProjectConfigUpdate u = updateProject(project)) {
- u.getConfig()
- .getProject()
- .setBooleanConfig(
- BooleanProjectConfig.CREATE_NEW_CHANGE_FOR_ALL_NOT_IN_TARGET,
- InheritableBoolean.TRUE);
- u.save();
- }
+ enableCreateNewChangeForAllNotInTarget();
PushOneCommit push1 =
pushFactory.create(
diff --git a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
index e388d6c..a83bf7b 100644
--- a/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/change/CreateChangeIT.java
@@ -49,6 +49,12 @@
import com.google.gerrit.testing.TestTimeUtil;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.lib.Repository;
@@ -404,6 +410,42 @@
assertCreateSucceeds(in);
}
+ @Test
+ public void sha1sOfTwoNewChangesDiffer() throws Exception {
+ ChangeInput changeInput = newChangeInput(ChangeStatus.NEW);
+ ChangeInfo info1 = assertCreateSucceeds(changeInput);
+ ChangeInfo info2 = assertCreateSucceeds(changeInput);
+ assertThat(info1.currentRevision).isNotEqualTo(info2.currentRevision);
+ assertThat(info1.changeId).isNotEqualTo(info2.changeId);
+ }
+
+ @Test
+ public void sha1sOfTwoNewChangesDifferIfCreatedConcurrently() throws Exception {
+ ExecutorService executor = Executors.newFixedThreadPool(2);
+ try {
+ for (int i = 0; i < 10; i++) {
+ ChangeInput changeInput = newChangeInput(ChangeStatus.NEW);
+
+ CyclicBarrier sync = new CyclicBarrier(2);
+ Callable<ChangeInfo> createChange =
+ () -> {
+ setApiUser(admin);
+ sync.await();
+ return assertCreateSucceeds(changeInput);
+ };
+
+ Future<ChangeInfo> changeInfo1 = executor.submit(createChange);
+ Future<ChangeInfo> changeInfo2 = executor.submit(createChange);
+ assertThat(changeInfo1.get().currentRevision)
+ .isNotEqualTo(changeInfo2.get().currentRevision);
+ assertThat(changeInfo1.get().changeId).isNotEqualTo(changeInfo2.get().changeId);
+ }
+ } finally {
+ executor.shutdown();
+ executor.awaitTermination(5, TimeUnit.SECONDS);
+ }
+ }
+
private ChangeInput newChangeInput(ChangeStatus status) {
ChangeInput in = new ChangeInput();
in.project = project.get();
diff --git a/javatests/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java b/javatests/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java
index c249973..b9ed0f3 100644
--- a/javatests/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java
+++ b/javatests/com/google/gerrit/acceptance/server/notedb/OnlineNoteDbMigrationIT.java
@@ -35,6 +35,8 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedSet;
+import com.google.common.io.MoreFiles;
+import com.google.common.io.RecursiveDeleteOption;
import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.GerritConfig;
import com.google.gerrit.acceptance.NoHttpd;
@@ -84,6 +86,7 @@
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.RepositoryCache.FileKey;
import org.eclipse.jgit.storage.file.FileBasedConfig;
import org.eclipse.jgit.util.FS;
import org.junit.After;
@@ -511,6 +514,42 @@
}
@Test
+ public void fullMigrationOneChangeWithNoProject() throws Exception {
+ PushOneCommit.Result r1 = createChange();
+ Change.Id id1 = r1.getChange().getId();
+
+ Project.NameKey p2 = createProject("project2");
+ TestRepository<?> tr2 = cloneProject(p2, admin);
+ PushOneCommit.Result r2 = pushFactory.create(db, admin.getIdent(), tr2).to("refs/for/master");
+ Change.Id id2 = r2.getChange().getId();
+
+ // TODO(davido): Find an easier way to wipe out a repository from the file system.
+ MoreFiles.deleteRecursively(
+ FileKey.lenient(
+ sitePaths
+ .resolve(cfg.getString("gerrit", null, "basePath"))
+ .resolve(p2.get())
+ .toFile(),
+ FS.DETECTED)
+ .getFile()
+ .toPath(),
+ RecursiveDeleteOption.ALLOW_INSECURE);
+
+ migrate(b -> b);
+ assertNotesMigrationState(NOTE_DB, false, false);
+
+ try (ReviewDb db = schemaFactory.open();
+ Repository repo = repoManager.openRepository(project)) {
+ assertThat(repo.exactRef(RefNames.changeMetaRef(id1))).isNotNull();
+ assertThat(db.changes().get(id1).getNoteDbState()).isEqualTo(NOTE_DB_PRIMARY_STATE);
+ }
+
+ // A change without project is so corrupt that it is completely skipped by the migration
+ // process.
+ assertThat(db.changes().get(id2).getNoteDbState()).isNull();
+ }
+
+ @Test
public void fullMigrationMissingPatchSetRefs() throws Exception {
PushOneCommit.Result r = createChange();
Change.Id id = r.getChange().getId();
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html
index 6d413b7..19839a8 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-linked-text/gr-linked-text_test.html
@@ -50,6 +50,10 @@
match: '([Bb]ug|[Ii]ssue)\\s*#?(\\d+)',
link: 'https://bugs.chromium.org/p/gerrit/issues/detail?id=$2',
},
+ prefixsameinlinkandpattern: {
+ match: '([Hh][Tt][Tt][Pp]example)\\s*#?(\\d+)',
+ link: 'https://bugs.chromium.org/p/gerrit/issues/detail?id=$2',
+ },
changeid: {
match: '(I[0-9a-f]{8,40})',
link: '#/q/$1',
@@ -116,6 +120,18 @@
assert.equal(linkEl.textContent, 'Bug 3650');
});
+ test('Pattern with same prefix as link was correctly parsed', () => {
+ // Pattern starts with the same prefix (`http`) as the url.
+ element.content = 'httpexample 3650';
+
+ assert.equal(element.$.output.childNodes.length, 1);
+ const linkEl = element.$.output.childNodes[0];
+ const url = 'https://bugs.chromium.org/p/gerrit/issues/detail?id=3650';
+ assert.equal(linkEl.target, '_blank');
+ assert.equal(linkEl.href, url);
+ assert.equal(linkEl.textContent, 'httpexample 3650');
+ });
+
test('Change-Id pattern was parsed and linked', () => {
// "Change-Id:" pattern.
const changeID = 'I11d6a37f5e9b5df0486f6c922d8836dfa780e03e';
diff --git a/polygerrit-ui/app/elements/shared/gr-linked-text/link-text-parser.js b/polygerrit-ui/app/elements/shared/gr-linked-text/link-text-parser.js
index 23a71f9..027c632 100644
--- a/polygerrit-ui/app/elements/shared/gr-linked-text/link-text-parser.js
+++ b/polygerrit-ui/app/elements/shared/gr-linked-text/link-text-parser.js
@@ -312,14 +312,15 @@
let result = match[0].replace(pattern,
patterns[p].html || patterns[p].link);
- let i;
- // Skip portion of replacement string that is equal to original.
- for (i = 0; i < result.length; i++) {
- if (result[i] !== match[0][i]) { break; }
- }
- result = result.slice(i);
-
if (patterns[p].html) {
+ let i;
+ // Skip portion of replacement string that is equal to original to
+ // allow overlapping patterns.
+ for (i = 0; i < result.length; i++) {
+ if (result[i] !== match[0][i]) { break; }
+ }
+ result = result.slice(i);
+
this.addHTML(
result,
susbtrIndex + match.index + i,
@@ -329,8 +330,8 @@
this.addLink(
match[0],
result,
- susbtrIndex + match.index + i,
- match[0].length - i,
+ susbtrIndex + match.index,
+ match[0].length,
outputArray);
} else {
throw Error('linkconfig entry ' + p +
diff --git a/resources/com/google/gerrit/server/mime/mime-types.properties b/resources/com/google/gerrit/server/mime/mime-types.properties
index 7c94f32..84eef77 100644
--- a/resources/com/google/gerrit/server/mime/mime-types.properties
+++ b/resources/com/google/gerrit/server/mime/mime-types.properties
@@ -13,6 +13,7 @@
bzl = text/x-python
BUCK = text/x-python
BUILD = text/x-python
+BUILD.bazel = text/x-python
c = text/x-csrc
cfg = text/x-ttcn-cfg
cl = text/x-common-lisp
@@ -247,6 +248,7 @@
vtl = text/velocity
webidl = text/x-webidl
wsdl = application/xml
+WORKSPACE = text/x-python
xaml = application/xml
xhtml = text/html
xml = application/xml