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&#8230;</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&#8230;</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;
   }
 }