Merge "Fix ignoring change when project is watched"
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index d4fa912..13fca66 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -1948,7 +1948,7 @@
--
Update star labels on a change. The star labels to be added/removed
-must be specified in the request body as link:#star-input[StarInput]
+must be specified in the request body as link:#stars-input[StarsInput]
entity. Starred changes are returned for the search query `has:stars`.
.Request
diff --git a/WORKSPACE b/WORKSPACE
index 4b8c90a..2a24c5a 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -130,22 +130,22 @@
sha1 = "cdb2dcb4e22b83d6b32b93095f644c3462739e82",
)
-load("//lib/jgit:jgit.bzl", "JGIT_VERS")
+load("//lib/jgit:jgit.bzl", "JGIT_VERS", "JGIT_REPO", "JGIT_SHA1", "JGIT_SRC_SHA1", "JGIT_SERVLET_SHA1", "JGIT_ARCHIVE_SHA1", "JGIT_JUNIT_SHA1")
maven_jar(
name = "jgit",
artifact = "org.eclipse.jgit:org.eclipse.jgit:" + JGIT_VERS,
- repository = GERRIT,
- sha1 = "a2b5970b853f8fee64589fc1103c0ceb7677ba63",
- src_sha1 = "765f955774c36c226aa41fba7c20119451de2db7",
+ repository = JGIT_REPO,
+ sha1 = JGIT_SHA1,
+ src_sha1 = JGIT_SRC_SHA1,
unsign = True,
)
maven_jar(
name = "jgit_servlet",
artifact = "org.eclipse.jgit:org.eclipse.jgit.http.server:" + JGIT_VERS,
- repository = GERRIT,
- sha1 = "d3aa54bd610db9a5c246aa8fef13989982c98628",
+ repository = JGIT_REPO,
+ sha1 = JGIT_SERVLET_SHA1,
unsign = True,
)
@@ -159,15 +159,15 @@
maven_jar(
name = "jgit_archive",
artifact = "org.eclipse.jgit:org.eclipse.jgit.archive:" + JGIT_VERS,
- repository = GERRIT,
- sha1 = "a728cf277396f1227c5a8dffcf5dee0188fc0821",
+ repository = JGIT_REPO,
+ sha1 = JGIT_ARCHIVE_SHA1,
)
maven_jar(
name = "jgit_junit",
artifact = "org.eclipse.jgit:org.eclipse.jgit.junit:" + JGIT_VERS,
- repository = GERRIT,
- sha1 = "6c2b2f192c95d25a2e1576aee5d1169dd8bd2266",
+ repository = JGIT_REPO,
+ sha1 = JGIT_JUNIT_SHA1,
unsign = True,
)
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
index a1bd747..31ca9df 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/git/AbstractPushForReview.java
@@ -44,6 +44,7 @@
import com.google.gerrit.extensions.client.InheritableBoolean;
import com.google.gerrit.extensions.client.ListChangesOption;
import com.google.gerrit.extensions.client.ProjectWatchInfo;
+import com.google.gerrit.extensions.client.ReviewerState;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.ChangeMessageInfo;
import com.google.gerrit.extensions.common.EditInfo;
@@ -505,6 +506,8 @@
cr = ci.labels.get("Code-Review");
assertThat(Iterables.getLast(ci.messages).message)
.isEqualTo("Uploaded patch set 2: Code-Review+2.");
+ // Check that the user who pushed the change was added as a reviewer since they added a vote
+ assertThatUserIsOnlyReviewer(ci, admin);
assertThat(cr.all).hasSize(1);
assertThat(cr.all.get(0).name).isEqualTo("Administrator");
@@ -524,6 +527,36 @@
assertThat(Iterables.getLast(ci.messages).message).isEqualTo("Uploaded patch set 3.");
}
+ @Test
+ public void pushNewPatchSetForMasterWithApprovals() throws Exception {
+ PushOneCommit.Result r = pushTo("refs/for/master");
+ r.assertOkStatus();
+
+ PushOneCommit push =
+ pushFactory.create(
+ db,
+ admin.getIdent(),
+ testRepo,
+ PushOneCommit.SUBJECT,
+ "b.txt",
+ "anotherContent",
+ r.getChangeId());
+ r = push.to("refs/for/master/%l=Code-Review+2");
+
+ ChangeInfo ci = get(r.getChangeId());
+ LabelInfo cr = ci.labels.get("Code-Review");
+ assertThat(Iterables.getLast(ci.messages).message)
+ .isEqualTo("Uploaded patch set 2: Code-Review+2.");
+
+ // Check that the user who pushed the new patch set was added as a reviewer since they added
+ // a vote
+ assertThatUserIsOnlyReviewer(ci, admin);
+
+ assertThat(cr.all).hasSize(1);
+ assertThat(cr.all.get(0).name).isEqualTo("Administrator");
+ assertThat(cr.all.get(0).value).isEqualTo(2);
+ }
+
/**
* There was a bug that allowed a user with Forge Committer Identity access right to upload a
* commit and put *votes on behalf of another user* on it. This test checks that this is not
@@ -566,6 +599,8 @@
assertThat(cr.all.get(indexUser).value.intValue()).isEqualTo(0);
assertThat(Iterables.getLast(ci.messages).message)
.isEqualTo("Uploaded patch set 1: Code-Review+1.");
+ // Check that the user who pushed the change was added as a reviewer since they added a vote
+ assertThatUserIsOnlyReviewer(ci, admin);
}
@Test
@@ -594,6 +629,8 @@
assertThat(cr.all).hasSize(1);
cr = ci.labels.get("Custom-Label");
assertThat(cr.all).hasSize(1);
+ // Check that the user who pushed the change was added as a reviewer since they added a vote
+ assertThatUserIsOnlyReviewer(ci, admin);
}
@Test
@@ -1366,6 +1403,13 @@
assertThat(info.status).isEqualTo(ChangeStatus.NEW);
}
+ private void assertThatUserIsOnlyReviewer(ChangeInfo ci, TestAccount reviewer) {
+ assertThat(ci.reviewers).isNotNull();
+ assertThat(ci.reviewers.keySet()).containsExactly(ReviewerState.REVIEWER);
+ assertThat(ci.reviewers.get(ReviewerState.REVIEWER).iterator().next().email)
+ .isEqualTo(reviewer.email);
+ }
+
private void pushWithReviewerInFooter(String nameEmail, TestAccount expectedReviewer)
throws Exception {
int n = 5;
diff --git a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/AuthInfo.java b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/AuthInfo.java
index 2f6ef79..c0ece8c 100644
--- a/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/AuthInfo.java
+++ b/gerrit-gwtui-common/src/main/java/com/google/gerrit/client/info/AuthInfo.java
@@ -67,8 +67,11 @@
public final List<AgreementInfo> contributorAgreements() {
List<AgreementInfo> agreements = new ArrayList<>();
- for (AgreementInfo a : Natives.asList(_contributorAgreements())) {
- agreements.add(a);
+ JsArray<AgreementInfo> contributorAgreements = _contributorAgreements();
+ if (contributorAgreements != null) {
+ for (AgreementInfo a : Natives.asList(contributorAgreements)) {
+ agreements.add(a);
+ }
}
return agreements;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteActive.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteActive.java
index 8710e91..d013120 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteActive.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteActive.java
@@ -25,12 +25,13 @@
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.DeleteActive.Input;
+import com.google.gwtorm.server.AtomicUpdate;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
-import java.util.Collections;
+import java.util.concurrent.atomic.AtomicBoolean;
@RequiresCapability(GlobalCapability.MODIFY_ACCOUNT)
@Singleton
@@ -52,18 +53,34 @@
@Override
public Response<?> apply(AccountResource rsrc, Input input)
throws RestApiException, OrmException, IOException {
- Account a = dbProvider.get().accounts().get(rsrc.getUser().getAccountId());
- if (a == null) {
- throw new ResourceNotFoundException("account not found");
- }
- if (!a.isActive()) {
- throw new ResourceConflictException("account not active");
- }
if (self.get() == rsrc.getUser()) {
throw new ResourceConflictException("cannot deactivate own account");
}
- a.setActive(false);
- dbProvider.get().accounts().update(Collections.singleton(a));
+
+ AtomicBoolean alreadyInactive = new AtomicBoolean(false);
+ Account a =
+ dbProvider
+ .get()
+ .accounts()
+ .atomicUpdate(
+ rsrc.getUser().getAccountId(),
+ new AtomicUpdate<Account>() {
+ @Override
+ public Account update(Account a) {
+ if (!a.isActive()) {
+ alreadyInactive.set(true);
+ } else {
+ a.setActive(false);
+ }
+ return a;
+ }
+ });
+ if (a == null) {
+ throw new ResourceNotFoundException("account not found");
+ }
+ if (alreadyInactive.get()) {
+ throw new ResourceConflictException("account not active");
+ }
byIdCache.evict(a.getId());
return Response.none();
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutActive.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutActive.java
index cbddefd..32c5345 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutActive.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutActive.java
@@ -22,12 +22,14 @@
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.account.PutActive.Input;
+import com.google.gwtorm.server.AtomicUpdate;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.Collections;
+import java.util.concurrent.atomic.AtomicBoolean;
@RequiresCapability(GlobalCapability.MODIFY_ACCOUNT)
@Singleton
@@ -46,16 +48,29 @@
@Override
public Response<String> apply(AccountResource rsrc, Input input)
throws ResourceNotFoundException, OrmException, IOException {
- Account a = dbProvider.get().accounts().get(rsrc.getUser().getAccountId());
+ AtomicBoolean alreadyActive = new AtomicBoolean(false);
+ Account a =
+ dbProvider
+ .get()
+ .accounts()
+ .atomicUpdate(
+ rsrc.getUser().getAccountId(),
+ new AtomicUpdate<Account>() {
+ @Override
+ public Account update(Account a) {
+ if (a.isActive()) {
+ alreadyActive.set(true);
+ } else {
+ a.setActive(true);
+ }
+ return a;
+ }
+ });
if (a == null) {
throw new ResourceNotFoundException("account not found");
}
- if (a.isActive()) {
- return Response.ok("");
- }
- a.setActive(true);
dbProvider.get().accounts().update(Collections.singleton(a));
byIdCache.evict(a.getId());
- return Response.created("");
+ return alreadyActive.get() ? Response.ok("") : Response.created("");
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutName.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutName.java
index 726508f..443a549 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutName.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutName.java
@@ -27,12 +27,12 @@
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.PutName.Input;
+import com.google.gwtorm.server.AtomicUpdate;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
-import java.util.Collections;
@Singleton
public class PutName implements RestModifyView<AccountResource, Input> {
@@ -77,12 +77,23 @@
throw new MethodNotAllowedException("realm does not allow editing name");
}
- Account a = dbProvider.get().accounts().get(user.getAccountId());
+ String newName = input.name;
+ Account a =
+ dbProvider
+ .get()
+ .accounts()
+ .atomicUpdate(
+ user.getAccountId(),
+ new AtomicUpdate<Account>() {
+ @Override
+ public Account update(Account a) {
+ a.setFullName(newName);
+ return a;
+ }
+ });
if (a == null) {
throw new ResourceNotFoundException("account not found");
}
- a.setFullName(input.name);
- dbProvider.get().accounts().update(Collections.singleton(a));
byIdCache.evict(a.getId());
return Strings.isNullOrEmpty(a.getFullName())
? Response.<String>none()
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutPreferred.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutPreferred.java
index 3c80d2c..ec60fb3 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutPreferred.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutPreferred.java
@@ -23,12 +23,14 @@
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.PutPreferred.Input;
+import com.google.gwtorm.server.AtomicUpdate;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
import java.util.Collections;
+import java.util.concurrent.atomic.AtomicBoolean;
@Singleton
public class PutPreferred implements RestModifyView<AccountResource.Email, Input> {
@@ -56,16 +58,29 @@
public Response<String> apply(IdentifiedUser user, String email)
throws ResourceNotFoundException, OrmException, IOException {
- Account a = dbProvider.get().accounts().get(user.getAccountId());
+ AtomicBoolean alreadyPreferred = new AtomicBoolean(false);
+ Account a =
+ dbProvider
+ .get()
+ .accounts()
+ .atomicUpdate(
+ user.getAccountId(),
+ new AtomicUpdate<Account>() {
+ @Override
+ public Account update(Account a) {
+ if (email.equals(a.getPreferredEmail())) {
+ alreadyPreferred.set(true);
+ } else {
+ a.setPreferredEmail(email);
+ }
+ return a;
+ }
+ });
if (a == null) {
throw new ResourceNotFoundException("account not found");
}
- if (email.equals(a.getPreferredEmail())) {
- return Response.ok("");
- }
- a.setPreferredEmail(email);
dbProvider.get().accounts().update(Collections.singleton(a));
byIdCache.evict(a.getId());
- return Response.created("");
+ return alreadyPreferred.get() ? Response.ok("") : Response.created("");
}
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutStatus.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutStatus.java
index d826899..ff541fd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutStatus.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutStatus.java
@@ -25,12 +25,12 @@
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.PutStatus.Input;
+import com.google.gwtorm.server.AtomicUpdate;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.io.IOException;
-import java.util.Collections;
@Singleton
public class PutStatus implements RestModifyView<AccountResource, Input> {
@@ -70,12 +70,23 @@
input = new Input();
}
- Account a = dbProvider.get().accounts().get(user.getAccountId());
+ String newStatus = input.status;
+ Account a =
+ dbProvider
+ .get()
+ .accounts()
+ .atomicUpdate(
+ user.getAccountId(),
+ new AtomicUpdate<Account>() {
+ @Override
+ public Account update(Account a) {
+ a.setStatus(Strings.nullToEmpty(newStatus));
+ return a;
+ }
+ });
if (a == null) {
throw new ResourceNotFoundException("account not found");
}
- a.setStatus(Strings.nullToEmpty(input.status));
- dbProvider.get().accounts().update(Collections.singleton(a));
byIdCache.evict(a.getId());
return Strings.isNullOrEmpty(a.getStatus()) ? Response.none() : Response.ok(a.getStatus());
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java
index 9983928..e929382 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java
@@ -17,6 +17,7 @@
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.gerrit.reviewdb.client.Change.INITIAL_PATCH_SET_ID;
+import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
import static java.util.stream.Collectors.toSet;
import com.google.common.base.MoreObjects;
@@ -390,6 +391,12 @@
Collections.<Account.Id>emptySet());
approvalsUtil.addApprovalsForNewPatchSet(
db, update, labelTypes, patchSet, ctx.getControl(), approvals);
+ // Check if approvals are changing in with this update. If so, add current user to reviewers.
+ // Note that this is done separately as addReviewers is filtering out the change owner as
+ // reviewer which is needed in several other code paths.
+ if (!approvals.isEmpty()) {
+ update.putReviewer(ctx.getAccountId(), REVIEWER);
+ }
if (message != null) {
changeMessage =
ChangeMessagesUtil.newMessage(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReplaceOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReplaceOp.java
index 3cd3c1e..a3bd97c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReplaceOp.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReplaceOp.java
@@ -17,6 +17,7 @@
import static com.google.gerrit.common.FooterConstants.CHANGE_ID;
import static com.google.gerrit.server.mail.MailUtil.getRecipientsFromFooters;
import static com.google.gerrit.server.mail.MailUtil.getRecipientsFromReviewers;
+import static com.google.gerrit.server.notedb.ReviewerStateInternal.REVIEWER;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
@@ -280,6 +281,14 @@
info,
recipients.getReviewers(),
oldRecipients.getAll());
+
+ // Check if approvals are changing in with this update. If so, add current user to reviewers.
+ // Note that this is done separately as addReviewers is filtering out the change owner as
+ // reviewer which is needed in several other code paths.
+ if (magicBranch != null && !magicBranch.labels.isEmpty()) {
+ update.putReviewer(ctx.getAccountId(), REVIEWER);
+ }
+
recipients.add(oldRecipients);
String approvalMessage =
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/OutgoingEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
index 2b90306..730b710 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/send/OutgoingEmail.java
@@ -147,23 +147,22 @@
//
removeUser(fromUser);
}
-
- // Check the preferences of all recipients. If any user has disabled
- // his email notifications then drop him from recipients' list.
- // In addition, check if users only want to receive plaintext email.
- for (Account.Id id : rcptTo) {
- Account thisUser = args.accountCache.get(id).getAccount();
- GeneralPreferencesInfo prefs = thisUser.getGeneralPreferencesInfo();
- if (prefs == null || prefs.getEmailStrategy() == DISABLED) {
- removeUser(thisUser);
- } else if (useHtml() && prefs.getEmailFormat() == EmailFormat.PLAINTEXT) {
- removeUser(thisUser);
- smtpRcptToPlaintextOnly.add(
- new Address(thisUser.getFullName(), thisUser.getPreferredEmail()));
- }
- if (smtpRcptTo.isEmpty() && smtpRcptToPlaintextOnly.isEmpty()) {
- return;
- }
+ }
+ // Check the preferences of all recipients. If any user has disabled
+ // his email notifications then drop him from recipients' list.
+ // In addition, check if users only want to receive plaintext email.
+ for (Account.Id id : rcptTo) {
+ Account thisUser = args.accountCache.get(id).getAccount();
+ GeneralPreferencesInfo prefs = thisUser.getGeneralPreferencesInfo();
+ if (prefs == null || prefs.getEmailStrategy() == DISABLED) {
+ removeUser(thisUser);
+ } else if (useHtml() && prefs.getEmailFormat() == EmailFormat.PLAINTEXT) {
+ removeUser(thisUser);
+ smtpRcptToPlaintextOnly.add(
+ new Address(thisUser.getFullName(), thisUser.getPreferredEmail()));
+ }
+ if (smtpRcptTo.isEmpty() && smtpRcptToPlaintextOnly.isEmpty()) {
+ return;
}
}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/ChangeArgumentParser.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/ChangeArgumentParser.java
index e956be3..4488c71 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/ChangeArgumentParser.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/ChangeArgumentParser.java
@@ -80,10 +80,13 @@
List<ChangeControl> matched =
useIndex ? changeFinder.find(id, currentUser) : changeFromNotesFactory(id, currentUser);
List<ChangeControl> toAdd = new ArrayList<>(changes.size());
+ boolean canMaintainServer =
+ currentUser.isIdentifiedUser()
+ && currentUser.asIdentifiedUser().getCapabilities().canMaintainServer();
for (ChangeControl ctl : matched) {
if (!changes.containsKey(ctl.getId())
&& inProject(projectControl, ctl.getProject())
- && ctl.isVisible(db)) {
+ && (canMaintainServer || ctl.isVisible(db))) {
toAdd.add(ctl);
}
}
diff --git a/lib/GUAVA_VERSION b/lib/GUAVA_VERSION
deleted file mode 100644
index b5f47b3..0000000
--- a/lib/GUAVA_VERSION
+++ /dev/null
@@ -1 +0,0 @@
-include_defs('//lib/guava.bzl')
diff --git a/lib/JGIT_VERSION b/lib/JGIT_VERSION
deleted file mode 100644
index 87a625f..0000000
--- a/lib/JGIT_VERSION
+++ /dev/null
@@ -1,4 +0,0 @@
-include_defs('//lib/jgit/jgit.bzl')
-include_defs('//lib/maven.defs')
-
-REPO = GERRIT # Leave here even if set to MAVEN_CENTRAL.
diff --git a/lib/jgit/jgit.bzl b/lib/jgit/jgit.bzl
index 99f0432..ac843d8 100644
--- a/lib/jgit/jgit.bzl
+++ b/lib/jgit/jgit.bzl
@@ -1,5 +1,19 @@
+load("//tools/bzl:maven_jar.bzl", "GERRIT", "MAVEN_LOCAL", "MAVEN_CENTRAL")
+
JGIT_VERS = "4.6.0.201612231935-r.30-gd3148f300"
DOC_VERS = "4.6.0.201612231935-r" # Set to JGIT_VERS unless using a snapshot
JGIT_DOC_URL = "http://download.eclipse.org/jgit/site/" + DOC_VERS + "/apidocs"
+
+JGIT_REPO = GERRIT # Leave here even if set to MAVEN_CENTRAL.
+
+JGIT_SHA1 = "a2b5970b853f8fee64589fc1103c0ceb7677ba63"
+
+JGIT_SRC_SHA1 = "765f955774c36c226aa41fba7c20119451de2db7"
+
+JGIT_SERVLET_SHA1 = "d3aa54bd610db9a5c246aa8fef13989982c98628"
+
+JGIT_ARCHIVE_SHA1 = "a728cf277396f1227c5a8dffcf5dee0188fc0821"
+
+JGIT_JUNIT_SHA1 = "6c2b2f192c95d25a2e1576aee5d1169dd8bd2266"
diff --git a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html
index acbb367..8e2b192 100644
--- a/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html
+++ b/polygerrit-ui/app/elements/change-list/gr-change-list-item/gr-change-list-item.html
@@ -141,7 +141,9 @@
</td>
<td class="cell updated"
hidden$="[[isColumnHidden('Updated', visibleChangeTableColumns)]]">
- <gr-date-formatter date-str="[[change.updated]]"></gr-date-formatter>
+ <gr-date-formatter
+ has-tooltip
+ date-str="[[change.updated]]"></gr-date-formatter>
</td>
<td class="cell size u-monospace"
hidden$="[[isColumnHidden('Size', visibleChangeTableColumns)]]">
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html
index 4e362e5..932b914 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html
@@ -99,6 +99,7 @@
<span class="title">Updated</span>
<span class="value">
<gr-date-formatter
+ has-tooltip
date-str="[[change.updated]]"></gr-date-formatter>
</span>
</section>
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html
index 3f5f64f..14361f4 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html
@@ -73,14 +73,14 @@
</div>
<span
id="messageControlsContainer"
- hidden$="[[_computeShowHideTextHidden(_visibleMessages.length, _processedMessages, _hideAutomated)]]">
+ hidden$="[[_computeShowHideTextHidden(_visibleMessages, _processedMessages, _hideAutomated, _visibleMessages.length)]]">
<gr-button id="oldMessagesBtn" link on-tap="_handleShowAllTap">
- [[_computeNumMessagesText(_visibleMessages.length, _processedMessages, _hideAutomated)]]
+ [[_computeNumMessagesText(_visibleMessages, _processedMessages, _hideAutomated, _visibleMessages.length)]]
</gr-button>
/
<gr-button id="incrementMessagesBtn" link
on-tap="_handleIncrementShownMessages">
- [[_computeIncrementText(_visibleMessages.length, _processedMessages, _hideAutomated)]]
+ [[_computeIncrementText(_visibleMessages, _processedMessages, _hideAutomated, _visibleMessages.length)]]
</gr-button>
</span>
<template
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js
index 0c3e126..0d58d96 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.js
@@ -255,9 +255,9 @@
* remaining in the list and the number of messages needed to display five
* more visible messages in the list.
*/
- _getDelta: function(numVisible, messages, hideAutomated) {
+ _getDelta: function(visibleMessages, messages, hideAutomated) {
var delta = MESSAGES_INCREMENT;
- var msgsRemaining = messages.length - numVisible;
+ var msgsRemaining = messages.length - visibleMessages.length;
if (hideAutomated) {
var counter = 0;
var i;
@@ -273,29 +273,34 @@
* Gets the number of messages that would be visible, but do not currently
* exist in _visibleMessages.
*/
- _numRemaining: function(numVisible, messages, hideAutomated) {
- var total = hideAutomated ?
- messages.filter(function(msg) {
- return !this._isAutomated(msg);
- }.bind(this)).length :
- messages.length;
- return total - numVisible;
+ _numRemaining: function(visibleMessages, messages, hideAutomated) {
+ if (hideAutomated) {
+ return this._getHumanMessages(messages).length -
+ this._getHumanMessages(visibleMessages).length;
+ }
+ return messages.length - visibleMessages.length;
},
- _computeIncrementText: function(numVisible, messages, hideAutomated) {
- var delta = this._getDelta(numVisible, messages, hideAutomated);
+ _computeIncrementText: function(visibleMessages, messages, hideAutomated) {
+ var delta = this._getDelta(visibleMessages, messages, hideAutomated);
delta = Math.min(
- this._numRemaining(numVisible, messages, hideAutomated), delta);
+ this._numRemaining(visibleMessages, messages, hideAutomated), delta);
return 'Show ' + Math.min(MESSAGES_INCREMENT, delta) + ' more';
},
- _computeShowHideTextHidden: function(numVisible, messages, hideAutomated) {
- if (numVisible >= messages.length) { return true; }
- if (!hideAutomated) {
- return numVisible >= messages.length;
+ _getHumanMessages: function(messages) {
+ return messages.filter(function(msg) {
+ return !this._isAutomated(msg);
+ }.bind(this));
+ },
+
+ _computeShowHideTextHidden: function(visibleMessages, messages,
+ hideAutomated) {
+ if (hideAutomated) {
+ messages = this._getHumanMessages(messages);
+ visibleMessages = this._getHumanMessages(visibleMessages);
}
- var hiddenMessages = messages.slice(0, messages.length - numVisible);
- return this._hasAutomatedMessages(hiddenMessages);
+ return visibleMessages.length >= messages.length;
},
_handleShowAllTap: function() {
@@ -304,9 +309,9 @@
},
_handleIncrementShownMessages: function() {
- var len = this._visibleMessages.length;
- var delta = this._getDelta(len, this._processedMessages,
+ var delta = this._getDelta(this._visibleMessages, this._processedMessages,
this._hideAutomated);
+ var len = this._visibleMessages.length;
var newMessages = this._processedMessages.slice(-(len + delta), -len);
// Add newMessages to the beginning of _visibleMessages
this.splice.apply(this, ['_visibleMessages', 0, 0].concat(newMessages));
@@ -317,8 +322,9 @@
this._visibleMessages = messages.slice(-MAX_INITIAL_SHOWN_MESSAGES);
},
- _computeNumMessagesText: function(numVisible, messages, hideAutomated) {
- var total = this._numRemaining(numVisible, messages, hideAutomated);
+ _computeNumMessagesText: function(visibleMessages, messages,
+ hideAutomated) {
+ var total = this._numRemaining(visibleMessages, messages, hideAutomated);
return total === 1 ? 'Show 1 message' : 'Show all ' + total + ' messages';
},
});
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.html b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.html
index b8acae3..7026d3c 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.html
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list_test.html
@@ -128,6 +128,20 @@
assert.isTrue(element.$.messageControlsContainer.hasAttribute('hidden'));
});
+ test('message count still respects non-automated on toggle', function() {
+ element.messages = _.times(10, randomMessage)
+ .concat(_.times(11, randomAutomated));
+ flushAsynchronousOperations();
+
+ assert.equal(element.$.oldMessagesBtn.innerText, 'Show 1 message');
+ assert.isFalse(element.$.messageControlsContainer.hasAttribute('hidden'));
+ MockInteractions.tap(element.$.automatedMessageToggle);
+ flushAsynchronousOperations();
+
+ assert.equal(element.$.oldMessagesBtn.innerText, 'Show 1 message');
+ assert.isFalse(element.$.messageControlsContainer.hasAttribute('hidden'));
+ });
+
test('show all messages respects expand', function() {
element.messages = _.times(10, randomAutomated)
.concat(_.times(11, randomMessage));
@@ -450,19 +464,34 @@
test('_getDelta', function() {
var messages = [randomMessage()];
- assert.equal(element._getDelta(0, messages, false), 1);
- assert.equal(element._getDelta(0, messages, true), 1);
+ assert.equal(element._getDelta([], messages, false), 1);
+ assert.equal(element._getDelta([], messages, true), 1);
messages = _.times(7, randomMessage);
- assert.equal(element._getDelta(0, messages, false), 5);
- assert.equal(element._getDelta(0, messages, true), 5);
+ assert.equal(element._getDelta([], messages, false), 5);
+ assert.equal(element._getDelta([], messages, true), 5);
messages = _.times(4, randomMessage)
.concat(_.times(2, randomAutomated))
.concat(_.times(3, randomMessage));
- assert.equal(element._getDelta(2, messages, false), 5);
- assert.equal(element._getDelta(2, messages, true), 7);
+
+ var dummyArr = _.times(2, randomMessage);
+ assert.equal(element._getDelta(dummyArr, messages, false), 5);
+ assert.equal(element._getDelta(dummyArr, messages, true), 7);
+ });
+
+ test('_getHumanMessages', function() {
+ assert.equal(
+ element._getHumanMessages(_.times(5, randomAutomated)).length, 0);
+ assert.equal(
+ element._getHumanMessages(_.times(5, randomMessage)).length, 5);
+
+ var messages = _.shuffle(_.times(5, randomMessage)
+ .concat(_.times(5, randomAutomated)));
+ messages = element._getHumanMessages(messages);
+ assert.equal(messages.length, 5);
+ assert.isFalse(element._hasAutomatedMessages(messages));
});
});
</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html b/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html
index e3a3327..749ca12 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-comment/gr-diff-comment.html
@@ -189,7 +189,9 @@
<span class="collapsedContent">[[comment.message]]</span>
</div>
<a class="date" href$="[[_computeLinkToComment(comment)]]" on-tap="_handleLinkTap">
- <gr-date-formatter date-str="[[comment.updated]]"></gr-date-formatter>
+ <gr-date-formatter
+ has-tooltip
+ date-str="[[comment.updated]]"></gr-date-formatter>
</a>
<div class="show-hide">
<label class="show-hide">
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js
index c135caa..e40ccf3 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-cursor/gr-diff-cursor.js
@@ -127,7 +127,10 @@
},
moveToNextChunk: function() {
- this.$.cursorManager.next(this._isFirstRowOfChunk.bind(this));
+ this.$.cursorManager.next(this._isFirstRowOfChunk.bind(this),
+ function(target) {
+ return target.parentNode.scrollHeight;
+ });
this._fixSide();
},
diff --git a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.html b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.html
index 1ff1f96..defcf18 100644
--- a/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.html
+++ b/polygerrit-ui/app/elements/settings/gr-account-info/gr-account-info.html
@@ -37,6 +37,7 @@
<span class="title">Registered</span>
<span class="value">
<gr-date-formatter
+ has-tooltip
date-str="[[_account.registered_on]]"></gr-date-formatter>
</span>
</section>
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html
index c3542ba..54aafb8 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html
@@ -157,8 +157,8 @@
<select
is="gr-select"
bind-value="{{_localPrefs.time_format}}">
- <option value="HHMM_12">4:10 PM (PST)</option>
- <option value="HHMM_24">16:10 (PST)</option>
+ <option value="HHMM_12">4:10 PM</option>
+ <option value="HHMM_24">16:10</option>
</select>
</span>
</section>
diff --git a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.js b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.js
index 63a1a7d..9bfdcfb 100644
--- a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.js
+++ b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager.js
@@ -35,6 +35,10 @@
notify: true,
observer: '_scrollToTarget',
},
+ /**
+ * The height of content intended to be included with the target.
+ */
+ _targetHeight: Number,
/**
* The index of the current target (if any). -1 otherwise.
@@ -76,8 +80,8 @@
this.unsetCursor();
},
- next: function(opt_condition) {
- this._moveCursor(1, opt_condition);
+ next: function(opt_condition, opt_getTargetHeight) {
+ this._moveCursor(1, opt_condition, opt_getTargetHeight);
},
previous: function(opt_condition) {
@@ -109,6 +113,7 @@
this._unDecorateTarget();
this.index = -1;
this.target = null;
+ this._targetHeight = null;
},
isAtStart: function() {
@@ -136,9 +141,12 @@
* @param {Function} opt_condition Optional stop condition. If a condition
* is passed the cursor will continue to move in the specified direction
* until the condition is met.
+ * @param {Function} opt_getTargetHeight Optional function to calculate the
+ * height of the target's 'section'. The height of the target itself is
+ * sometimes different, used by the diff cursor.
* @private
*/
- _moveCursor: function(delta, opt_condition) {
+ _moveCursor: function(delta, opt_condition, opt_getTargetHeight) {
if (!this.stops.length) {
this.unsetCursor();
return;
@@ -153,6 +161,12 @@
newTarget = this.stops[newIndex];
}
+ if (opt_getTargetHeight) {
+ this._targetHeight = opt_getTargetHeight(newTarget);
+ } else {
+ this._targetHeight = newTarget.scrollHeight;
+ }
+
this.index = newIndex;
this.target = newTarget;
@@ -245,22 +259,35 @@
top < window.pageYOffset + window.innerHeight;
},
+ _calculateScrollToValue: function(top, target) {
+ return top - (window.innerHeight / 3) + (target.offsetHeight / 2);
+ },
+
_scrollToTarget: function() {
if (!this.target || this.scrollBehavior === ScrollBehavior.NEVER) {
return;
}
var top = this._getTop(this.target);
+ var bottomIsVisible = this._targetHeight ?
+ this._targetIsVisible(top + this._targetHeight) : true;
+ var scrollToValue = this._calculateScrollToValue(top, this.target);
+
if (this._targetIsVisible(top)) {
- return;
+ // Don't scroll if either the bottom is visible or if the position that
+ // would get scrolled to is higher up than the current position. this
+ // woulld cause less of the target content to be displayed than is
+ // already.
+ if (bottomIsVisible || scrollToValue < window.scrollY) {
+ return;
+ }
}
// Scroll the element to the middle of the window. Dividing by a third
// instead of half the inner height feels a bit better otherwise the
// element appears to be below the center of the window even when it
// isn't.
- window.scrollTo(0, top - (window.innerHeight / 3) +
- (this.target.offsetHeight / 2));
+ window.scrollTo(0, scrollToValue);
},
});
})();
diff --git a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_test.html b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_test.html
index 0f48501..d7496c7 100644
--- a/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-cursor-manager/gr-cursor-manager_test.html
@@ -125,6 +125,23 @@
assert.equal(element.index, -1);
});
+
+ test('_moveCursor', function() {
+ // Initialize the cursor with its stops.
+ element.stops = list.querySelectorAll('li');
+ // Select the first stop.
+ element.setCursor(list.children[0]);
+ var getTargetHeight = sinon.stub();
+
+ // Move the cursor without an optional get target height function.
+ element._moveCursor(1);
+ assert.isFalse(getTargetHeight.called);
+
+ // Move the cursor with an optional get target height function.
+ element._moveCursor(1, null, getTargetHeight);
+ assert.isTrue(getTargetHeight.called);
+ });
+
test('opt_noScroll', function() {
sandbox.stub(element, '_targetIsVisible', function() { return false; });
var scrollStub = sandbox.stub(window, 'scrollTo');
@@ -182,5 +199,63 @@
element.next();
assert.isTrue(element.target.focus.called);
});
+
+ suite('_scrollToTarget', function() {
+ var scrollStub;
+ setup(function() {
+ element.stops = list.querySelectorAll('li');
+ element.scrollBehavior = 'keep-visible';
+
+ // There is a target which has a targetNext
+ element.setCursor(list.children[0]);
+ element._moveCursor(1);
+ scrollStub = sandbox.stub(window, 'scrollTo');
+ window.innerHeight = 60;
+ });
+
+ test('Called when top and bottom not visible', function() {
+ sandbox.stub(element, '_targetIsVisible', function() {
+ return false;
+ });
+ element._scrollToTarget();
+ assert.isTrue(scrollStub.called);
+ });
+
+ test('Not called when top and bottom visible', function() {
+ sandbox.stub(element, '_targetIsVisible', function() {
+ return true;
+ });
+ element._scrollToTarget();
+ assert.isFalse(scrollStub.called);
+ });
+
+ test('Called when top is visible, bottom is not, and scroll is lower',
+ function() {
+ var visibleStub = sandbox.stub(element, '_targetIsVisible', function() {
+ return visibleStub.callCount == 2;
+ });
+ window.scrollY = 15;
+ sandbox.stub(element, '_calculateScrollToValue', function() {
+ return 20;
+ });
+ element._scrollToTarget();
+ assert.isTrue(scrollStub.called);
+ assert.equal(visibleStub.callCount, 2);
+ });
+
+ test('Called when top is visible, bottom is not, and scroll is higher',
+ function() {
+ var visibleStub = sandbox.stub(element, '_targetIsVisible', function() {
+ return visibleStub.callCount == 2;
+ });
+ window.scrollY = 25;
+ sandbox.stub(element, '_calculateScrollToValue', function() {
+ return 20;
+ });
+ element._scrollToTarget();
+ assert.isFalse(scrollStub.called);
+ assert.equal(visibleStub.callCount, 2);
+ });
+ });
});
</script>
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js
index fe0dad0..6f4cd3d 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.js
@@ -65,10 +65,6 @@
this._loadPreferences();
},
- _getTzString: function() {
- return ' ' + new Date().toString().split(' ').pop();
- },
-
_getUtcOffsetString: function() {
return ' UTC' + moment().format('Z');
},
@@ -150,7 +146,7 @@
var now = new Date();
var format = TimeFormats.MONTH_DAY_YEAR;
if (this._isWithinDay(now, date)) {
- return date.format(timeFormat) + this._getTzString();
+ format = timeFormat;
} else if (this._isWithinHalfYear(now, date)) {
format = TimeFormats.MONTH_DAY;
}
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html
index 43aea17..5955cc5 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter_test.html
@@ -82,7 +82,6 @@
{time_format: 'HHMM_24', relative_date_in_change_table: false}
).then(function() {
element = fixture('basic');
- sandbox.stub(element, '_getTzString').returns('');
sandbox.stub(element, '_getUtcOffsetString').returns('');
return element._loadPreferences();
});
@@ -125,7 +124,6 @@
{time_format: 'HHMM_12'}
).then(function() {
element = fixture('basic');
- sandbox.stub(element, '_getTzString').returns('');
sandbox.stub(element, '_getUtcOffsetString').returns('');
return element._loadPreferences();
});
@@ -144,7 +142,6 @@
{time_format: 'HHMM_12', relative_date_in_change_table: true}
).then(function() {
element = fixture('basic');
- sandbox.stub(element, '_getTzString').returns('');
sandbox.stub(element, '_getUtcOffsetString').returns('');
return element._loadPreferences();
});
@@ -169,7 +166,6 @@
{time_format: 'HHMM_12', relative_date_in_change_table: true}
).then(function() {
element = fixture('basic');
- sandbox.stub(element, '_getTzString').returns('');
return element._loadPreferences();
});
});
@@ -184,7 +180,6 @@
setup(function() {
return stubRestAPI(null).then(function() {
element = fixture('basic');
- sandbox.stub(element, '_getTzString').returns('');
return element._loadPreferences();
});
});