Merge "Plugin API for project"
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 1ceb3dc..1bc14c4 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -1725,6 +1725,9 @@
If enabled, an NCSA combined log format request log file is written
out by the internal HTTP daemon.
+
+`log4j.appender` with the name `httpd_log` can be configured to overwrite
+programmatic configuration.
++
By default, true if httpd.listenUrl uses http:// or https://,
and false if httpd.listenUrl uses proxy-http:// or proxy-https://.
@@ -2711,6 +2714,9 @@
Enable (or disable) the `'$site_path'/logs/sshd_log` request log.
If enabled, a request log file is written out by the SSH daemon.
+
+`log4j.appender` with the name `sshd_log` can be configured to overwrite
+programmatic configuration.
++
By default, true.
[[suggest]] Section suggest
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index d1c79ac..95f960f 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -364,6 +364,10 @@
+
Project deletion
+* `com.google.gerrit.extensions.events.HeadUpdatedListener`:
++
+Update of HEAD on a project
+
[[stream-events]]
Sending Events to the Events Stream
-----------------------------------
diff --git a/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/DefaultCacheFactory.java b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/DefaultCacheFactory.java
index 4478cc9..1e627bf 100644
--- a/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/DefaultCacheFactory.java
+++ b/gerrit-cache-h2/src/main/java/com/google/gerrit/server/cache/h2/DefaultCacheFactory.java
@@ -113,8 +113,7 @@
@SuppressWarnings({"rawtypes", "unchecked"})
private static <K, V> CacheBuilder<K, V> newCacheBuilder() {
- CacheBuilder builder = CacheBuilder.newBuilder();
- return builder;
+ return (CacheBuilder<K, V>) CacheBuilder.newBuilder();
}
private static <K, V> Weigher<K, V> unitWeight() {
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java b/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
index 9e64aae..bd5816a 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
@@ -70,6 +70,10 @@
return "/admin/projects/" + p.get() + ",access";
}
+ public static String toProjectBranches(Project.NameKey p) {
+ return "/admin/projects/" + p.get() + ",branches";
+ }
+
public static String toAccountQuery(String fullname, Status status) {
return toChangeQuery(op("owner", fullname) + " " + status(status), TOP);
}
diff --git a/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/HeadUpdatedListener.java b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/HeadUpdatedListener.java
new file mode 100644
index 0000000..5961d6f
--- /dev/null
+++ b/gerrit-extension-api/src/main/java/com/google/gerrit/extensions/events/HeadUpdatedListener.java
@@ -0,0 +1,29 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.extensions.events;
+
+import com.google.gerrit.extensions.annotations.ExtensionPoint;
+
+/** Notified whenever the HEAD of a project is updated. */
+@ExtensionPoint
+public interface HeadUpdatedListener {
+ public interface Event {
+ String getProjectName();
+ String getOldHeadName();
+ String getNewHeadName();
+ }
+
+ void onHeadUpdated(Event event);
+}
diff --git a/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/linker/server/Permutation.java b/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/linker/server/Permutation.java
index b319db1..87979cc 100644
--- a/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/linker/server/Permutation.java
+++ b/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/linker/server/Permutation.java
@@ -55,15 +55,14 @@
*/
public void inject(Document dom) {
String moduleName = selector.getModuleName();
- String moduleFunc = moduleName;
StringBuilder s = new StringBuilder();
s.append("\n");
- s.append("function " + moduleFunc + "(){");
+ s.append("function ").append(moduleName).append("(){");
s.append("var s,l,t");
s.append(",w=window");
s.append(",d=document");
- s.append(",n='" + moduleName + "'");
+ s.append(",n='").append(moduleName).append("'");
s.append(",f=d.createElement('iframe')");
s.append(";");
@@ -78,7 +77,7 @@
s.append("i.src=n+'/clear.cache.gif';");
s.append("b=i.src;");
s.append("b=b.substring(0,b.lastIndexOf('/')+1);");
- s.append(moduleFunc + "=null;"); // allow us to GC
+ s.append(moduleName).append("=null;"); // allow us to GC
s.append("f.contentWindow.gwtOnLoad(undefined,n,b);");
s.append("}");
s.append("}");
@@ -87,14 +86,14 @@
// exact name here is known to the IFrameLinker and is called by
// the code in the iframe.
//
- s.append(moduleFunc + ".onScriptLoad=function(){");
+ s.append(moduleName).append(".onScriptLoad=function(){");
s.append("s=1;m();");
s.append("};");
// Set l true when the browser has finished processing the iframe
// tag, and everything else on the page.
//
- s.append(moduleFunc + ".r=function(){");
+ s.append(moduleName).append(".r=function(){");
s.append("l=1;m();");
s.append("};");
@@ -110,14 +109,13 @@
// refresh quirks in Safari. We have to use the location.replace trick to
// avoid FF2 refresh quirks.
//
- s.append("f.contentWindow.location.replace(n+'/" + cacheHTML + "');");
+ s.append("f.contentWindow.location.replace(n+'/").append(cacheHTML).append("');");
// defer attribute here is to workaround IE running immediately.
//
- s.append("d.write('<script defer=\"defer\">" //
- + moduleFunc + ".r()</'+'script>');");
+ s.append("d.write('<script defer=\"defer\">").append(moduleName).append(".r()</'+'script>');");
s.append("}");
- s.append(moduleFunc + "();");
+ s.append(moduleName).append("();");
s.append("\n//");
final Element html = dom.getDocumentElement();
diff --git a/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/client/LinkFindReplace.java b/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/client/LinkFindReplace.java
index 7e32ff0..bddd33a 100644
--- a/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/client/LinkFindReplace.java
+++ b/gerrit-gwtexpui/src/main/java/com/google/gwtexpui/safehtml/client/LinkFindReplace.java
@@ -68,13 +68,12 @@
throw new IllegalArgumentException(
"Invalid scheme (" + toString() + "): " + href);
}
- String result = new SafeHtmlBuilder()
+ return new SafeHtmlBuilder()
.openAnchor()
.setAttribute("href", href)
.append(SafeHtml.asis(input))
.closeAnchor()
.asString();
- return result;
}
@Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java
index 9842049..5bc15d3 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/GerritCss.java
@@ -106,6 +106,7 @@
String downloadLinkListCell();
String downloadLink_Active();
String drafts();
+ String editHeadButton();
String emptySection();
String errorDialog();
String errorDialogButtons();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/RelativeDateFormatter.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/RelativeDateFormatter.java
index 7b6009a..5fc8cb3 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/RelativeDateFormatter.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/RelativeDateFormatter.java
@@ -133,12 +133,10 @@
}
private static long upperLimit(long unit) {
- long limit = unit + unit / 2;
- return limit;
+ return unit + unit / 2;
}
private static long round(long n, long unit) {
- long rounded = (n + unit / 2) / unit;
- return rounded;
+ return (n + unit / 2) / unit;
}
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
index 7affd1c..2f26ad9 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
@@ -92,6 +92,8 @@
String initialRevision();
String buttonAddBranch();
String buttonDeleteBranch();
+ String saveHeadButton();
+ String cancelHeadButton();
String groupItemHelp();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
index fa1dc87..f06d657 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
@@ -71,6 +71,8 @@
initialRevision = Initial Revision
buttonAddBranch = Create Branch
buttonDeleteBranch = Delete
+saveHeadButton = Save
+cancelHeadButton = Cancel
groupItemHelp = group
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.java
index 567f14c..02a19dc8 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.java
@@ -112,7 +112,7 @@
parentProject.setTargetHistoryToken( //
Dispatcher.toProjectAdmin(parent, ProjectScreen.ACCESS));
- parentProjectBox.setVisible(editing && value.canChangeParent());
+ parentProjectBox.setVisible(editing);
parentProjectBox.setProject(value.getProjectName());
parentProjectBox.setParentProject(value.getInheritsFrom());
parentProject.setVisible(!parentProjectBox.isVisible());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java
index 04d407e..b7c82fb 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectBranchesScreen.java
@@ -25,10 +25,13 @@
import com.google.gerrit.client.projects.BranchInfo;
import com.google.gerrit.client.projects.ProjectApi;
import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.rpc.NativeString;
import com.google.gerrit.client.rpc.Natives;
import com.google.gerrit.client.rpc.ScreenLoadCallback;
import com.google.gerrit.client.ui.FancyFlexTable;
import com.google.gerrit.client.ui.HintTextBox;
+import com.google.gerrit.client.ui.OnEditEnabler;
+import com.google.gerrit.common.PageLinks;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gwt.core.client.JsArray;
@@ -47,8 +50,11 @@
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.Image;
+import com.google.gwt.user.client.ui.InlineLabel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
+import com.google.gwtexpui.globalkey.client.NpTextBox;
import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
import java.util.Comparator;
@@ -371,7 +377,11 @@
table.setText(row, 2, k.getShortName());
if (k.revision() != null) {
- table.setText(row, 3, k.revision());
+ if ("HEAD".equals(k.getShortName())) {
+ setHeadRevision(row, 3, k.revision());
+ } else {
+ table.setText(row, 3, k.revision());
+ }
} else {
table.setText(row, 3, "");
}
@@ -400,6 +410,92 @@
setRowItem(row, k);
}
+ private void setHeadRevision(final int row, final int column,
+ final String rev) {
+ AccessMap.get(getProjectKey(),
+ new GerritCallback<ProjectAccessInfo>() {
+ @Override
+ public void onSuccess(ProjectAccessInfo result) {
+ if (result.isOwner()) {
+ table.setWidget(row, column, getHeadRevisionWidget(rev));
+ } else {
+ table.setText(row, 3, rev);
+ }
+ }
+ });
+ }
+
+ private Widget getHeadRevisionWidget(final String headRevision) {
+ FlowPanel p = new FlowPanel();
+ final InlineLabel l = new InlineLabel(headRevision);
+ final Image edit = new Image(Gerrit.RESOURCES.edit());
+ edit.addStyleName(Gerrit.RESOURCES.css().editHeadButton());
+
+ final NpTextBox input = new NpTextBox();
+ input.setVisibleLength(35);
+ input.setValue(headRevision);
+ input.setVisible(false);
+ final Button save = new Button();
+ save.setText(Util.C.saveHeadButton());
+ save.setVisible(false);
+ save.setEnabled(false);
+ final Button cancel = new Button();
+ cancel.setText(Util.C.cancelHeadButton());
+ cancel.setVisible(false);
+
+ OnEditEnabler e = new OnEditEnabler(save);
+ e.listenTo(input);
+
+ edit.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ l.setVisible(false);
+ edit.setVisible(false);
+ input.setVisible(true);
+ save.setVisible(true);
+ cancel.setVisible(true);
+ }
+ });
+ save.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ save.setEnabled(false);
+ ProjectApi.setHead(getProjectKey(), input.getValue().trim(),
+ new GerritCallback<NativeString>() {
+ @Override
+ public void onSuccess(NativeString result) {
+ Gerrit.display(PageLinks.toProjectBranches(getProjectKey()));
+ }
+
+ @Override
+ public void onFailure(Throwable caught) {
+ super.onFailure(caught);
+ save.setEnabled(true);
+ }
+ });
+ }
+ });
+ cancel.addClickHandler(new ClickHandler() {
+ @Override
+ public void onClick(ClickEvent event) {
+ l.setVisible(true);
+ edit.setVisible(true);
+ input.setVisible(false);
+ input.setValue(headRevision);
+ save.setVisible(false);
+ save.setEnabled(false);
+ cancel.setVisible(false);
+ }
+ });
+
+ p.add(l);
+ p.add(edit);
+ p.add(input);
+ p.add(save);
+ p.add(cancel);
+ return p;
+ }
+
boolean hasBranchCanDelete() {
return canDelete;
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java
index a1c7a0c..5c3784c 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.java
@@ -55,7 +55,6 @@
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JsArray;
import com.google.gwt.core.client.JsArrayString;
-import com.google.gwt.dom.client.AnchorElement;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.ClickEvent;
@@ -70,15 +69,16 @@
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.EventListener;
import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.ToggleButton;
-import com.google.gwtexpui.clippy.client.CopyableLabel;
import com.google.gwtexpui.globalkey.client.GlobalKey;
import com.google.gwtexpui.globalkey.client.KeyCommand;
import com.google.gwtexpui.globalkey.client.KeyCommandSet;
+import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
import com.google.gwtorm.client.KeyUtil;
import java.sql.Timestamp;
@@ -128,13 +128,12 @@
@UiField HTMLPanel headerLine;
@UiField Style style;
+ @UiField Element commitSubjectText;
@UiField ToggleButton star;
- @UiField Reload reload;
- @UiField AnchorElement permalink;
+ @UiField Anchor permalink;
@UiField Element reviewersText;
@UiField Reviewers reviewers;
- @UiField Element changeIdText;
@UiField Element ownerText;
@UiField Element statusText;
@UiField Image projectQuery;
@@ -142,7 +141,6 @@
@UiField InlineHyperlink branchLink;
@UiField Element submitActionText;
@UiField Element notMergeable;
- @UiField CopyableLabel idText;
@UiField Topic topic;
@UiField Element actionText;
@UiField Element actionDate;
@@ -236,7 +234,7 @@
keysNavigation.add(new KeyCommand(0, 'R', Util.C.keyReloadChange()) {
@Override
public void onKeyPress(final KeyPressEvent event) {
- reload.reload();
+ Gerrit.display(PageLinks.toChange(changeId));
}
});
@@ -412,6 +410,12 @@
onReply();
}
+ @UiHandler("permalink")
+ void onReload(ClickEvent e) {
+ e.preventDefault();
+ Gerrit.display(PageLinks.toChange(changeId));
+ }
+
private void onReply() {
if (Gerrit.isSignedIn()) {
replyAction.onReply();
@@ -650,6 +654,7 @@
statusText.setInnerText(Util.toLongString(info.status()));
}
+ renderCommitSubject(info);
renderOwner(info);
renderActionTextDate(info);
renderHistory(info);
@@ -662,10 +667,7 @@
star.setValue(info.starred());
permalink.setHref(ChangeLink.permalink(changeId));
- changeIdText.setInnerText(String.valueOf(info.legacy_id()));
- idText.setText("Change-Id: " + info.change_id());
- idText.setPreviewText(info.change_id());
- reload.set(info);
+ permalink.setText(String.valueOf(info.legacy_id()));
topic.set(info, revision);
commit.set(commentLinkProcessor, info, revision);
related.set(info, revision);
@@ -697,6 +699,13 @@
setWindowTitle(sb.toString());
}
+ private void renderCommitSubject(ChangeInfo info) {
+ RevisionInfo rev = info.revision(revision);
+ String sub = rev.commit().subject();
+ commitSubjectText.setInnerSafeHtml(commentLinkProcessor.apply(
+ new SafeHtmlBuilder().append(sub).linkify()));
+ }
+
private void renderOwner(ChangeInfo info) {
// TODO info card hover
String name = info.owner().name() != null
@@ -755,7 +764,7 @@
updateAvailable = new UpdateAvailableBar() {
@Override
void onShow() {
- reload.reload();
+ Gerrit.display(PageLinks.toChange(changeId));
}
void onIgnore(Timestamp newTime) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.ui.xml
index ce8a062..57bace1 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/ChangeScreen2.ui.xml
@@ -18,15 +18,14 @@
xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:c='urn:import:com.google.gerrit.client.change'
xmlns:g='urn:import:com.google.gwt.user.client.ui'
- xmlns:x='urn:import:com.google.gerrit.client.ui'
- xmlns:clippy='urn:import:com.google.gwtexpui.clippy.client'>
+ xmlns:x='urn:import:com.google.gerrit.client.ui'>
<ui:with field='ico' type='com.google.gerrit.client.GerritResources'/>
<ui:with field='res' type='com.google.gerrit.client.change.Resources'/>
<ui:style type='com.google.gerrit.client.change.ChangeScreen2.Style'>
@eval textColor com.google.gerrit.client.Gerrit.getTheme().textColor;
@eval trimColor com.google.gerrit.client.Gerrit.getTheme().trimColor;
- @def INFO_WIDTH 450px;
+ @def COMMIT_WIDTH 560px;
@def HEADER_HEIGHT 29px;
.headerLine {
@@ -35,60 +34,74 @@
height: HEADER_HEIGHT;
}
- .idBlock {
+ .subjectLine {
position: relative;
- width: INFO_WIDTH;
+ width: COMMIT_WIDTH;
height: HEADER_HEIGHT;
background-color: trimColor;
color: textColor;
font-family: sans-serif;
font-weight: bold;
}
- .star {
- cursor: pointer;
- outline: none;
- position: absolute;
- left: 5px;
- top: 5px;
- }
- .idLine, .idStatus {
+ .subjectText {
+ width: 460px;
+ height: HEADER_HEIGHT;
line-height: HEADER_HEIGHT;
- }
- .idLine {
- position: absolute;
- top: 0;
- left: 29px;
- width: 245px;
- white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
- .idStatus {
+ .subjectButtons {
position: absolute;
top: 0;
- right: 26px;
+ right: 3px;
}
- .reload {
+
+ .infoLine {
+ position: absolute;
+ top: 0;
+ left: COMMIT_WIDTH;
+ height: HEADER_HEIGHT;
+ padding-left: 25px;
+ }
+
+ .statusRight {
+ position: absolute;
+ top: 0;
+ right: 0;
+ height: HEADER_HEIGHT;
+ }
+ .starChangeStatus {
+ display: inline-block;
+ width: 325px;
+ }
+ .star {
+ position: absolute;
+ top: 5px;
+ left: 0;
+ cursor: pointer;
+ outline: none;
+ }
+ .changeId {
display: block;
position: absolute;
- top: 7px;
- right: 5px;
- cursor: pointer;
- }
-
- .headerButtons {
- position: absolute;
top: 0;
- left: INFO_WIDTH;
- height: HEADER_HEIGHT;
- padding-left: 5px;
+ left: 22px;
+ width: 300px;
+ white-space: nowrap;
+ line-height: HEADER_HEIGHT;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ .statusText {
+ font-weight: bold;
}
- .popdown {
- position: absolute;
- top: 2px;
- right: 0;
+ div.popdown {
+ display: inline-block;
+ margin-top: 2px;
+ margin-left: 5px;
}
+
.popdown button {
cursor: pointer;
height: 25px;
@@ -110,18 +123,22 @@
.popdown button.selected {
font-weight: bold;
}
- .headerLine button:disabled,
- .headerTable button:disabled,
+ .popdown button:focus {
+ outline: none;
+ }
+
+ .headerButtons button:disabled,
+ #change_infoTable button:disabled,
.popdown button:disabled {
background-color: #999;
background-image: -webkit-linear-gradient(top, #999, #999);
}
- .headerTable {
+ .infoTable {
border-spacing: 0;
}
- .headerTable th {
+ .infoTable th {
width: 60px;
color: #444;
font-weight: normal;
@@ -130,10 +147,6 @@
padding-right: 5px;
}
- .clippy div {
- float: right;
- }
-
.queryProject {
float: left;
cursor: pointer;
@@ -141,7 +154,8 @@
.infoColumn {
width: 440px;
- padding-right: 10px;
+ padding-left: 17px;
+ padding-right: 17px;
vertical-align: top;
}
@@ -158,12 +172,16 @@
color: red;
}
- .commitColumn, .related {
+ .commitColumn, .relatedColumn {
padding: 0;
vertical-align: top;
}
- .commitColumn {
- width: 600px;
+ .commitColumn { width: COMMIT_WIDTH; }
+ .relatedColumn { width: 375px; }
+
+ .commitSubjectText {
+ font-family: monospace;
+ white-space: pre;
}
.labels {
@@ -246,9 +264,12 @@
padding: 5px 10px;
}
.sectionHeader .headerButtons {
+ position: absolute;
+ left: 300px;
top: 2px;
height: 18px;
border-left: 1px inset #fff;
+ padding-left: 5px;
padding-top: 3px;
padding-bottom: 3px;
}
@@ -261,58 +282,70 @@
<g:HTMLPanel>
<g:HTMLPanel styleName='{style.headerLine}' ui:field='headerLine'>
- <div class='{style.idBlock}'>
- <c:StarIcon ui:field='star' styleName='{style.star}'/>
- <div class='{style.idLine}'>
- <ui:msg>Change <span ui:field='changeIdText'/> by <span ui:field='ownerText'/></ui:msg>
- </div>
- <div ui:field='statusText' class='{style.idStatus}'/>
- <a ui:field='permalink' class='{style.reload}'>
- <c:Reload ui:field='reload'
- title='Reload the change (Shortcut: R)'>
+ <div class='{style.subjectLine}'>
+ <div class='{style.subjectText}' ui:field='commitSubjectText'/>
+ <div class='{style.subjectButtons} {style.headerButtons}'>
+ <g:Button ui:field='editMessage'
+ styleName=''
+ visible='false'
+ title='Edit commit message (Shortcut: e)'>
<ui:attribute name='title'/>
- </c:Reload>
- </a>
- </div>
- <div class='{style.headerButtons}'>
- <g:Button ui:field='reply'
- styleName=''
- title='Reply and score (Shortcut: a)'>
- <ui:attribute name='title'/>
- <div><ui:msg>Reply…</ui:msg></div>
- </g:Button>
- <c:QuickApprove ui:field='quickApprove'
- styleName='{style.quickApprove}'
- title='Apply score with one click'>
- <ui:attribute name='title'/>
- </c:QuickApprove>
- <g:Button ui:field='editMessage'
- styleName=''
- visible='false'
- title='Edit commit message (Shortcut: e)'>
- <ui:attribute name='title'/>
- <div><ui:msg>Edit Message</ui:msg></div>
- </g:Button>
+ <div><ui:msg>Edit Message</ui:msg></div>
+ </g:Button>
+ </div>
</div>
- <g:FlowPanel styleName='{style.popdown}'>
- <g:Button ui:field='includedIn' styleName='' visible="false">
- <div><ui:msg>Included in</ui:msg></div>
- </g:Button>
- <g:Button ui:field='revisions' styleName=''>
- <div><ui:msg>Revisions</ui:msg></div>
- </g:Button>
- <g:Button ui:field='download' styleName=''>
- <div><ui:msg>Download</ui:msg></div>
- </g:Button>
- </g:FlowPanel>
+ <div class='{style.infoLine}'>
+ <div class='{style.headerButtons}'>
+ <g:Button ui:field='reply'
+ styleName=''
+ title='Reply and score (Shortcut: a)'>
+ <ui:attribute name='title'/>
+ <div><ui:msg>Reply…</ui:msg></div>
+ </g:Button>
+ <c:QuickApprove ui:field='quickApprove'
+ styleName='{style.quickApprove}'
+ title='Apply score with one click'>
+ <ui:attribute name='title'/>
+ </c:QuickApprove>
+ </div>
+ </div>
+
+ <div class='{style.statusRight}'>
+ <div class='{style.starChangeStatus}'>
+ <c:StarIcon ui:field='star' styleName='{style.star}'/>
+ <span class='{style.changeId}'>
+ <ui:msg>Change <g:Anchor ui:field='permalink' title='Reload the change (Shortcut: R)'>
+ <ui:attribute name='title'/>
+ </g:Anchor> - <span ui:field='statusText' class='{style.statusText}'/></ui:msg>
+ </span>
+ </div>
+ <g:FlowPanel styleName='{style.popdown}'>
+ <g:Button ui:field='includedIn' styleName='' visible="false">
+ <div><ui:msg>Included in</ui:msg></div>
+ </g:Button>
+ <g:Button ui:field='revisions' styleName=''>
+ <div><ui:msg>Revisions</ui:msg></div>
+ </g:Button>
+ <g:Button ui:field='download' styleName=''>
+ <div><ui:msg>Download</ui:msg></div>
+ </g:Button>
+ </g:FlowPanel>
+ </div>
</g:HTMLPanel>
- <table class='{style.headerTable}'>
+ <table class='{style.infoTable}'>
<tr>
+ <td class='{style.commitColumn}'>
+ <c:CommitBox ui:field='commit'/>
+ </td>
<td class='{style.infoColumn}'>
<table id='change_infoTable'>
<tr>
+ <th><ui:msg>Owner</ui:msg></th>
+ <td ui:field='ownerText'/>
+ </tr>
+ <tr>
<th><ui:msg>Reviewers</ui:msg></th>
<td ui:field='reviewersText'/>
</tr>
@@ -346,6 +379,10 @@
</td>
</tr>
<tr>
+ <th><ui:msg>Topic</ui:msg></th>
+ <td><c:Topic ui:field='topic'/></td>
+ </tr>
+ <tr>
<th><ui:msg>Strategy</ui:msg></th>
<td>
<span ui:field='submitActionText'/>
@@ -357,29 +394,16 @@
</div>
</td>
</tr>
- <tr><td colspan='2'><c:Actions ui:field='actions'/></td></tr>
<tr>
<th ui:field='actionText'/>
<td ui:field='actionDate'/>
</tr>
- <tr>
- <th><ui:msg>Change-Id</ui:msg></th>
- <td><clippy:CopyableLabel styleName='{style.clippy}' ui:field='idText'/></td>
- </tr>
- <tr>
- <th><ui:msg>Topic</ui:msg></th>
- <td><c:Topic ui:field='topic'/></td>
- </tr>
+ <tr><td colspan='2'><c:Actions ui:field='actions'/></td></tr>
</table>
<hr/>
<c:Labels ui:field='labels' styleName='{style.labels}'/>
</td>
-
- <td class='{style.commitColumn}'>
- <c:CommitBox ui:field='commit'/>
- </td>
-
- <td class='{style.related}'>
+ <td class='{style.relatedColumn}'>
<c:RelatedChanges ui:field='related'/>
</td>
</tr>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java
index 5cb0443..4b7e14a 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.java
@@ -28,27 +28,61 @@
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.AnchorElement;
import com.google.gwt.dom.client.Element;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.resources.client.CssResource;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.client.UiHandler;
+import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HTMLPanel;
+import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.UIObject;
+import com.google.gwtexpui.clippy.client.CopyableLabel;
import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
class CommitBox extends Composite {
interface Binder extends UiBinder<HTMLPanel, CommitBox> {}
private static final Binder uiBinder = GWT.create(Binder.class);
+ interface Style extends CssResource {
+ String collapsed();
+ String expanded();
+ }
+
+ @UiField Style style;
@UiField Element commitName;
@UiField AnchorElement browserLink;
@UiField InlineHyperlink authorNameEmail;
@UiField Element authorDate;
@UiField InlineHyperlink committerNameEmail;
@UiField Element committerDate;
- @UiField Element commitMessageText;
+ @UiField CopyableLabel idText;
+ @UiField HTML text;
+ @UiField ScrollPanel scroll;
+ @UiField Button more;
+ private boolean expanded;
CommitBox() {
initWidget(uiBinder.createAndBindUi(this));
+ addStyleName(style.collapsed());
+ }
+
+ void onShowView() {
+ more.setVisible(scroll.getMaximumVerticalScrollPosition() > 0);
+ }
+
+ @UiHandler("more")
+ void onMore(ClickEvent e) {
+ if (expanded) {
+ removeStyleName(style.expanded());
+ addStyleName(style.collapsed());
+ } else {
+ removeStyleName(style.collapsed());
+ addStyleName(style.expanded());
+ }
+ expanded = !expanded;
}
void set(CommentLinkProcessor commentLinkProcessor,
@@ -56,14 +90,24 @@
String revision) {
RevisionInfo revInfo = change.revision(revision);
CommitInfo commit = revInfo.commit();
+ String sub = commit.subject();
+ String msg = commit.message();
+ if (msg.startsWith(sub)) {
+ msg = msg.substring(sub.length());
+ if (msg.length() > 0 && msg.charAt(0) == '\n') {
+ msg = msg.substring(1);
+ }
+ }
commitName.setInnerText(revision);
+ idText.setText("Change-Id: " + change.change_id());
+ idText.setPreviewText(change.change_id());
formatLink(commit.author(), authorNameEmail,
authorDate, change.status());
formatLink(commit.committer(), committerNameEmail,
committerDate, change.status());
- commitMessageText.setInnerSafeHtml(commentLinkProcessor.apply(
- new SafeHtmlBuilder().append(commit.message()).linkify()));
+ text.setHTML(commentLinkProcessor.apply(
+ new SafeHtmlBuilder().append(msg).linkify()));
GitwebLink gw = Gerrit.getGitwebLink();
if (gw != null && gw.canLink(revInfo)) {
@@ -86,7 +130,7 @@
return person.name() + " <" + person.email() + ">";
}
- public static String owner(GitPerson person) {
+ private static String owner(GitPerson person) {
if (person.email() != null) {
return person.email();
} else if (person.name() != null) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.ui.xml
index a095926..8b543ba 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/CommitBox.ui.xml
@@ -17,34 +17,82 @@
<ui:UiBinder
xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'
- xmlns:x='urn:import:com.google.gerrit.client.ui'>
- <ui:style>
- .commitHeader {
- border-spacing: 0;
- padding: 0;
- width: 564px;
+ xmlns:x='urn:import:com.google.gerrit.client.ui'
+ xmlns:clippy='urn:import:com.google.gwtexpui.clippy.client'>
+ <ui:image field="toggle" src="more_less.png"/>
+ <ui:style type='com.google.gerrit.client.change.CommitBox.Style'>
+ @eval trimColor com.google.gerrit.client.Gerrit.getTheme().trimColor;
+
+ .collapsed .scroll { height: 250px }
+ .scroll, .more { width: 560px }
+ .scroll {
+ border-right: 1px solid trimColor;
+ border-bottom: 1px solid trimColor;
}
- .commitHeader th { width: 70px; }
- .commitHeader td { white-space: pre; }
-
- .commitMessageBox { margin: 2px; }
- .commitMessage {
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
- border: 1px solid white;
- background-color: white;
+ .text {
font-family: monospace;
white-space: pre;
- width: 47em;
}
+
+ .more {
+ height: 8px;
+ line-height: 8px;
+ text-align: center;
+ }
+ .moreButton {
+ padding: 0 5px 0 5px;
+ margin: 0;
+ border: none;
+ height: 8px;
+ background-color: #F7F7F7;
+ }
+ .moreButton:focus {
+ outline: none;
+ }
+
+ @sprite .toggle {
+ gwt-image: "toggle";
+ width: 13px;
+ height: 8px;
+ padding: 0;
+ }
+ .collapsed .toggle { background-position: -13px -8px }
+ .expanded .toggle { background-position: 0px -8px }
+ .collapsed button:hover .toggle { background-position: -13px 0px }
+ .expanded button:hover .toggle { background-position: 0px 0px }
+
+ .header {
+ border-spacing: 0;
+ padding: 0;
+ width: 560px;
+ }
+ .header th { width: 70px; }
+ .header td { white-space: nowrap; }
+ .date { width: 132px; }
+
+ .clippy {
+ position: relative;
+ }
+ .clippy div {
+ position: absolute;
+ top: 0px;
+ right: -16px;
+ }
</ui:style>
<g:HTMLPanel>
- <table class='{style.commitHeader}'>
- <tr>
- <th><ui:msg>Commit</ui:msg></th>
- <td ui:field='commitName'/>
- <td><a ui:field='browserLink' href=""/></td>
- </tr>
+ <g:ScrollPanel styleName='{style.scroll}' ui:field='scroll'>
+ <g:HTML styleName='{style.text}' ui:field='text'/>
+ </g:ScrollPanel>
+ <div class='{style.more}'>
+ <g:Button ui:field='more'
+ styleName='{style.moreButton}'
+ title='Expand/Collapse'>
+ <ui:attribute name='title'/>
+ <div class='{style.toggle}'/>
+ </g:Button>
+ </div>
+ <table class='{style.header}'>
<tr>
<th><ui:msg>Author</ui:msg></th>
<td><x:InlineHyperlink ui:field='authorNameEmail'
@@ -52,7 +100,7 @@
<ui:attribute name='title'/>
</x:InlineHyperlink>
</td>
- <td ui:field='authorDate'/>
+ <td ui:field='authorDate' class='{style.date}'/>
</tr>
<tr>
<th><ui:msg>Committer</ui:msg></th>
@@ -61,12 +109,17 @@
<ui:attribute name='title'/>
</x:InlineHyperlink>
</td>
- <td ui:field='committerDate'/>
+ <td ui:field='committerDate' class='{style.date}'/>
+ </tr>
+ <tr>
+ <th><ui:msg>Commit</ui:msg></th>
+ <td ui:field='commitName'/>
+ <td><a ui:field='browserLink' href=""/></td>
+ </tr>
+ <tr>
+ <th><ui:msg>Change-Id</ui:msg></th>
+ <td><clippy:CopyableLabel styleName='{style.clippy}' ui:field='idText'/></td>
</tr>
</table>
-
- <div class='{style.commitMessageBox}'>
- <div class='{style.commitMessage}' ui:field='commitMessageText'/>
- </div>
</g:HTMLPanel>
</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditMessageBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditMessageBox.ui.xml
index 118409e..9df4bbc 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditMessageBox.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/EditMessageBox.ui.xml
@@ -30,7 +30,7 @@
<div class='{res.style.section}'>
<c:NpTextArea
visibleLines='30'
- characterWidth='72'
+ characterWidth='78'
styleName='{style.commitMessage}'
ui:field='message'/>
</div>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reload.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reload.java
deleted file mode 100644
index 7b88b2b..0000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Reload.java
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.client.change;
-
-import com.google.gerrit.client.Gerrit;
-import com.google.gerrit.client.changes.ChangeInfo;
-import com.google.gerrit.common.PageLinks;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.event.dom.client.ClickEvent;
-import com.google.gwt.event.dom.client.ClickHandler;
-import com.google.gwt.event.dom.client.MouseOutEvent;
-import com.google.gwt.event.dom.client.MouseOutHandler;
-import com.google.gwt.event.dom.client.MouseOverEvent;
-import com.google.gwt.event.dom.client.MouseOverHandler;
-import com.google.gwt.user.client.Event;
-import com.google.gwt.user.client.ui.Image;
-import com.google.gwt.user.client.ui.impl.HyperlinkImpl;
-
-class Reload extends Image implements ClickHandler,
- MouseOverHandler, MouseOutHandler {
- private static final HyperlinkImpl link = GWT.create(HyperlinkImpl.class);
- private Change.Id changeId;
- private boolean in;
-
- Reload() {
- setResource(Resources.I.reload_black());
- addClickHandler(this);
- addMouseOverHandler(this);
- addMouseOutHandler(this);
- }
-
- void set(ChangeInfo info) {
- changeId = info.legacy_id();
- }
-
- void reload() {
- Gerrit.display(PageLinks.toChange(changeId));
- }
-
- @Override
- public void onMouseOver(MouseOverEvent event) {
- if (!in) {
- in = true;
- setResource(Resources.I.reload_white());
- }
- }
-
- @Override
- public void onMouseOut(MouseOutEvent event) {
- if (in) {
- in = false;
- setResource(Resources.I.reload_black());
- }
- }
-
- @Override
- public void onClick(ClickEvent e) {
- if (link.handleAsClick(e.getNativeEvent().<Event> cast())) {
- e.preventDefault();
- e.stopPropagation();
- reload();
- }
- }
-}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Resources.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Resources.java
index c1feb5d..7590909 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Resources.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/Resources.java
@@ -25,8 +25,6 @@
@Source("star_open.png") ImageResource star_open();
@Source("star_filled.png") ImageResource star_filled();
- @Source("reload_black.png") ImageResource reload_black();
- @Source("reload_white.png") ImageResource reload_white();
@Source("remove_reviewer.png") ImageResource remove_reviewer();
@Source("common.css") Style style();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/more_less.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/more_less.png
new file mode 100644
index 0000000..298514f
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/more_less.png
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/reload_black.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/reload_black.png
deleted file mode 100644
index 13f3a5d..0000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/reload_black.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/reload_white.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/reload_white.png
deleted file mode 100644
index 8f5a311..0000000
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/change/reload_white.png
+++ /dev/null
Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchTable.java
index 48aa02d..bc187e5 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchTable.java
@@ -219,11 +219,8 @@
if (previousPatchIndex < 0) {
return null;
}
- InlineHyperlink link =
- createLink(previousPatchIndex, patchType,
- SafeHtml.asis(Util.C.prevPatchLinkIcon()), null);
-
- return link;
+ return createLink(previousPatchIndex, patchType,
+ SafeHtml.asis(Util.C.prevPatchLinkIcon()), null);
}
/**
@@ -234,11 +231,8 @@
if (nextPatchIndex < 0) {
return null;
}
- InlineHyperlink link =
- createLink(nextPatchIndex, patchType, null,
- SafeHtml.asis(Util.C.nextPatchLinkIcon()));
-
- return link;
+ return createLink(nextPatchIndex, patchType, null,
+ SafeHtml.asis(Util.C.nextPatchLinkIcon()));
}
/**
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
index 48cabe7..21dcbd1 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/gerrit.css
@@ -1571,6 +1571,11 @@
padding: 2px 6px 1px;
}
+.editHeadButton {
+ float: right;
+ cursor: pointer;
+}
+
/** PluginListScreen **/
.pluginsTable {
}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java
index ebae86a..adc2a64 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/projects/ProjectApi.java
@@ -136,6 +136,14 @@
}
}
+ public static void setHead(Project.NameKey name, String ref,
+ AsyncCallback<NativeString> cb) {
+ RestApi call = project(name).view("HEAD");
+ HeadInput input = HeadInput.create();
+ input.setRef(ref);
+ call.put(input, cb);
+ }
+
public static RestApi project(Project.NameKey name) {
return new RestApi("/projects/").id(name.get());
}
@@ -229,4 +237,15 @@
final native void setDescription(String d) /*-{ if(d)this.description=d; }-*/;
}
+
+ private static class HeadInput extends JavaScriptObject {
+ static HeadInput create() {
+ return createObject().cast();
+ }
+
+ protected HeadInput() {
+ }
+
+ final native void setRef(String r) /*-{ if(r)this.ref=r; }-*/;
+ }
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HtmlDomUtil.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HtmlDomUtil.java
index c47552d..1a2d3f6 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HtmlDomUtil.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/HtmlDomUtil.java
@@ -267,7 +267,6 @@
factory.setExpandEntityReferences(false);
factory.setIgnoringComments(true);
factory.setCoalescing(true);
- final DocumentBuilder parser = factory.newDocumentBuilder();
- return parser;
+ return factory.newDocumentBuilder();
}
}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java
index 57be31e..2b6c128d 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java
@@ -71,7 +71,8 @@
@Override
protected ProjectAccess updateProjectConfig(ProjectConfig config,
- MetaDataUpdate md) throws IOException, NoSuchProjectException, ConfigInvalidException {
+ MetaDataUpdate md, boolean parentProjectUpdate) throws IOException,
+ NoSuchProjectException, ConfigInvalidException {
config.commit(md);
projectCache.evict(config.getProject());
return projectAccessFactory.create(projectName).call();
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
index 971c4b3..783915a 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessHandler.java
@@ -132,12 +132,18 @@
}
}
+ boolean parentProjectUpdate = false;
if (!config.getProject().getNameKey().equals(allProjects.get()) &&
!config.getProject().getParent(allProjects.get()).equals(parentProjectName)) {
+ parentProjectUpdate = true;
try {
- setParent.get().validateParentUpdate(projectControl, parentProjectName.get());
+ setParent.get().validateParentUpdate(projectControl,
+ parentProjectName.get(), checkIfOwner);
} catch (AuthException e) {
- throw new UpdateParentFailedException(e.getMessage(), e);
+ throw new UpdateParentFailedException(
+ "You are not allowed to change the parent project since you are "
+ + "not an administrator. You may save the modifications for review "
+ + "so that an administrator can approve them.", e);
} catch (ResourceConflictException e) {
throw new UpdateParentFailedException(e.getMessage(), e);
} catch (UnprocessableEntityException e) {
@@ -155,15 +161,15 @@
md.setMessage("Modify access rules\n");
}
- return updateProjectConfig(config, md);
+ return updateProjectConfig(config, md, parentProjectUpdate);
} finally {
md.close();
}
}
protected abstract T updateProjectConfig(ProjectConfig config,
- MetaDataUpdate md) throws IOException, NoSuchProjectException,
- ConfigInvalidException, OrmException;
+ MetaDataUpdate md, boolean parentProjectUpdate) throws IOException,
+ NoSuchProjectException, ConfigInvalidException, OrmException;
private void replace(ProjectConfig config, Set<String> toDelete,
AccessSection section) throws NoSuchGroupException {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
index 3f4030d..946a703 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ReviewProjectAccess.java
@@ -17,6 +17,8 @@
import com.google.gerrit.common.ChangeHooks;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.data.AccessSection;
+import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Branch;
import com.google.gerrit.reviewdb.client.Change;
@@ -39,6 +41,7 @@
import com.google.gerrit.server.mail.CreateChangeSender;
import com.google.gerrit.server.patch.PatchSetInfoFactory;
import com.google.gerrit.server.project.ChangeControl;
+import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.project.ProjectControl;
import com.google.gerrit.server.project.SetParent;
import com.google.gerrit.server.util.TimeUtil;
@@ -78,6 +81,7 @@
private final ChangeIndexer indexer;
private final ChangeHooks hooks;
private final CreateChangeSender.Factory createChangeSenderFactory;
+ private final ProjectCache projectCache;
@Inject
ReviewProjectAccess(final ProjectControl.Factory projectControlFactory,
@@ -88,6 +92,7 @@
ChangeControl.GenericFactory changeFactory,
ChangeIndexer indexer, ChangeHooks hooks,
CreateChangeSender.Factory createChangeSenderFactory,
+ ProjectCache projectCache,
AllProjectsNameProvider allProjects,
Provider<SetParent> setParent,
@@ -107,11 +112,13 @@
this.indexer = indexer;
this.hooks = hooks;
this.createChangeSenderFactory = createChangeSenderFactory;
+ this.projectCache = projectCache;
}
@Override
- protected Change.Id updateProjectConfig(ProjectConfig config, MetaDataUpdate md)
- throws IOException, OrmException {
+ protected Change.Id updateProjectConfig(ProjectConfig config,
+ MetaDataUpdate md, boolean parentProjectUpdate) throws IOException,
+ OrmException {
Change.Id changeId = new Change.Id(db.nextChangeId());
PatchSet ps =
new PatchSet(new PatchSet.Id(changeId, Change.INITIAL_PATCH_SET_ID));
@@ -158,6 +165,9 @@
log.error("Cannot send email for new change " + change.getId(), err);
}
addProjectOwnersAsReviewers(change);
+ if (parentProjectUpdate) {
+ addAdministratorsAsReviewers(change);
+ }
return changeId;
}
@@ -175,7 +185,7 @@
db.patchSetAncestors().insert(toInsert);
}
- private void addProjectOwnersAsReviewers(final Change change) {
+ private void addProjectOwnersAsReviewers(Change change) {
final String projectOwners =
groupBackend.get(AccountGroup.PROJECT_OWNERS).getName();
try {
@@ -189,4 +199,22 @@
// can't be added as reviewer
}
}
+
+ private void addAdministratorsAsReviewers(Change change) {
+ List<PermissionRule> adminRules =
+ projectCache.getAllProjects().getConfig()
+ .getAccessSection(AccessSection.GLOBAL_CAPABILITIES)
+ .getPermission(GlobalCapability.ADMINISTRATE_SERVER).getRules();
+ for (PermissionRule r : adminRules) {
+ try {
+ ChangeResource rsrc =
+ new ChangeResource(changeFactory.controlFor(change, user));
+ PostReviewers.Input input = new PostReviewers.Input();
+ input.reviewer = r.getGroup().getUUID().get();
+ reviewersProvider.get().apply(rsrc, input);
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+ }
}
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/HttpLog.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/HttpLog.java
index 6aef509..03b5a55 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/HttpLog.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/HttpLog.java
@@ -17,12 +17,12 @@
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.util.LogUtil;
import com.google.gerrit.server.util.TimeUtil;
import org.apache.log4j.Appender;
import org.apache.log4j.AsyncAppender;
import org.apache.log4j.DailyRollingFileAppender;
-import org.apache.log4j.Layout;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.spi.ErrorHandler;
@@ -35,45 +35,52 @@
import java.io.File;
import java.io.IOException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.TimeZone;
/** Writes the {@code httpd_log} file with per-request data. */
class HttpLog extends AbstractLifeCycle implements RequestLog {
private static final Logger log = Logger.getLogger(HttpLog.class);
private static final String LOG_NAME = "httpd_log";
- private static final String P_HOST = "Host";
- private static final String P_USER = "User";
- private static final String P_METHOD = "Method";
- private static final String P_RESOURCE = "Resource";
- private static final String P_PROTOCOL = "Version";
- private static final String P_STATUS = "Status";
- private static final String P_CONTENT_LENGTH = "Content-Length";
- private static final String P_REFERER = "Referer";
- private static final String P_USER_AGENT = "User-Agent";
+ protected static final String P_HOST = "Host";
+ protected static final String P_USER = "User";
+ protected static final String P_METHOD = "Method";
+ protected static final String P_RESOURCE = "Resource";
+ protected static final String P_PROTOCOL = "Version";
+ protected static final String P_STATUS = "Status";
+ protected static final String P_CONTENT_LENGTH = "Content-Length";
+ protected static final String P_REFERER = "Referer";
+ protected static final String P_USER_AGENT = "User-Agent";
private final AsyncAppender async;
HttpLog(final SitePaths site, final Config config) {
- final DailyRollingFileAppender dst = new DailyRollingFileAppender();
- dst.setName(LOG_NAME);
- dst.setLayout(new MyLayout());
- dst.setEncoding("UTF-8");
- dst.setFile(new File(resolve(site.logs_dir), LOG_NAME).getPath());
- dst.setImmediateFlush(true);
- dst.setAppend(true);
- dst.setThreshold(Level.INFO);
- dst.setErrorHandler(new DieErrorHandler());
- dst.activateOptions();
- dst.setErrorHandler(new LogLogHandler());
-
async = new AsyncAppender();
async.setBlocking(true);
async.setBufferSize(config.getInt("core", "asyncLoggingBufferSize", 64));
async.setLocationInfo(false);
- async.addAppender(dst);
+ if (LogUtil.shouldConfigureLogSystem()) {
+ final DailyRollingFileAppender dst = new DailyRollingFileAppender();
+ dst.setName(LOG_NAME);
+ dst.setLayout(new HttpLogLayout());
+ dst.setEncoding("UTF-8");
+ dst.setFile(new File(resolve(site.logs_dir), LOG_NAME).getPath());
+ dst.setImmediateFlush(true);
+ dst.setAppend(true);
+ dst.setThreshold(Level.INFO);
+ dst.setErrorHandler(new DieErrorHandler());
+ dst.activateOptions();
+ dst.setErrorHandler(new LogLogHandler());
+ async.addAppender(dst);
+ } else {
+ Appender appender = log.getAppender(LOG_NAME);
+ if (appender != null) {
+ async.addAppender(appender);
+ } else {
+ log.warn("No appender with the name: "
+ + LOG_NAME
+ + " was found. HTTPD logging is disabled");
+ }
+ }
async.activateOptions();
}
@@ -153,105 +160,6 @@
}
}
- private static final class MyLayout extends Layout {
- private final SimpleDateFormat dateFormat;
- private long lastTimeMillis;
- private String lastTimeString;
-
- MyLayout() {
- final TimeZone tz = TimeZone.getDefault();
- dateFormat = new SimpleDateFormat("dd/MMM/yyyy:HH:mm:ss Z");
- dateFormat.setTimeZone(tz);
-
- lastTimeMillis = TimeUtil.nowMs();
- lastTimeString = dateFormat.format(new Date(lastTimeMillis));
- }
-
- @Override
- public String format(LoggingEvent event) {
- final StringBuilder buf = new StringBuilder(128);
-
- opt(buf, event, P_HOST);
-
- buf.append(' ');
- buf.append('-'); // identd on client system (never requested)
-
- buf.append(' ');
- opt(buf, event, P_USER);
-
- buf.append(' ');
- buf.append('[');
- formatDate(event.getTimeStamp(), buf);
- buf.append(']');
-
- buf.append(' ');
- buf.append('"');
- buf.append(event.getMDC(P_METHOD));
- buf.append(' ');
- buf.append(event.getMDC(P_RESOURCE));
- buf.append(' ');
- buf.append(event.getMDC(P_PROTOCOL));
- buf.append('"');
-
- buf.append(' ');
- buf.append(event.getMDC(P_STATUS));
-
- buf.append(' ');
- opt(buf, event, P_CONTENT_LENGTH);
-
- buf.append(' ');
- dq_opt(buf, event, P_REFERER);
-
- buf.append(' ');
- dq_opt(buf, event, P_USER_AGENT);
-
- buf.append('\n');
- return buf.toString();
- }
-
- private void opt(StringBuilder buf, LoggingEvent event, String key) {
- String val = (String) event.getMDC(key);
- if (val == null) {
- buf.append('-');
- } else {
- buf.append(val);
- }
- }
-
- private void dq_opt(StringBuilder buf, LoggingEvent event, String key) {
- String val = (String) event.getMDC(key);
- if (val == null) {
- buf.append('-');
- } else {
- buf.append('"');
- buf.append(val);
- buf.append('"');
- }
- }
-
- private void formatDate(final long now, final StringBuilder sbuf) {
- final long rounded = now - (int) (now % 1000);
- if (rounded != lastTimeMillis) {
- synchronized (dateFormat) {
- lastTimeMillis = rounded;
- lastTimeString = dateFormat.format(new Date(lastTimeMillis));
- sbuf.append(lastTimeString);
- }
- } else {
- sbuf.append(lastTimeString);
- }
- }
-
- @Override
- public boolean ignoresThrowable() {
- return true;
- }
-
- @Override
- public void activateOptions() {
- }
- }
-
private static final class DieErrorHandler implements ErrorHandler {
@Override
public void error(String message, Exception e, int errorCode,
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/HttpLogLayout.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/HttpLogLayout.java
new file mode 100644
index 0000000..25c6d09
--- /dev/null
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/HttpLogLayout.java
@@ -0,0 +1,122 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.pgm.http.jetty;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.spi.LoggingEvent;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+public final class HttpLogLayout extends Layout {
+ private final SimpleDateFormat dateFormat;
+ private long lastTimeMillis;
+ private String lastTimeString;
+
+ HttpLogLayout() {
+ final TimeZone tz = TimeZone.getDefault();
+ dateFormat = new SimpleDateFormat("dd/MMM/yyyy:HH:mm:ss Z");
+ dateFormat.setTimeZone(tz);
+
+ lastTimeMillis = System.currentTimeMillis();
+ lastTimeString = dateFormat.format(new Date(lastTimeMillis));
+ }
+
+ @Override
+ public String format(LoggingEvent event) {
+ final StringBuilder buf = new StringBuilder(128);
+
+ opt(buf, event, HttpLog.P_HOST);
+
+ buf.append(' ');
+ buf.append('-'); // identd on client system (never requested)
+
+ buf.append(' ');
+ opt(buf, event, HttpLog.P_USER);
+
+ buf.append(' ');
+ buf.append('[');
+ formatDate(event.getTimeStamp(), buf);
+ buf.append(']');
+
+ buf.append(' ');
+ buf.append('"');
+ buf.append(event.getMDC(HttpLog.P_METHOD));
+ buf.append(' ');
+ buf.append(event.getMDC(HttpLog.P_RESOURCE));
+ buf.append(' ');
+ buf.append(event.getMDC(HttpLog.P_PROTOCOL));
+ buf.append('"');
+
+ buf.append(' ');
+ buf.append(event.getMDC(HttpLog.P_STATUS));
+
+ buf.append(' ');
+ opt(buf, event, HttpLog.P_CONTENT_LENGTH);
+
+ buf.append(' ');
+ dq_opt(buf, event, HttpLog.P_REFERER);
+
+ buf.append(' ');
+ dq_opt(buf, event, HttpLog.P_USER_AGENT);
+
+ buf.append('\n');
+ return buf.toString();
+ }
+
+ private void opt(StringBuilder buf, LoggingEvent event, String key) {
+ String val = (String) event.getMDC(key);
+ if (val == null) {
+ buf.append('-');
+ } else {
+ buf.append(val);
+ }
+ }
+
+ private void dq_opt(StringBuilder buf, LoggingEvent event, String key) {
+ String val = (String) event.getMDC(key);
+ if (val == null) {
+ buf.append('-');
+ } else {
+ buf.append('"');
+ buf.append(val);
+ buf.append('"');
+ }
+ }
+
+ private void formatDate(final long now, final StringBuilder sbuf) {
+ final long rounded = now - (int) (now % 1000);
+ if (rounded != lastTimeMillis) {
+ synchronized (dateFormat) {
+ lastTimeMillis = rounded;
+ lastTimeString = dateFormat.format(new Date(lastTimeMillis));
+ sbuf.append(lastTimeString);
+ }
+ } else {
+ sbuf.append(lastTimeString);
+ }
+ }
+
+ @Override
+ public boolean ignoresThrowable() {
+ return true;
+ }
+
+ @Override
+ public void activateOptions() {
+ }
+}
+
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/ErrorLogFile.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/ErrorLogFile.java
index 126ef98..da7704d 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/ErrorLogFile.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/ErrorLogFile.java
@@ -14,9 +14,9 @@
package com.google.gerrit.pgm.util;
-import com.google.common.base.Strings;
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.server.config.SitePaths;
+import com.google.gerrit.server.util.LogUtil;
import org.apache.log4j.Appender;
import org.apache.log4j.ConsoleAppender;
@@ -34,8 +34,6 @@
import java.io.IOException;
public class ErrorLogFile {
- @SuppressWarnings("deprecation")
- private static final String LOG4J_CONFIGURATION = LogManager.DEFAULT_CONFIGURATION_KEY;
static final String LOG_NAME = "error_log";
public static void errorOnlyConsole() {
@@ -60,7 +58,7 @@
if (!logdir.exists() && !logdir.mkdirs()) {
throw new Die("Cannot create log directory: " + logdir);
}
- if (shouldConfigureLogSystem()) {
+ if (LogUtil.shouldConfigureLogSystem()) {
initLogSystem(logdir);
}
@@ -76,10 +74,6 @@
};
}
- public static boolean shouldConfigureLogSystem() {
- return Strings.isNullOrEmpty(System.getProperty(LOG4J_CONFIGURATION));
- }
-
private static void initLogSystem(final File logdir) {
final PatternLayout layout = new PatternLayout();
layout.setConversionPattern("[%d] %-5p %c %x: %m%n");
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/GarbageCollectionLogFile.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/GarbageCollectionLogFile.java
index b34bb21..746355b 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/GarbageCollectionLogFile.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/GarbageCollectionLogFile.java
@@ -17,6 +17,7 @@
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.GarbageCollection;
+import com.google.gerrit.server.util.LogUtil;
import org.apache.log4j.Appender;
import org.apache.log4j.DailyRollingFileAppender;
@@ -42,7 +43,7 @@
if (!logdir.exists() && !logdir.mkdirs()) {
throw new Die("Cannot create log directory: " + logdir);
}
- if (ErrorLogFile.shouldConfigureLogSystem()) {
+ if (LogUtil.shouldConfigureLogSystem()) {
initLogSystem(logdir);
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/IncludedInResolver.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/IncludedInResolver.java
index b02f6f0..9c4817e 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/IncludedInResolver.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/IncludedInResolver.java
@@ -14,12 +14,17 @@
package com.google.gerrit.server.change;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Sets;
import com.google.gerrit.common.data.IncludedInDetail;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevFlag;
@@ -28,13 +33,10 @@
import org.slf4j.LoggerFactory;
import java.io.IOException;
-import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Set;
/**
@@ -48,16 +50,14 @@
public static IncludedInDetail resolve(final Repository repo,
final RevWalk rw, final RevCommit commit) throws IOException {
- Set<Ref> tags =
- new HashSet<Ref>(repo.getRefDatabase().getRefs(Constants.R_TAGS)
- .values());
- Set<Ref> branches =
- new HashSet<Ref>(repo.getRefDatabase().getRefs(Constants.R_HEADS)
- .values());
- Set<Ref> allTagsAndBranches = new HashSet<Ref>();
+ RefDatabase refDb = repo.getRefDatabase();
+ Collection<Ref> tags = refDb.getRefs(Constants.R_TAGS).values();
+ Collection<Ref> branches = refDb.getRefs(Constants.R_HEADS).values();
+ List<Ref> allTagsAndBranches = Lists.newArrayListWithCapacity(
+ tags.size() + branches.size());
allTagsAndBranches.addAll(tags);
allTagsAndBranches.addAll(branches);
- Set<Ref> allMatchingTagsAndBranches =
+ Set<String> allMatchingTagsAndBranches =
includedIn(repo, rw, commit, allTagsAndBranches);
IncludedInDetail detail = new IncludedInDetail();
@@ -71,15 +71,15 @@
/**
* Resolves which tip refs include the target commit.
*/
- private static Set<Ref> includedIn(final Repository repo, final RevWalk rw,
- final RevCommit target, final Set<Ref> tipRefs) throws IOException,
+ private static Set<String> includedIn(final Repository repo, final RevWalk rw,
+ final RevCommit target, final Collection<Ref> tipRefs) throws IOException,
MissingObjectException, IncorrectObjectTypeException {
- Set<Ref> result = new HashSet<Ref>();
+ Set<String> result = Sets.newHashSet();
- Map<RevCommit, Set<Ref>> tipsAndCommits = parseCommits(repo, rw, tipRefs);
+ Multimap<RevCommit, String> tipsAndCommits = parseCommits(repo, rw, tipRefs);
- List<RevCommit> tips = new ArrayList<RevCommit>(tipsAndCommits.keySet());
+ List<RevCommit> tips = Lists.newArrayList(tipsAndCommits.keySet());
Collections.sort(tips, new Comparator<RevCommit>() {
@Override
public int compare(RevCommit c1, RevCommit c2) {
@@ -87,17 +87,16 @@
}
});
- Set<RevCommit> targetReachableFrom = new HashSet<RevCommit>();
- targetReachableFrom.add(target);
+ RevFlag containsTarget = rw.newFlag("CONTAINS_TARGET");
for (RevCommit tip : tips) {
boolean commitFound = false;
- rw.resetRetain(RevFlag.UNINTERESTING);
+ rw.resetRetain(RevFlag.UNINTERESTING, containsTarget);
rw.markStart(tip);
for (RevCommit commit : rw) {
- if (targetReachableFrom.contains(commit)) {
+ if (commit.equals(target) || commit.has(containsTarget)) {
commitFound = true;
- targetReachableFrom.add(tip);
+ tip.add(containsTarget);
result.addAll(tipsAndCommits.get(tip));
break;
}
@@ -114,12 +113,12 @@
* Returns the short names of refs which are as well in the matchingRefs list
* as well as in the allRef list.
*/
- private static List<String> getMatchingRefNames(Set<Ref> matchingRefs,
- Set<Ref> allRefs) {
- List<String> refNames = new ArrayList<String>();
- for (Ref matchingRef : matchingRefs) {
- if (allRefs.contains(matchingRef)) {
- refNames.add(Repository.shortenRefName(matchingRef.getName()));
+ private static List<String> getMatchingRefNames(Set<String> matchingRefs,
+ Collection<Ref> allRefs) {
+ List<String> refNames = Lists.newArrayListWithCapacity(matchingRefs.size());
+ for (Ref r : allRefs) {
+ if (matchingRefs.contains(r.getName())) {
+ refNames.add(Repository.shortenRefName(r.getName()));
}
}
return refNames;
@@ -128,9 +127,9 @@
/**
* Parse commit of ref and store the relation between ref and commit.
*/
- private static Map<RevCommit, Set<Ref>> parseCommits(final Repository repo,
- final RevWalk rw, final Set<Ref> refs) throws IOException {
- Map<RevCommit, Set<Ref>> result = new HashMap<RevCommit, Set<Ref>>();
+ private static Multimap<RevCommit, String> parseCommits(final Repository repo,
+ final RevWalk rw, final Collection<Ref> refs) throws IOException {
+ Multimap<RevCommit, String> result = LinkedListMultimap.create();
for (Ref ref : refs) {
final RevCommit commit;
try {
@@ -147,12 +146,7 @@
+ " points to dangling object " + ref.getObjectId());
continue;
}
- Set<Ref> relatedRefs = result.get(commit);
- if (relatedRefs == null) {
- relatedRefs = new HashSet<Ref>();
- result.put(commit, relatedRefs);
- }
- relatedRefs.add(ref);
+ result.put(commit, ref.getName());
}
return result;
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Mergeable.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Mergeable.java
index 852b379..2cc7dc8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Mergeable.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Mergeable.java
@@ -28,6 +28,7 @@
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.MergeException;
import com.google.gerrit.server.git.SubmitStrategyFactory;
+import com.google.gerrit.server.index.ChangeIndexer;
import com.google.gerrit.server.project.NoSuchProjectException;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
@@ -66,16 +67,19 @@
private final GitRepositoryManager gitManager;
private final SubmitStrategyFactory submitStrategyFactory;
private final Provider<ReviewDb> db;
+ private final ChangeIndexer indexer;
@Inject
Mergeable(TestSubmitType.Get submitType,
GitRepositoryManager gitManager,
SubmitStrategyFactory submitStrategyFactory,
- Provider<ReviewDb> db) {
+ Provider<ReviewDb> db,
+ ChangeIndexer indexer) {
this.submitType = submitType;
this.gitManager = gitManager;
this.submitStrategyFactory = submitStrategyFactory;
this.db = db;
+ this.indexer = indexer;
}
@Override
@@ -172,6 +176,7 @@
c.setMergeable(mergeable);
c.setLastSha1MergeTested(toRevId(ref));
db.get().changes().update(Collections.singleton(c));
+ indexer.index(c);
}
return mergeable;
} catch (MergeException e) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Publish.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Publish.java
index f28f342..d49527d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Publish.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Publish.java
@@ -92,7 +92,7 @@
}
private Change updateDraftChange(RevisionResource rsrc) throws OrmException {
- Change updatedChange = dbProvider.get().changes()
+ return dbProvider.get().changes()
.atomicUpdate(rsrc.getChange().getId(),
new AtomicUpdate<Change>() {
@Override
@@ -104,11 +104,10 @@
return change;
}
});
- return updatedChange;
}
private PatchSet updateDraftPatchSet(RevisionResource rsrc) throws OrmException {
- final PatchSet updatedPatchSet = dbProvider.get().patchSets()
+ return dbProvider.get().patchSets()
.atomicUpdate(rsrc.getPatchSet().getId(),
new AtomicUpdate<PatchSet>() {
@Override
@@ -117,7 +116,6 @@
return patchset;
}
});
- return updatedPatchSet;
}
@Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
index b719a0b..89e2f1a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Submit.java
@@ -163,7 +163,7 @@
*/
public ChangeMessage getConflictMessage(RevisionResource rsrc)
throws OrmException {
- ChangeMessage msg = Iterables.getFirst(Iterables.filter(
+ return Iterables.getFirst(Iterables.filter(
Lists.reverse(dbProvider.get().changeMessages()
.byChange(rsrc.getChange().getId())
.toList()),
@@ -173,7 +173,6 @@
return input.getAuthor() == null;
}
}), null);
- return msg;
}
public Change submit(RevisionResource rsrc, IdentifiedUser caller)
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 87f5271..4b0600a 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -23,6 +23,7 @@
import com.google.gerrit.extensions.config.DownloadCommand;
import com.google.gerrit.extensions.config.DownloadScheme;
import com.google.gerrit.extensions.events.GitReferenceUpdatedListener;
+import com.google.gerrit.extensions.events.HeadUpdatedListener;
import com.google.gerrit.extensions.events.LifecycleListener;
import com.google.gerrit.extensions.events.NewProjectCreatedListener;
import com.google.gerrit.extensions.events.ProjectDeletedListener;
@@ -241,6 +242,7 @@
DynamicSet.setOf(binder(), GitReferenceUpdatedListener.class);
DynamicSet.setOf(binder(), NewProjectCreatedListener.class);
DynamicSet.setOf(binder(), ProjectDeletedListener.class);
+ DynamicSet.setOf(binder(), HeadUpdatedListener.class);
DynamicSet.bind(binder(), GitReferenceUpdatedListener.class).to(ChangeCache.class);
DynamicSet.setOf(binder(), ChangeListener.class);
DynamicSet.setOf(binder(), CommitValidationListener.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java
index 2f8e26d..05b392b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetHead.java
@@ -15,6 +15,8 @@
package com.google.gerrit.server.project;
import com.google.common.base.Strings;
+import com.google.gerrit.extensions.events.HeadUpdatedListener;
+import com.google.gerrit.extensions.registration.DynamicSet;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.extensions.restapi.DefaultInput;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
@@ -31,10 +33,14 @@
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.RefUpdate;
import org.eclipse.jgit.lib.Repository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.io.IOException;
public class SetHead implements RestModifyView<ProjectResource, Input> {
+ private static final Logger log = LoggerFactory.getLogger(SetHead.class);
+
static class Input {
@DefaultInput
String ref;
@@ -42,15 +48,19 @@
private final GitRepositoryManager repoManager;
private final Provider<IdentifiedUser> identifiedUser;
+ private final DynamicSet<HeadUpdatedListener> headUpdatedListener;
@Inject
- SetHead(GitRepositoryManager repoManager, Provider<IdentifiedUser> identifiedUser) {
+ SetHead(GitRepositoryManager repoManager,
+ Provider<IdentifiedUser> identifiedUser,
+ DynamicSet<HeadUpdatedListener> headUpdatedListener) {
this.repoManager = repoManager;
this.identifiedUser = identifiedUser;
+ this.headUpdatedListener = headUpdatedListener;
}
@Override
- public String apply(ProjectResource rsrc, Input input) throws AuthException,
+ public String apply(final ProjectResource rsrc, Input input) throws AuthException,
ResourceNotFoundException, BadRequestException,
UnprocessableEntityException, IOException {
if (!rsrc.getControl().isOwner()) {
@@ -72,10 +82,12 @@
"Ref Not Found: %s", ref));
}
- if (!repo.getRef(Constants.HEAD).getTarget().getName().equals(ref)) {
+ final String oldHead = repo.getRef(Constants.HEAD).getTarget().getName();
+ final String newHead = ref;
+ if (!oldHead.equals(newHead)) {
final RefUpdate u = repo.updateRef(Constants.HEAD, true);
u.setRefLogIdent(identifiedUser.get().newRefLogIdent());
- RefUpdate.Result res = u.link(ref);
+ RefUpdate.Result res = u.link(newHead);
switch(res) {
case NO_CHANGE:
case RENAMED:
@@ -85,6 +97,30 @@
default:
throw new IOException("Setting HEAD failed with " + res);
}
+
+ HeadUpdatedListener.Event event = new HeadUpdatedListener.Event() {
+ @Override
+ public String getProjectName() {
+ return rsrc.getNameKey().get();
+ }
+
+ @Override
+ public String getOldHeadName() {
+ return oldHead;
+ }
+
+ @Override
+ public String getNewHeadName() {
+ return newHead;
+ }
+ };
+ for (HeadUpdatedListener l : headUpdatedListener) {
+ try {
+ l.onHeadUpdated(event);
+ } catch (RuntimeException e) {
+ log.warn("Failure in HeadUpdatedListener", e);
+ }
+ }
}
return ref;
} catch (RepositoryNotFoundException e) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetParent.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetParent.java
index 337c6f4..dd989a5 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/SetParent.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/SetParent.java
@@ -62,7 +62,7 @@
throws AuthException, ResourceConflictException,
ResourceNotFoundException, UnprocessableEntityException, IOException {
ProjectControl ctl = rsrc.getControl();
- validateParentUpdate(ctl, input.parent);
+ validateParentUpdate(ctl, input.parent, true);
IdentifiedUser user = (IdentifiedUser) ctl.getCurrentUser();
try {
MetaDataUpdate md = updateFactory.create(rsrc.getNameKey());
@@ -97,11 +97,11 @@
}
}
- public void validateParentUpdate(final ProjectControl ctl, String newParent)
- throws AuthException, ResourceConflictException,
+ public void validateParentUpdate(final ProjectControl ctl, String newParent,
+ boolean checkIfAdmin) throws AuthException, ResourceConflictException,
UnprocessableEntityException {
IdentifiedUser user = (IdentifiedUser) ctl.getCurrentUser();
- if (!user.getCapabilities().canAdministrateServer()) {
+ if (checkIfAdmin && !user.getCapabilities().canAdministrateServer()) {
throw new AuthException("not administrator");
}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
index ac4109a..d345e20 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryProcessor.java
@@ -233,6 +233,7 @@
* there are more than {@code limit} matches and suggest to its own caller
* that the query could be retried with {@link #setSortkeyBefore(String)}.
*/
+ @SuppressWarnings("unchecked")
public List<List<ChangeData>> queryChanges(List<String> queries)
throws OrmException, QueryParseException {
final Predicate<ChangeData> visibleToMe = queryBuilder.is_visible();
@@ -241,13 +242,11 @@
// Parse and rewrite all queries.
List<Integer> limits = Lists.newArrayListWithCapacity(cnt);
List<ChangeDataSource> sources = Lists.newArrayListWithCapacity(cnt);
- for (int i = 0; i < cnt; i++) {
- Predicate<ChangeData> q = parseQuery(queries.get(i), visibleToMe);
+ for (String query : queries) {
+ Predicate<ChangeData> q = parseQuery(query, visibleToMe);
Predicate<ChangeData> s = queryRewriter.rewrite(q);
if (!(s instanceof ChangeDataSource)) {
- @SuppressWarnings("unchecked")
- Predicate<ChangeData> o = Predicate.and(queryBuilder.status_open(), q);
- q = o;
+ q = Predicate.and(queryBuilder.status_open(), q);
s = queryRewriter.rewrite(q);
}
if (!(s instanceof ChangeDataSource)) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/util/LogUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/util/LogUtil.java
new file mode 100644
index 0000000..ba39a26
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/util/LogUtil.java
@@ -0,0 +1,26 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.util;
+
+import com.google.common.base.Strings;
+
+public class LogUtil {
+
+ private static final String LOG4J_CONFIGURATION = "log4j.configuration";
+
+ public static boolean shouldConfigureLogSystem() {
+ return Strings.isNullOrEmpty(System.getProperty(LOG4J_CONFIGURATION));
+ }
+}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java
index af31eaf..4f20b63 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java
@@ -299,9 +299,7 @@
final Account account = new Account(userId, TimeUtil.nowTs());
account.setFullName(name);
account.setPreferredEmail(email);
- final AccountState s =
- new AccountState(account, Collections.<AccountGroup.UUID> emptySet(),
- Collections.<AccountExternalId> emptySet());
- return s;
+ return new AccountState(account, Collections.<AccountGroup.UUID> emptySet(),
+ Collections.<AccountExternalId> emptySet());
}
}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryDatabase.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryDatabase.java
index 189f803..bc344fa 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryDatabase.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryDatabase.java
@@ -57,8 +57,7 @@
final Properties p = new Properties();
p.setProperty("driver", org.h2.Driver.class.getName());
p.setProperty("url", "jdbc:h2:mem:" + "Test_" + (++dbCnt));
- final DataSource dataSource = new SimpleDataSource(p);
- return dataSource;
+ return new SimpleDataSource(p);
}
/** Drop the database from memory; does nothing if the instance was null. */
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLog.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLog.java
index a78f9f5..fac0340 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLog.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLog.java
@@ -25,6 +25,7 @@
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.util.IdGenerator;
+import com.google.gerrit.server.util.LogUtil;
import com.google.gerrit.server.util.TimeUtil;
import com.google.gerrit.sshd.SshScope.Context;
import com.google.inject.Inject;
@@ -34,20 +35,14 @@
import org.apache.log4j.Appender;
import org.apache.log4j.AsyncAppender;
import org.apache.log4j.DailyRollingFileAppender;
-import org.apache.log4j.Layout;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.spi.ErrorHandler;
import org.apache.log4j.spi.LoggingEvent;
import org.eclipse.jgit.lib.Config;
-import org.eclipse.jgit.util.QuotedString;
import java.io.File;
import java.io.IOException;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.TimeZone;
@Singleton
class SshLog implements LifecycleListener {
@@ -72,10 +67,15 @@
this.context = context;
this.auditService = auditService;
- if (config.getBoolean("sshd", "requestLog", true)) {
+ async = new AsyncAppender();
+ async.setBlocking(true);
+ async.setBufferSize(config.getInt("core", "asyncLoggingBufferSize", 64));
+ async.setLocationInfo(false);
+
+ if (LogUtil.shouldConfigureLogSystem()) {
final DailyRollingFileAppender dst = new DailyRollingFileAppender();
dst.setName(LOG_NAME);
- dst.setLayout(new MyLayout());
+ dst.setLayout(new SshLogLayout());
dst.setEncoding("UTF-8");
dst.setFile(new File(resolve(site.logs_dir), LOG_NAME).getPath());
dst.setImmediateFlush(true);
@@ -84,16 +84,18 @@
dst.setErrorHandler(new DieErrorHandler());
dst.activateOptions();
dst.setErrorHandler(new LogLogHandler());
-
- async = new AsyncAppender();
- async.setBlocking(true);
- async.setBufferSize(config.getInt("core", "asyncLoggingBufferSize", 64));
- async.setLocationInfo(false);
async.addAppender(dst);
- async.activateOptions();
} else {
- async = null;
+ Appender appender = log.getAppender(LOG_NAME);
+ if (appender != null) {
+ async.addAppender(appender);
+ } else {
+ log.warn("No appender with the name: "
+ + LOG_NAME
+ + " was found. SSHD logging is disabled");
+ }
}
+ async.activateOptions();
}
@Override
@@ -277,126 +279,6 @@
}
}
- private static final class MyLayout extends Layout {
- private final Calendar calendar;
- private long lastTimeMillis;
- private final char[] lastTimeString = new char[20];
- private final char[] timeZone;
-
- MyLayout() {
- final TimeZone tz = TimeZone.getDefault();
- calendar = Calendar.getInstance(tz);
-
- final SimpleDateFormat sdf = new SimpleDateFormat("Z");
- sdf.setTimeZone(tz);
- timeZone = sdf.format(new Date()).toCharArray();
- }
-
- @Override
- public String format(LoggingEvent event) {
- final StringBuffer buf = new StringBuffer(128);
-
- buf.append('[');
- formatDate(event.getTimeStamp(), buf);
- buf.append(' ');
- buf.append(timeZone);
- buf.append(']');
-
- req(P_SESSION, buf, event);
- req(P_USER_NAME, buf, event);
- req(P_ACCOUNT_ID, buf, event);
-
- buf.append(' ');
- buf.append(event.getMessage());
-
- opt(P_WAIT, buf, event);
- opt(P_EXEC, buf, event);
- opt(P_STATUS, buf, event);
-
- buf.append('\n');
- return buf.toString();
- }
-
- private void formatDate(final long now, final StringBuffer sbuf) {
- final int millis = (int) (now % 1000);
- final long rounded = now - millis;
- if (rounded != lastTimeMillis) {
- synchronized (calendar) {
- final int start = sbuf.length();
-
- calendar.setTimeInMillis(rounded);
- sbuf.append(calendar.get(Calendar.YEAR));
- sbuf.append('-');
- final int month = calendar.get(Calendar.MONTH) + 1;
- if (month < 10) sbuf.append('0');
- sbuf.append(month);
- sbuf.append('-');
- final int day = calendar.get(Calendar.DAY_OF_MONTH);
- if (day < 10) sbuf.append('0');
- sbuf.append(day);
-
- sbuf.append(' ');
- final int hour = calendar.get(Calendar.HOUR_OF_DAY);
- if (hour < 10) sbuf.append('0');
- sbuf.append(hour);
- sbuf.append(':');
- final int mins = calendar.get(Calendar.MINUTE);
- if (mins < 10) sbuf.append('0');
- sbuf.append(mins);
- sbuf.append(':');
- final int secs = calendar.get(Calendar.SECOND);
- if (secs < 10) sbuf.append('0');
- sbuf.append(secs);
-
- sbuf.append(',');
- sbuf.getChars(start, sbuf.length(), lastTimeString, 0);
- lastTimeMillis = rounded;
- }
- } else {
- sbuf.append(lastTimeString);
- }
- if (millis < 100) {
- sbuf.append('0');
- }
- if (millis < 10) {
- sbuf.append('0');
- }
- sbuf.append(millis);
- }
-
- private void req(String key, StringBuffer buf, LoggingEvent event) {
- Object val = event.getMDC(key);
- buf.append(' ');
- if (val != null) {
- String s = val.toString();
- if (0 <= s.indexOf(' ')) {
- buf.append(QuotedString.BOURNE.quote(s));
- } else {
- buf.append(val);
- }
- } else {
- buf.append('-');
- }
- }
-
- private void opt(String key, StringBuffer buf, LoggingEvent event) {
- Object val = event.getMDC(key);
- if (val != null) {
- buf.append(' ');
- buf.append(val);
- }
- }
-
- @Override
- public boolean ignoresThrowable() {
- return true;
- }
-
- @Override
- public void activateOptions() {
- }
- }
-
private static final class DieErrorHandler implements ErrorHandler {
@Override
public void error(String message, Exception e, int errorCode,
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLogLayout.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLogLayout.java
new file mode 100644
index 0000000..af8f9b7e
--- /dev/null
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshLogLayout.java
@@ -0,0 +1,153 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.sshd;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.spi.LoggingEvent;
+import org.eclipse.jgit.util.QuotedString;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+
+public final class SshLogLayout extends Layout {
+
+ private static final String P_SESSION = "session";
+ private static final String P_USER_NAME = "userName";
+ private static final String P_ACCOUNT_ID = "accountId";
+ private static final String P_WAIT = "queueWaitTime";
+ private static final String P_EXEC = "executionTime";
+ private static final String P_STATUS = "status";
+
+ private final Calendar calendar;
+ private long lastTimeMillis;
+ private final char[] lastTimeString = new char[20];
+ private final char[] timeZone;
+
+ public SshLogLayout() {
+ final TimeZone tz = TimeZone.getDefault();
+ calendar = Calendar.getInstance(tz);
+
+ final SimpleDateFormat sdf = new SimpleDateFormat("Z");
+ sdf.setTimeZone(tz);
+ timeZone = sdf.format(new Date()).toCharArray();
+ }
+
+ @Override
+ public String format(LoggingEvent event) {
+ final StringBuffer buf = new StringBuffer(128);
+
+ buf.append('[');
+ formatDate(event.getTimeStamp(), buf);
+ buf.append(' ');
+ buf.append(timeZone);
+ buf.append(']');
+
+ req(P_SESSION, buf, event);
+ req(P_USER_NAME, buf, event);
+ req(P_ACCOUNT_ID, buf, event);
+
+ buf.append(' ');
+ buf.append(event.getMessage());
+
+ opt(P_WAIT, buf, event);
+ opt(P_EXEC, buf, event);
+ opt(P_STATUS, buf, event);
+
+ buf.append('\n');
+ return buf.toString();
+ }
+
+ private void formatDate(final long now, final StringBuffer sbuf) {
+ final int millis = (int) (now % 1000);
+ final long rounded = now - millis;
+ if (rounded != lastTimeMillis) {
+ synchronized (calendar) {
+ final int start = sbuf.length();
+
+ calendar.setTimeInMillis(rounded);
+ sbuf.append(calendar.get(Calendar.YEAR));
+ sbuf.append('-');
+ final int month = calendar.get(Calendar.MONTH) + 1;
+ if (month < 10) sbuf.append('0');
+ sbuf.append(month);
+ sbuf.append('-');
+ final int day = calendar.get(Calendar.DAY_OF_MONTH);
+ if (day < 10) sbuf.append('0');
+ sbuf.append(day);
+
+ sbuf.append(' ');
+ final int hour = calendar.get(Calendar.HOUR_OF_DAY);
+ if (hour < 10) sbuf.append('0');
+ sbuf.append(hour);
+ sbuf.append(':');
+ final int mins = calendar.get(Calendar.MINUTE);
+ if (mins < 10) sbuf.append('0');
+ sbuf.append(mins);
+ sbuf.append(':');
+ final int secs = calendar.get(Calendar.SECOND);
+ if (secs < 10) sbuf.append('0');
+ sbuf.append(secs);
+
+ sbuf.append(',');
+ sbuf.getChars(start, sbuf.length(), lastTimeString, 0);
+ lastTimeMillis = rounded;
+ }
+ } else {
+ sbuf.append(lastTimeString);
+ }
+ if (millis < 100) {
+ sbuf.append('0');
+ }
+ if (millis < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(millis);
+ }
+
+ private void req(String key, StringBuffer buf, LoggingEvent event) {
+ Object val = event.getMDC(key);
+ buf.append(' ');
+ if (val != null) {
+ String s = val.toString();
+ if (0 <= s.indexOf(' ')) {
+ buf.append(QuotedString.BOURNE.quote(s));
+ } else {
+ buf.append(val);
+ }
+ } else {
+ buf.append('-');
+ }
+ }
+
+ private void opt(String key, StringBuffer buf, LoggingEvent event) {
+ Object val = event.getMDC(key);
+ if (val != null) {
+ buf.append(' ');
+ buf.append(val);
+ }
+ }
+
+ @Override
+ public boolean ignoresThrowable() {
+ return true;
+ }
+
+ @Override
+ public void activateOptions() {
+ }
+}
diff --git a/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/OptionHandlers.java b/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/OptionHandlers.java
index 756a885..50cad12 100644
--- a/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/OptionHandlers.java
+++ b/gerrit-util-cli/src/main/java/com/google/gerrit/util/cli/OptionHandlers.java
@@ -73,9 +73,8 @@
return (Class<?>) p.getActualTypeArguments()[0];
}
+ @SuppressWarnings("unchecked")
private static Binding<OptionHandlerFactory<?>> cast(Binding<?> e) {
- @SuppressWarnings("unchecked")
- Binding<OptionHandlerFactory<?>> b = (Binding<OptionHandlerFactory<?>>) e;
- return b;
+ return (Binding<OptionHandlerFactory<?>>) e;
}
}