Add lists of all open, merged, abandoned changes on the server
To make pagination relatively efficient on the server side we use a new "sort_key" column in the changes table. This is a hex string comprised of the pair lastUpdatedOn,changeId, creating a unique key we can position in the index on. Queries operate by performing a range scan on the index, making them quite simple to execute. Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
@@ -267,7 +267,7 @@ public class Gerrit implements EntryPoint {
|
||||
if (isSignedIn()) {
|
||||
History.newItem(Link.MINE);
|
||||
} else {
|
||||
History.newItem(Link.ALL);
|
||||
History.newItem(Link.ALL_OPEN);
|
||||
}
|
||||
} else {
|
||||
History.fireCurrentHistoryState();
|
||||
@@ -298,8 +298,9 @@ public class Gerrit implements EntryPoint {
|
||||
MenuBar m;
|
||||
|
||||
m = new MenuBar(true);
|
||||
addLink(m, C.menuAllRecentChanges(), Link.ALL);
|
||||
addLink(m, C.menuAllUnclaimedChanges(), Link.ALL_UNCLAIMED);
|
||||
addLink(m, C.menuAllOpen(), Link.ALL_OPEN);
|
||||
addLink(m, C.menuAllMerged(), Link.ALL_MERGED);
|
||||
addLink(m, C.menuAllAbandoned(), Link.ALL_ABANDONED);
|
||||
menuBar.addItem(C.menuAll(), m);
|
||||
|
||||
if (signedIn) {
|
||||
|
@@ -32,8 +32,9 @@ public interface GerritConstants extends Constants {
|
||||
String nameAlreadyUsedBody();
|
||||
|
||||
String menuAll();
|
||||
String menuAllUnclaimedChanges();
|
||||
String menuAllRecentChanges();
|
||||
String menuAllOpen();
|
||||
String menuAllMerged();
|
||||
String menuAllAbandoned();
|
||||
|
||||
String menuMine();
|
||||
String menuMyChanges();
|
||||
|
@@ -13,8 +13,9 @@ notFoundBody = The page you requested was not found.
|
||||
nameAlreadyUsedBody = The name is already in use.
|
||||
|
||||
menuAll = All
|
||||
menuAllUnclaimedChanges = Unclaimed Changes
|
||||
menuAllRecentChanges = Recent Changes
|
||||
menuAllOpen = Open
|
||||
menuAllMerged = Merged
|
||||
menuAllAbandoned = Abandoned
|
||||
|
||||
menuMine = My
|
||||
menuMyChanges = Changes
|
||||
|
@@ -22,6 +22,9 @@ import com.google.gerrit.client.admin.GroupListScreen;
|
||||
import com.google.gerrit.client.admin.ProjectAdminScreen;
|
||||
import com.google.gerrit.client.admin.ProjectListScreen;
|
||||
import com.google.gerrit.client.changes.AccountDashboardScreen;
|
||||
import com.google.gerrit.client.changes.AllAbandonedChangesScreen;
|
||||
import com.google.gerrit.client.changes.AllMergedChangesScreen;
|
||||
import com.google.gerrit.client.changes.AllOpenChangesScreen;
|
||||
import com.google.gerrit.client.changes.ChangeScreen;
|
||||
import com.google.gerrit.client.changes.MineDraftsScreen;
|
||||
import com.google.gerrit.client.changes.MineStarredScreen;
|
||||
@@ -54,8 +57,9 @@ public class Link implements HistoryListener {
|
||||
public static final String MINE_STARRED = "mine,starred";
|
||||
public static final String MINE_DRAFTS = "mine,drafts";
|
||||
|
||||
public static final String ALL = "all";
|
||||
public static final String ALL_OPEN = "all,open";
|
||||
public static final String ALL_ABANDONED = "all,abandoned,n,z";
|
||||
public static final String ALL_MERGED = "all,merged,n,z";
|
||||
public static final String ALL_OPEN = "all,open,n,z";
|
||||
public static final String ALL_UNCLAIMED = "all,unclaimed";
|
||||
|
||||
public static final String ADMIN_PEOPLE = "admin,people";
|
||||
@@ -140,6 +144,23 @@ public class Link implements HistoryListener {
|
||||
}
|
||||
}
|
||||
|
||||
if (token.startsWith("all,")) {
|
||||
p = "all,abandoned,";
|
||||
if (token.startsWith(p)) {
|
||||
return new AllAbandonedChangesScreen(skip(p, token));
|
||||
}
|
||||
|
||||
p = "all,merged,";
|
||||
if (token.startsWith(p)) {
|
||||
return new AllMergedChangesScreen(skip(p, token));
|
||||
}
|
||||
|
||||
p = "all,open,";
|
||||
if (token.startsWith(p)) {
|
||||
return new AllOpenChangesScreen(skip(p, token));
|
||||
}
|
||||
}
|
||||
|
||||
if (token.startsWith("patch,")) {
|
||||
p = "patch,sidebyside,";
|
||||
if (token.startsWith(p))
|
||||
|
@@ -0,0 +1,46 @@
|
||||
// Copyright 2008 Google Inc.
|
||||
//
|
||||
// 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.client.changes;
|
||||
|
||||
import com.google.gerrit.client.data.SingleListChangeInfo;
|
||||
import com.google.gerrit.client.reviewdb.Change;
|
||||
import com.google.gerrit.client.rpc.GerritCallback;
|
||||
|
||||
|
||||
public class AllAbandonedChangesScreen extends AllSingleListScreen {
|
||||
public AllAbandonedChangesScreen(final String positionToken) {
|
||||
super(Util.C.allAbandonedChanges(), "all,abandoned", positionToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadPrev() {
|
||||
Util.LIST_SVC.allClosedPrev(Change.Status.ABANDONED, pos, pageSize,
|
||||
new GerritCallback<SingleListChangeInfo>() {
|
||||
public void onSuccess(final SingleListChangeInfo result) {
|
||||
display(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadNext() {
|
||||
Util.LIST_SVC.allClosedNext(Change.Status.ABANDONED, pos, pageSize,
|
||||
new GerritCallback<SingleListChangeInfo>() {
|
||||
public void onSuccess(final SingleListChangeInfo result) {
|
||||
display(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
// Copyright 2008 Google Inc.
|
||||
//
|
||||
// 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.client.changes;
|
||||
|
||||
import com.google.gerrit.client.data.SingleListChangeInfo;
|
||||
import com.google.gerrit.client.reviewdb.Change;
|
||||
import com.google.gerrit.client.rpc.GerritCallback;
|
||||
|
||||
|
||||
public class AllMergedChangesScreen extends AllSingleListScreen {
|
||||
public AllMergedChangesScreen(final String positionToken) {
|
||||
super(Util.C.allMergedChanges(), "all,merged", positionToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadPrev() {
|
||||
Util.LIST_SVC.allClosedPrev(Change.Status.MERGED, pos, pageSize,
|
||||
new GerritCallback<SingleListChangeInfo>() {
|
||||
public void onSuccess(final SingleListChangeInfo result) {
|
||||
display(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadNext() {
|
||||
Util.LIST_SVC.allClosedNext(Change.Status.MERGED, pos, pageSize,
|
||||
new GerritCallback<SingleListChangeInfo>() {
|
||||
public void onSuccess(final SingleListChangeInfo result) {
|
||||
display(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
// Copyright 2008 Google Inc.
|
||||
//
|
||||
// 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.client.changes;
|
||||
|
||||
import com.google.gerrit.client.data.SingleListChangeInfo;
|
||||
import com.google.gerrit.client.rpc.GerritCallback;
|
||||
|
||||
|
||||
public class AllOpenChangesScreen extends AllSingleListScreen {
|
||||
public AllOpenChangesScreen(final String positionToken) {
|
||||
super(Util.C.allOpenChanges(), "all,open", positionToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadPrev() {
|
||||
Util.LIST_SVC.allOpenPrev(pos, pageSize,
|
||||
new GerritCallback<SingleListChangeInfo>() {
|
||||
public void onSuccess(final SingleListChangeInfo result) {
|
||||
display(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadNext() {
|
||||
Util.LIST_SVC.allOpenNext(pos, pageSize,
|
||||
new GerritCallback<SingleListChangeInfo>() {
|
||||
public void onSuccess(final SingleListChangeInfo result) {
|
||||
display(result);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@@ -0,0 +1,121 @@
|
||||
// Copyright 2008 Google Inc.
|
||||
//
|
||||
// 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.client.changes;
|
||||
|
||||
import com.google.gerrit.client.data.ChangeInfo;
|
||||
import com.google.gerrit.client.data.SingleListChangeInfo;
|
||||
import com.google.gerrit.client.ui.Screen;
|
||||
import com.google.gwt.user.client.ui.HorizontalPanel;
|
||||
import com.google.gwt.user.client.ui.Hyperlink;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public abstract class AllSingleListScreen extends Screen {
|
||||
protected static final String MIN_SORTKEY = "";
|
||||
protected static final String MAX_SORTKEY = "z";
|
||||
|
||||
protected static final int pageSize = 25;
|
||||
private ChangeTable table;
|
||||
private ChangeTable.Section section;
|
||||
protected Hyperlink prev;
|
||||
protected Hyperlink next;
|
||||
protected List<ChangeInfo> changes;
|
||||
|
||||
protected final String anchorPrefix;
|
||||
protected boolean useLoadPrev;
|
||||
protected String pos;
|
||||
|
||||
public AllSingleListScreen(final String title, final String anchorToken,
|
||||
final String positionToken) {
|
||||
super(title);
|
||||
anchorPrefix = anchorToken;
|
||||
useLoadPrev = positionToken.startsWith("p,");
|
||||
pos = positionToken.substring(2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getScreenCacheToken() {
|
||||
return anchorPrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Screen recycleThis(final Screen newScreen) {
|
||||
final AllSingleListScreen o = (AllSingleListScreen) newScreen;
|
||||
useLoadPrev = o.useLoadPrev;
|
||||
pos = o.pos;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
if (table == null) {
|
||||
table = new ChangeTable();
|
||||
section = new ChangeTable.Section();
|
||||
|
||||
table.addSection(section);
|
||||
table.setSavePointerId(anchorPrefix);
|
||||
add(table);
|
||||
|
||||
prev = new Hyperlink(Util.C.pagedChangeListPrev(), true, "");
|
||||
prev.setVisible(false);
|
||||
|
||||
next = new Hyperlink(Util.C.pagedChangeListNext(), true, "");
|
||||
next.setVisible(false);
|
||||
|
||||
final HorizontalPanel buttons = new HorizontalPanel();
|
||||
buttons.setStyleName("gerrit-ChangeTable-PrevNextLinks");
|
||||
buttons.add(prev);
|
||||
buttons.add(next);
|
||||
add(buttons);
|
||||
}
|
||||
|
||||
super.onLoad();
|
||||
|
||||
if (useLoadPrev) {
|
||||
loadPrev();
|
||||
} else {
|
||||
loadNext();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void loadPrev();
|
||||
|
||||
protected abstract void loadNext();
|
||||
|
||||
protected void display(final SingleListChangeInfo result) {
|
||||
changes = result.getChanges();
|
||||
|
||||
if (!changes.isEmpty()) {
|
||||
final ChangeInfo f = changes.get(0);
|
||||
final ChangeInfo l = changes.get(changes.size() - 1);
|
||||
|
||||
prev.setTargetHistoryToken(anchorPrefix + ",p," + f.getSortKey());
|
||||
next.setTargetHistoryToken(anchorPrefix + ",n," + l.getSortKey());
|
||||
|
||||
if (useLoadPrev) {
|
||||
prev.setVisible(!result.isAtEnd());
|
||||
next.setVisible(!MIN_SORTKEY.equals(pos));
|
||||
} else {
|
||||
prev.setVisible(!MAX_SORTKEY.equals(pos));
|
||||
next.setVisible(!result.isAtEnd());
|
||||
}
|
||||
}
|
||||
|
||||
table.setAccountInfoCache(result.getAccounts());
|
||||
section.display(result.getChanges());
|
||||
table.finishDisplay(true);
|
||||
}
|
||||
}
|
@@ -26,6 +26,9 @@ public interface ChangeConstants extends Constants {
|
||||
|
||||
String starredHeading();
|
||||
String draftsHeading();
|
||||
String allOpenChanges();
|
||||
String allAbandonedChanges();
|
||||
String allMergedChanges();
|
||||
|
||||
String changeTableColumnID();
|
||||
String changeTableColumnSubject();
|
||||
@@ -72,4 +75,7 @@ public interface ChangeConstants extends Constants {
|
||||
String buttonPublishCommentsCancel();
|
||||
String headingCoverMessage();
|
||||
String headingPatchComments();
|
||||
|
||||
String pagedChangeListPrev();
|
||||
String pagedChangeListNext();
|
||||
}
|
||||
|
@@ -6,6 +6,9 @@ statusLongAbandoned = Abandoned
|
||||
starredHeading = Starred Changes
|
||||
draftsHeading = Changes with unpublished drafts
|
||||
changesRecentlyClosed = Recently closed changes
|
||||
allOpenChanges = All open changes
|
||||
allAbandonedChanges = All abandoned changes
|
||||
allMergedChanges = All merged changes
|
||||
|
||||
changeTableColumnID = ID
|
||||
changeTableColumnSubject = Subject
|
||||
@@ -52,3 +55,6 @@ buttonPublishCommentsSend = Publish Comments
|
||||
buttonPublishCommentsCancel = Cancel
|
||||
headingCoverMessage = Cover Message:
|
||||
headingPatchComments = Patch Comments:
|
||||
|
||||
pagedChangeListPrev = ⇦Prev
|
||||
pagedChangeListNext = Next⇨
|
||||
|
@@ -26,6 +26,22 @@ import com.google.gwtjsonrpc.client.VoidResult;
|
||||
import java.util.Set;
|
||||
|
||||
public interface ChangeListService extends RemoteJsonService {
|
||||
/** Get all open changes more recent than pos, fetching at most limit rows. */
|
||||
void allOpenPrev(String pos, int limit,
|
||||
AsyncCallback<SingleListChangeInfo> callback);
|
||||
|
||||
/** Get all open changes older than pos, fetching at most limit rows. */
|
||||
void allOpenNext(String pos, int limit,
|
||||
AsyncCallback<SingleListChangeInfo> callback);
|
||||
|
||||
/** Get all closed changes more recent than pos, fetching at most limit rows. */
|
||||
void allClosedPrev(Change.Status status, String pos, int limit,
|
||||
AsyncCallback<SingleListChangeInfo> callback);
|
||||
|
||||
/** Get all closed changes older than pos, fetching at most limit rows. */
|
||||
void allClosedNext(Change.Status status, String pos, int limit,
|
||||
AsyncCallback<SingleListChangeInfo> callback);
|
||||
|
||||
/** Get the data to show {@link AccountDashboardScreen} for an account. */
|
||||
void forAccount(Account.Id id, AsyncCallback<AccountDashboardInfo> callback);
|
||||
|
||||
|
@@ -43,6 +43,56 @@ import java.util.Set;
|
||||
|
||||
public class ChangeListServiceImpl extends BaseServiceImplementation implements
|
||||
ChangeListService {
|
||||
private static final int MAX_PER_PAGE = 50;
|
||||
|
||||
private static int safePageSize(final int pageSize) {
|
||||
return 0 < pageSize && pageSize <= MAX_PER_PAGE ? pageSize : MAX_PER_PAGE;
|
||||
}
|
||||
|
||||
public void allOpenPrev(final String pos, final int pageSize,
|
||||
final AsyncCallback<SingleListChangeInfo> callback) {
|
||||
run(callback, new QueryPrev(pageSize, pos) {
|
||||
@Override
|
||||
ResultSet<Change> query(ReviewDb db, int slim, String sortKey)
|
||||
throws OrmException {
|
||||
return db.changes().allOpenPrev(sortKey, slim);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void allOpenNext(final String pos, final int pageSize,
|
||||
final AsyncCallback<SingleListChangeInfo> callback) {
|
||||
run(callback, new QueryNext(pageSize, pos) {
|
||||
@Override
|
||||
ResultSet<Change> query(ReviewDb db, int slim, String sortKey)
|
||||
throws OrmException {
|
||||
return db.changes().allOpenNext(sortKey, slim);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void allClosedPrev(final Change.Status s, final String pos,
|
||||
final int pageSize, final AsyncCallback<SingleListChangeInfo> callback) {
|
||||
run(callback, new QueryPrev(pageSize, pos) {
|
||||
@Override
|
||||
ResultSet<Change> query(ReviewDb db, int lim, String key)
|
||||
throws OrmException {
|
||||
return db.changes().allClosedPrev(s.getCode(), key, lim);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void allClosedNext(final Change.Status s, final String pos,
|
||||
final int pageSize, final AsyncCallback<SingleListChangeInfo> callback) {
|
||||
run(callback, new QueryNext(pageSize, pos) {
|
||||
@Override
|
||||
ResultSet<Change> query(ReviewDb db, int lim, String key)
|
||||
throws OrmException {
|
||||
return db.changes().allClosedNext(s.getCode(), key, lim);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void forAccount(final Account.Id id,
|
||||
final AsyncCallback<AccountDashboardInfo> callback) {
|
||||
final Account.Id me = Common.getAccountId();
|
||||
@@ -77,15 +127,9 @@ public class ChangeListServiceImpl extends BaseServiceImplementation implements
|
||||
public void myStarredChanges(
|
||||
final AsyncCallback<SingleListChangeInfo> callback) {
|
||||
run(callback, new Action<SingleListChangeInfo>() {
|
||||
public SingleListChangeInfo run(final ReviewDb db)
|
||||
throws OrmException, Failure {
|
||||
public SingleListChangeInfo run(final ReviewDb db) throws OrmException {
|
||||
final Account.Id me = Common.getAccountId();
|
||||
final AccountInfoCacheFactory ac = new AccountInfoCacheFactory(db);
|
||||
final Account user = ac.get(me);
|
||||
if (user == null) {
|
||||
throw new Failure(new NoSuchEntityException());
|
||||
}
|
||||
|
||||
final SingleListChangeInfo d = new SingleListChangeInfo();
|
||||
final Set<Change.Id> starred = starredBy(db, me);
|
||||
d.setChanges(filter(db.changes().get(starred), starred, ac));
|
||||
@@ -100,18 +144,11 @@ public class ChangeListServiceImpl extends BaseServiceImplementation implements
|
||||
});
|
||||
}
|
||||
|
||||
public void myDraftChanges(
|
||||
final AsyncCallback<SingleListChangeInfo> callback) {
|
||||
public void myDraftChanges(final AsyncCallback<SingleListChangeInfo> callback) {
|
||||
run(callback, new Action<SingleListChangeInfo>() {
|
||||
public SingleListChangeInfo run(final ReviewDb db)
|
||||
throws OrmException, Failure {
|
||||
public SingleListChangeInfo run(final ReviewDb db) throws OrmException {
|
||||
final Account.Id me = Common.getAccountId();
|
||||
final AccountInfoCacheFactory ac = new AccountInfoCacheFactory(db);
|
||||
final Account user = ac.get(me);
|
||||
if (user == null) {
|
||||
throw new Failure(new NoSuchEntityException());
|
||||
}
|
||||
|
||||
final SingleListChangeInfo d = new SingleListChangeInfo();
|
||||
final Set<Change.Id> starred = starredBy(db, me);
|
||||
final Set<Change.Id> drafted = draftedBy(db, me);
|
||||
@@ -207,4 +244,73 @@ public class ChangeListServiceImpl extends BaseServiceImplementation implements
|
||||
}
|
||||
return existing;
|
||||
}
|
||||
|
||||
private abstract class QueryNext implements Action<SingleListChangeInfo> {
|
||||
protected final String pos;
|
||||
protected final int limit;
|
||||
protected final int slim;
|
||||
|
||||
QueryNext(final int pageSize, final String pos) {
|
||||
this.pos = pos;
|
||||
this.limit = safePageSize(pageSize);
|
||||
this.slim = limit + 1;
|
||||
}
|
||||
|
||||
public SingleListChangeInfo run(final ReviewDb db) throws OrmException {
|
||||
final Account.Id me = Common.getAccountId();
|
||||
final AccountInfoCacheFactory ac = new AccountInfoCacheFactory(db);
|
||||
final SingleListChangeInfo d = new SingleListChangeInfo();
|
||||
final Set<Change.Id> starred = starredBy(db, me);
|
||||
|
||||
boolean results = true;
|
||||
String sortKey = pos;
|
||||
final ArrayList<ChangeInfo> list = new ArrayList<ChangeInfo>();
|
||||
while (results && list.size() < slim) {
|
||||
results = false;
|
||||
final ResultSet<Change> rs = query(db, slim, sortKey);
|
||||
for (final Change c : rs) {
|
||||
results = true;
|
||||
if (canRead(c)) {
|
||||
final ChangeInfo ci = new ChangeInfo(c, ac);
|
||||
ci.setStarred(starred.contains(ci.getId()));
|
||||
list.add(ci);
|
||||
if (list.size() == slim) {
|
||||
rs.close();
|
||||
break;
|
||||
}
|
||||
}
|
||||
sortKey = c.getSortKey();
|
||||
}
|
||||
}
|
||||
|
||||
final boolean atEnd = finish(list);
|
||||
d.setChanges(list, atEnd);
|
||||
d.setAccounts(ac.create());
|
||||
return d;
|
||||
}
|
||||
|
||||
boolean finish(final ArrayList<ChangeInfo> list) {
|
||||
final boolean atEnd = list.size() <= limit;
|
||||
if (list.size() == slim) {
|
||||
list.remove(limit);
|
||||
}
|
||||
return atEnd;
|
||||
}
|
||||
|
||||
abstract ResultSet<Change> query(final ReviewDb db, final int slim,
|
||||
String sortKey) throws OrmException;
|
||||
}
|
||||
|
||||
private abstract class QueryPrev extends QueryNext {
|
||||
QueryPrev(int pageSize, String pos) {
|
||||
super(pageSize, pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean finish(final ArrayList<ChangeInfo> list) {
|
||||
final boolean atEnd = super.finish(list);
|
||||
Collections.reverse(list);
|
||||
return atEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -18,7 +18,6 @@ import com.google.gerrit.client.reviewdb.Account;
|
||||
import com.google.gerrit.client.reviewdb.Change;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Date;
|
||||
|
||||
public class ChangeInfo {
|
||||
protected Change.Id id;
|
||||
@@ -28,6 +27,7 @@ public class ChangeInfo {
|
||||
protected ProjectInfo project;
|
||||
protected boolean starred;
|
||||
protected Timestamp lastUpdatedOn;
|
||||
protected String sortKey;
|
||||
|
||||
protected ChangeInfo() {
|
||||
}
|
||||
@@ -39,6 +39,7 @@ public class ChangeInfo {
|
||||
status = c.getStatus();
|
||||
project = new ProjectInfo(c.getDest().getParentKey());
|
||||
lastUpdatedOn = c.getLastUpdatedOn();
|
||||
sortKey = c.getSortKey();
|
||||
|
||||
acc.want(owner);
|
||||
}
|
||||
@@ -71,7 +72,11 @@ public class ChangeInfo {
|
||||
starred = s;
|
||||
}
|
||||
|
||||
public Date getLastUpdatedOn() {
|
||||
public java.sql.Timestamp getLastUpdatedOn() {
|
||||
return lastUpdatedOn;
|
||||
}
|
||||
|
||||
public String getSortKey() {
|
||||
return sortKey;
|
||||
}
|
||||
}
|
||||
|
@@ -20,6 +20,7 @@ import java.util.List;
|
||||
public class SingleListChangeInfo {
|
||||
protected AccountInfoCache accounts;
|
||||
protected List<ChangeInfo> changes;
|
||||
protected boolean atEnd;
|
||||
|
||||
public SingleListChangeInfo() {
|
||||
}
|
||||
@@ -36,7 +37,16 @@ public class SingleListChangeInfo {
|
||||
return changes;
|
||||
}
|
||||
|
||||
public boolean isAtEnd() {
|
||||
return atEnd;
|
||||
}
|
||||
|
||||
public void setChanges(List<ChangeInfo> c) {
|
||||
setChanges(c, true);
|
||||
}
|
||||
|
||||
public void setChanges(List<ChangeInfo> c, boolean end) {
|
||||
changes = c;
|
||||
atEnd = end;
|
||||
}
|
||||
}
|
||||
|
@@ -118,6 +118,10 @@ public final class Change {
|
||||
@Column
|
||||
protected Timestamp lastUpdatedOn;
|
||||
|
||||
/** A {@link #lastUpdatedOn} ASC,{@link #changeId} ASC for sorting. */
|
||||
@Column(length = 16)
|
||||
protected String sortKey;
|
||||
|
||||
@Column(name = "owner_account_id")
|
||||
protected Account.Id owner;
|
||||
|
||||
@@ -174,10 +178,18 @@ public final class Change {
|
||||
return lastUpdatedOn;
|
||||
}
|
||||
|
||||
public void updated() {
|
||||
public void resetLastUpdatedOn() {
|
||||
lastUpdatedOn = new Timestamp(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
public String getSortKey() {
|
||||
return sortKey;
|
||||
}
|
||||
|
||||
public void setSortKey(final String newSortKey) {
|
||||
sortKey = newSortKey;
|
||||
}
|
||||
|
||||
public Account.Id getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
@@ -33,4 +33,18 @@ public interface ChangeAccess extends Access<Change, Change.Id> {
|
||||
@Query("WHERE dest = ? AND status = '" + Change.STATUS_SUBMITTED
|
||||
+ "' ORDER BY lastUpdatedOn")
|
||||
ResultSet<Change> submitted(Branch.NameKey dest) throws OrmException;
|
||||
|
||||
@Query("WHERE open = true AND sortKey > ? ORDER BY sortKey LIMIT ?")
|
||||
ResultSet<Change> allOpenPrev(String sortKey, int limit) throws OrmException;
|
||||
|
||||
@Query("WHERE open = true AND sortKey < ? ORDER BY sortKey DESC LIMIT ?")
|
||||
ResultSet<Change> allOpenNext(String sortKey, int limit) throws OrmException;
|
||||
|
||||
@Query("WHERE open = false AND status = ? AND sortKey > ? ORDER BY sortKey LIMIT ?")
|
||||
ResultSet<Change> allClosedPrev(char status, String sortKey, int limit)
|
||||
throws OrmException;
|
||||
|
||||
@Query("WHERE open = false AND status = ? AND sortKey < ? ORDER BY sortKey DESC LIMIT ?")
|
||||
ResultSet<Change> allClosedNext(char status, String sortKey, int limit)
|
||||
throws OrmException;
|
||||
}
|
||||
|
@@ -55,7 +55,7 @@ public class Screen extends FlowPanel {
|
||||
/** Invoked if this screen is the current screen and the user signs out. */
|
||||
public void onSignOut() {
|
||||
if (isRequiresSignIn()) {
|
||||
History.newItem(Link.ALL);
|
||||
History.newItem(Link.ALL_OPEN);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -457,7 +457,7 @@ public class MergeOp {
|
||||
final PatchSet.Id merged = c.currentPatchSetId();
|
||||
for (int attempts = 0; attempts < 10; attempts++) {
|
||||
c.setStatus(Change.Status.MERGED);
|
||||
c.updated();
|
||||
ChangeUtil.updated(c);
|
||||
try {
|
||||
final Transaction txn = schema.beginTransaction();
|
||||
schema.changes().update(Collections.singleton(c), txn);
|
||||
@@ -484,7 +484,7 @@ public class MergeOp {
|
||||
private void setNew(Change c, ChangeMessage msg) {
|
||||
for (int attempts = 0; attempts < 10; attempts++) {
|
||||
c.setStatus(Change.Status.NEW);
|
||||
c.updated();
|
||||
ChangeUtil.updated(c);
|
||||
try {
|
||||
final Transaction txn = schema.beginTransaction();
|
||||
schema.changes().update(Collections.singleton(c), txn);
|
||||
|
@@ -27,6 +27,7 @@ import com.google.gerrit.client.reviewdb.SystemConfig;
|
||||
import com.google.gerrit.client.rpc.Common;
|
||||
import com.google.gerrit.git.InvalidRepositoryException;
|
||||
import com.google.gerrit.git.PatchSetImporter;
|
||||
import com.google.gerrit.server.ChangeUtil;
|
||||
import com.google.gerrit.server.GerritServer;
|
||||
import com.google.gwtjsonrpc.server.XsrfException;
|
||||
import com.google.gwtorm.client.OrmException;
|
||||
@@ -107,6 +108,25 @@ public class ImportGerrit1 {
|
||||
srcs.close();
|
||||
pm.endTask();
|
||||
|
||||
// Compute the sort keys for change entities
|
||||
//
|
||||
srcs = query.executeQuery("SELECT change_id FROM changes");
|
||||
final ArrayList<Change.Id> changesToDo = new ArrayList<Change.Id>();
|
||||
while (srcs.next()) {
|
||||
final Change.Id changeId = new Change.Id(srcs.getInt(1));
|
||||
changesToDo.add(changeId);
|
||||
}
|
||||
srcs.close();
|
||||
pm.start(1);
|
||||
pm.beginTask("Compute sort keys", changesToDo.size());
|
||||
for (final Change.Id changeId : changesToDo) {
|
||||
final Change c = db.changes().get(changeId);
|
||||
ChangeUtil.computeSortKey(c);
|
||||
db.changes().update(Collections.singleton(c));
|
||||
pm.update(1);
|
||||
}
|
||||
pm.endTask();
|
||||
|
||||
// Rebuild the cached PatchSet information directly from Git.
|
||||
// There's some oddities in the Gerrit 1 data that we got from
|
||||
// Google App Engine's data store; the quickest way to fix it
|
||||
|
@@ -264,6 +264,19 @@
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
.gerrit-ChangeTable-PrevNextLinks {
|
||||
float: right;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.gerrit-ChangeTable-PrevNextLinks td {
|
||||
width: 5em;
|
||||
text-align: right;
|
||||
}
|
||||
.gerrit-ChangeTable-PrevNextLinks .gwt-Hyperlink {
|
||||
font-size: 9pt;
|
||||
color: #2a5db0;
|
||||
}
|
||||
|
||||
|
||||
/** PatchContentTable **/
|
||||
.gerrit-PatchContentTable {
|
||||
|
@@ -108,7 +108,7 @@ public class ChangeManageServiceImpl extends BaseServiceImplementation
|
||||
if (ApprovalCategory.SUBMIT.equals(actionType.getCategory().getId())) {
|
||||
if (change.getStatus() == Change.Status.NEW) {
|
||||
change.setStatus(Change.Status.SUBMITTED);
|
||||
change.updated();
|
||||
ChangeUtil.updated(change);
|
||||
}
|
||||
} else {
|
||||
throw new Failure(new IllegalArgumentException(actionType
|
||||
|
@@ -14,6 +14,7 @@
|
||||
|
||||
package com.google.gerrit.server;
|
||||
|
||||
import com.google.gerrit.client.reviewdb.Change;
|
||||
import com.google.gerrit.client.reviewdb.ReviewDb;
|
||||
import com.google.gwtorm.client.OrmException;
|
||||
|
||||
@@ -47,4 +48,37 @@ public class ChangeUtil {
|
||||
NB.encodeInt32(raw, 0, uuidPrefix);
|
||||
NB.encodeInt32(raw, 4, uuidSeq--);
|
||||
}
|
||||
|
||||
public static void updated(final Change c) {
|
||||
c.resetLastUpdatedOn();
|
||||
computeSortKey(c);
|
||||
}
|
||||
|
||||
public static void computeSortKey(final Change c) {
|
||||
// The encoding uses minutes since Wed Oct 1 00:00:00 2008 UTC.
|
||||
// We overrun approximately 4,085 years later, so ~6093.
|
||||
//
|
||||
final long lastUpdatedOn =
|
||||
(c.getLastUpdatedOn().getTime() / 1000L) - 1222819200L;
|
||||
final StringBuilder r = new StringBuilder(16);
|
||||
r.setLength(16);
|
||||
formatHexInt(r, 0, (int) (lastUpdatedOn / 60));
|
||||
formatHexInt(r, 8, c.getId().get());
|
||||
c.setSortKey(r.toString());
|
||||
}
|
||||
|
||||
private static final char[] hexchar =
|
||||
{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', //
|
||||
'a', 'b', 'c', 'd', 'e', 'f'};
|
||||
|
||||
private static void formatHexInt(final StringBuilder dst, final int p, int w) {
|
||||
int o = p + 7;
|
||||
while (o >= p && w != 0) {
|
||||
dst.setCharAt(o--, hexchar[w & 0xf]);
|
||||
w >>>= 4;
|
||||
}
|
||||
while (o >= p) {
|
||||
dst.setCharAt(o--, '0');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -60,8 +60,8 @@ public class UrlRewriteFilter implements Filter {
|
||||
staticLinks.put("/unclaimed", Link.MINE_UNCLAIMED);
|
||||
staticLinks.put("/starred", Link.MINE_STARRED);
|
||||
|
||||
staticLinks.put("/all", Link.ALL);
|
||||
staticLinks.put("/all_unclaimed", Link.ALL_UNCLAIMED);
|
||||
staticLinks.put("/all", Link.ALL_MERGED);
|
||||
staticLinks.put("/open", Link.ALL_OPEN);
|
||||
|
||||
staticExtensions = new HashSet<String>();
|
||||
|
@@ -390,7 +390,7 @@ public class PatchDetailServiceImpl extends BaseServiceImplementation implements
|
||||
db.changeMessages().insert(Collections.singleton(r.message), txn);
|
||||
}
|
||||
|
||||
r.change.updated();
|
||||
ChangeUtil.updated(r.change);
|
||||
db.changes().update(Collections.singleton(r.change), txn);
|
||||
return r;
|
||||
}
|
||||
|
@@ -580,7 +580,7 @@ class Receive extends AbstractGitCommand {
|
||||
|
||||
change.setStatus(Change.Status.NEW);
|
||||
change.setCurrentPatchSet(imp.getPatchSetInfo());
|
||||
change.updated();
|
||||
ChangeUtil.updated(change);
|
||||
db.changes().update(Collections.singleton(change), txn);
|
||||
return ps;
|
||||
}
|
||||
|
Reference in New Issue
Block a user