Merge "Revert "Additional log on plug-in self-registration and failures.""
diff --git a/Documentation/access-control.txt b/Documentation/access-control.txt
index 23f6e72..7027562 100644
--- a/Documentation/access-control.txt
+++ b/Documentation/access-control.txt
@@ -452,6 +452,9 @@
 to projects in Gerrit. It can give permission to abandon a specific
 change to a given ref.
 
+This also grants the permission to restore a change if the change
+can be uploaded.
+
 [[category_create]]
 Create reference
 ~~~~~~~~~~~~~~~~
diff --git a/Documentation/cmd-cherry-pick.txt b/Documentation/cmd-cherry-pick.txt
index 568c872..d051a9a 100644
--- a/Documentation/cmd-cherry-pick.txt
+++ b/Documentation/cmd-cherry-pick.txt
@@ -39,7 +39,7 @@
 ====
   $ scp -p -P 29418 john.doe@review.example.com:bin/gerrit-cherry-pick ~/bin/
 
-  $ curl http://review.example.com/tools/bin/gerrit-cherry-pick
+  $ curl -o ~/bin/gerrit-cherry-pick http://review.example.com/tools/bin/gerrit-cherry-pick
 ====
 
 GERRIT
diff --git a/Documentation/cmd-index.txt b/Documentation/cmd-index.txt
index 4c0560e..c4f222b 100644
--- a/Documentation/cmd-index.txt
+++ b/Documentation/cmd-index.txt
@@ -12,8 +12,8 @@
   $ scp -p -P 29418 john.doe@review.example.com:bin/gerrit-cherry-pick ~/bin/
   $ scp -p -P 29418 john.doe@review.example.com:hooks/commit-msg .git/hooks/
 
-  $ curl http://review.example.com/tools/bin/gerrit-cherry-pick
-  $ curl http://review.example.com/tools/hooks/commit-msg
+  $ curl -o ~/bin/gerrit-cherry-pick http://review.example.com/tools/bin/gerrit-cherry-pick
+  $ curl -o .git/hooks/commit-msg http://review.example.com/tools/hooks/commit-msg
 
 For more details on how to determine the correct SSH port number,
 see link:user-upload.html#test_ssh[Testing Your SSH Connection].
diff --git a/Documentation/cmd-review.txt b/Documentation/cmd-review.txt
index ac613e5..eed6902 100644
--- a/Documentation/cmd-review.txt
+++ b/Documentation/cmd-review.txt
@@ -52,7 +52,7 @@
 
 --force-message::
 	Option which allows Gerrit to publish the --message, even
-	when the labels could not be applied due to change being
+	when the labels could not be applied due to the change being
 	closed).
 +
 Used by some scripts/CI-systems, where the results (or links
@@ -69,11 +69,11 @@
 	complete listing of supported approval categories and values.
 
 --abandon::
-	Abandon the specified patch set(s).
+	Abandon the specified change(s).
 	(option is mutually exclusive with --submit and --restore)
 
 --restore::
-	Restore the specified abandoned patch set(s).
+	Restore the specified abandoned change(s).
 	(option is mutually exclusive with --abandon)
 
 --submit::
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index cb70254..8610dba 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -88,6 +88,12 @@
 provider chosen by the end-user.  For more information see
 http://openid.net/[openid.net].
 +
+* `OpenID_SSO`
++
+Supports OpenID from a single provider.  There is no registration
+link, and the "Sign In" link sends the user directly to the provider's
+SSO entry point.
++
 * `HTTP`
 +
 Gerrit relies upon data presented in the HTTP request.  This includes
@@ -229,6 +235,13 @@
 +
 Default is 12 hours.
 
+[[auth.openIdSsoUrl]]auth.openIdSsoUrl::
++
+The SSO entry point URL.  Only used if `auth.type` was set to
+OpenID_SSO.
++
+The "Sign In" link will send users directly to this URL.
+
 [[auth.httpHeader]]auth.httpHeader::
 +
 HTTP header to trust the username from, or unset to select HTTP basic
@@ -2237,6 +2250,31 @@
 +
 By default a shade of yellow, `FFFFCC`.
 
+[[theme.changeTableOutdatedColor]]theme.changeTableOutdatedColor::
++
+Background color used for patch outdated messages.  The value must be
+a valid HTML hex color code, or standard color name.
++
+By default a shade of red, `FF0000`.
+
+[[theme.tableOddRowColor]]theme.tableOddRowColor::
++
+Background color for tables such as lists of open reviews for odd
+rows.  This is so you can have a different color for odd and even
+rows of the table.  The value must be a valid HTML hex color code,
+or standard color name.
++
+By default transparent.
+
+[[theme.tableEvenRowColor]]theme.tableEvenRowColor::
++
+Background color for tables such as lists of open reviews for even
+rows.  This is so you can have a different color for odd and even
+rows of the table.  The value must be a valid HTML hex color code,
+or standard color name.
++
+By default transparent.
+
 A different theme may be used for signed-in vs. signed-out user status
 by using the "signed-in" and "signed-out" theme sections. Variables
 not specified in a section are inherited from the default theme.
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index 586ae07..7832aa9 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -192,7 +192,7 @@
 by PrintHello class will be available to users as:
 
 ----
-$ ssh -P 29418 review.example.com helloworld print
+$ ssh -p 29418 review.example.com helloworld print
 ----
 
 HTTP Servlets
@@ -312,7 +312,7 @@
   the plugin from this location to its own site path.
 +
 ----
-$ ssh -P 29418 localhost gerrit plugin install -n name $(pwd)/my-plugin.jar
+$ ssh -p 29418 localhost gerrit plugin install -n name $(pwd)/my-plugin.jar
 ----
 
 * Valid URL, including any HTTP or FTP site reachable by the
@@ -320,14 +320,14 @@
   its own site path.
 +
 ----
-$ ssh -P 29418 localhost gerrit plugin install -n name http://build-server/output/our-plugin.jar
+$ ssh -p 29418 localhost gerrit plugin install -n name http://build-server/output/our-plugin.jar
 ----
 
 * As piped input to the plugin install command. The server will
   copy input until EOF, and save a copy under its own site path.
 +
 ----
-$ ssh -P 29418 localhost gerrit plugin install -n name - <target/name-0.1.jar
+$ ssh -p 29418 localhost gerrit plugin install -n name - <target/name-0.1.jar
 ----
 
 Plugins can also be copied directly into the server's
diff --git a/Documentation/index.txt b/Documentation/index.txt
index 2b53772..c99d26c 100644
--- a/Documentation/index.txt
+++ b/Documentation/index.txt
@@ -48,6 +48,7 @@
 * link:dev-readme.html[Developer Setup]
 * link:dev-eclipse.html[Eclipse Setup]
 * link:dev-contributing.html[Contributing to Gerrit]
+* link:dev-plugins.html[Developing Plugins]
 * link:dev-design.html[System Design]
 * link:i18n-readme.html[i18n Support]
 * link:dev-release.html[Developer Release]
diff --git a/Documentation/user-changeid.txt b/Documentation/user-changeid.txt
index 409bb32..a3015e1 100644
--- a/Documentation/user-changeid.txt
+++ b/Documentation/user-changeid.txt
@@ -46,11 +46,13 @@
 Gerrit Code Review provides a standard 'commit-msg' hook which
 can be installed in the local Git repository to automatically
 create and insert a unique Change-Id line during `git commit`.
-To install the hook, copy it from Gerrit's daemon:
+To install the hook, copy it from Gerrit's daemon by executing
+one of the following commands while being in the root directory
+of the local Git repository:
 
   $ scp -p -P 29418 john.doe@review.example.com:hooks/commit-msg .git/hooks/
 
-  $ curl http://review.example.com/tools/hooks/commit-msg
+  $ curl -o .git/hooks/commit-msg http://review.example.com/tools/hooks/commit-msg
 
 For more details, see link:cmd-hook-commit-msg.html[commit-msg].
 
diff --git a/gerrit-antlr/.settings/org.eclipse.core.resources.prefs b/gerrit-antlr/.settings/org.eclipse.core.resources.prefs
index 589908f..e9441bb 100644
--- a/gerrit-antlr/.settings/org.eclipse.core.resources.prefs
+++ b/gerrit-antlr/.settings/org.eclipse.core.resources.prefs
@@ -1,4 +1,3 @@
-#Thu Jul 28 11:02:35 PDT 2011
 eclipse.preferences.version=1
 encoding//src/main/java=UTF-8
 encoding/<project>=UTF-8
diff --git a/gerrit-cache-h2/.settings/org.eclipse.core.resources.prefs b/gerrit-cache-h2/.settings/org.eclipse.core.resources.prefs
index fc11c3f..f9fe345 100644
--- a/gerrit-cache-h2/.settings/org.eclipse.core.resources.prefs
+++ b/gerrit-cache-h2/.settings/org.eclipse.core.resources.prefs
@@ -1,4 +1,3 @@
-#Thu Jul 28 11:02:36 PDT 2011
 eclipse.preferences.version=1
 encoding//src/main/java=UTF-8
 encoding//src/test/java=UTF-8
diff --git a/gerrit-common/.settings/org.eclipse.core.resources.prefs b/gerrit-common/.settings/org.eclipse.core.resources.prefs
index fc11c3f..f9fe345 100644
--- a/gerrit-common/.settings/org.eclipse.core.resources.prefs
+++ b/gerrit-common/.settings/org.eclipse.core.resources.prefs
@@ -1,4 +1,3 @@
-#Thu Jul 28 11:02:36 PDT 2011
 eclipse.preferences.version=1
 encoding//src/main/java=UTF-8
 encoding//src/test/java=UTF-8
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java
index 07a8534..89de3b4 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GerritConfig.java
@@ -28,6 +28,7 @@
 public class GerritConfig implements Cloneable {
   protected String registerUrl;
   protected String httpPasswordUrl;
+  protected String openIdSsoUrl;
   protected List<OpenIdProviderPattern> allowedOpenIDs;
 
   protected GitwebConfig gitweb;
@@ -72,6 +73,14 @@
     httpPasswordUrl = url;
   }
 
+  public String getOpenIdSsoUrl() {
+      return openIdSsoUrl;
+  }
+
+  public void setOpenIdSsoUrl(final String u) {
+    openIdSsoUrl = u;
+  }
+
   public List<OpenIdProviderPattern> getAllowedOpenIDs() {
     return allowedOpenIDs;
   }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java
index c3d3f1e..f991f4c 100644
--- a/gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java
+++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/HostPageData.java
@@ -31,5 +31,8 @@
     public String textColor;
     public String trimColor;
     public String selectionColor;
+    public String changeTableOutdatedColor;
+    public String tableOddRowColor;
+    public String tableEvenRowColor;
   }
 }
diff --git a/gerrit-extension-api/.settings/org.eclipse.core.resources.prefs b/gerrit-extension-api/.settings/org.eclipse.core.resources.prefs
index fc11c3f..f9fe345 100644
--- a/gerrit-extension-api/.settings/org.eclipse.core.resources.prefs
+++ b/gerrit-extension-api/.settings/org.eclipse.core.resources.prefs
@@ -1,4 +1,3 @@
-#Thu Jul 28 11:02:36 PDT 2011
 eclipse.preferences.version=1
 encoding//src/main/java=UTF-8
 encoding//src/test/java=UTF-8
diff --git a/gerrit-gwtdebug/.settings/org.eclipse.core.resources.prefs b/gerrit-gwtdebug/.settings/org.eclipse.core.resources.prefs
index 36e1448..e9441bb 100644
--- a/gerrit-gwtdebug/.settings/org.eclipse.core.resources.prefs
+++ b/gerrit-gwtdebug/.settings/org.eclipse.core.resources.prefs
@@ -1,4 +1,3 @@
-#Thu Jul 28 11:02:38 PDT 2011
 eclipse.preferences.version=1
 encoding//src/main/java=UTF-8
 encoding/<project>=UTF-8
diff --git a/gerrit-gwtui/.settings/org.eclipse.core.resources.prefs b/gerrit-gwtui/.settings/org.eclipse.core.resources.prefs
index c780f44..e9441bb 100644
--- a/gerrit-gwtui/.settings/org.eclipse.core.resources.prefs
+++ b/gerrit-gwtui/.settings/org.eclipse.core.resources.prefs
@@ -1,4 +1,3 @@
-#Thu Jul 28 11:02:36 PDT 2011
 eclipse.preferences.version=1
 encoding//src/main/java=UTF-8
 encoding/<project>=UTF-8
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
index fcc2db1..267419f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Gerrit.java
@@ -18,6 +18,7 @@
 
 import com.google.gerrit.client.account.AccountCapabilities;
 import com.google.gerrit.client.auth.openid.OpenIdSignInDialog;
+import com.google.gerrit.client.auth.openid.OpenIdSsoPanel;
 import com.google.gerrit.client.auth.userpass.UserPassSignInDialog;
 import com.google.gerrit.client.changes.ChangeConstants;
 import com.google.gerrit.client.changes.ChangeListScreen;
@@ -258,6 +259,13 @@
         Location.assign(selfRedirect("/become"));
         break;
 
+      case OPENID_SSO:
+        final RootPanel gBody = RootPanel.get("gerrit_body");
+        OpenIdSsoPanel singleSignOnPanel = new OpenIdSsoPanel();
+        gBody.add(singleSignOnPanel);
+        singleSignOnPanel.authenticate(SignInMode.SIGN_IN, token);
+        break;
+
       case OPENID:
         new OpenIdSignInDialog(SignInMode.SIGN_IN, token, null).center();
         break;
@@ -627,6 +635,14 @@
           });
           break;
 
+        case OPENID_SSO:
+          menuRight.addItem(C.menuSignIn(), new Command() {
+            public void execute() {
+              doSignIn(History.getToken());
+            }
+          });
+          break;
+
         case LDAP:
         case LDAP_BIND:
         case CUSTOM_EXTENSION:
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdSsoPanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdSsoPanel.java
new file mode 100644
index 0000000..3dd54a7
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/auth/openid/OpenIdSsoPanel.java
@@ -0,0 +1,70 @@
+// Copyright (C) 2012 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.auth.openid;
+
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.client.rpc.GerritCallback;
+import com.google.gerrit.client.ui.SmallHeading;
+import com.google.gerrit.common.auth.SignInMode;
+import com.google.gerrit.common.auth.openid.DiscoveryResult;
+import com.google.gerrit.common.auth.openid.OpenIdUrls;
+import com.google.gwt.dom.client.FormElement;
+import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.FormPanel;
+import com.google.gwt.user.client.ui.Hidden;
+
+import java.util.Map;
+
+public class OpenIdSsoPanel extends FlowPanel {
+  private final FormPanel redirectForm;
+  private final FlowPanel redirectBody;
+  private final String ssoUrl;
+
+  public OpenIdSsoPanel() {
+    super();
+    redirectBody = new FlowPanel();
+    redirectBody.setVisible(false);
+    redirectForm = new FormPanel();
+    redirectForm.add(redirectBody);
+
+    add(redirectForm);
+
+    ssoUrl = Gerrit.getConfig().getOpenIdSsoUrl();
+  }
+
+  public void authenticate(SignInMode requestedMode, final String token) {
+    OpenIdUtil.SVC.discover(ssoUrl, requestedMode, /* remember */ false, token,
+        new GerritCallback<DiscoveryResult>() {
+          public void onSuccess(final DiscoveryResult result) {
+            onDiscovery(result);
+          }
+        });
+  }
+
+  private void onDiscovery(final DiscoveryResult result) {
+    switch (result.status) {
+      case VALID:
+        redirectForm.setMethod(FormPanel.METHOD_POST);
+        redirectForm.setAction(result.providerUrl);
+        redirectBody.clear();
+        for (final Map.Entry<String, String> e : result.providerArgs.entrySet()) {
+          redirectBody.add(new Hidden(e.getKey(), e.getValue()));
+        }
+        FormElement.as(redirectForm.getElement()).setTarget("_top");
+        redirectForm.submit();
+        break;
+    }
+  }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java
index 44a49a8..97bb4ca 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable.java
@@ -202,10 +202,7 @@
     }
     table.setWidget(row, C_ID, new TableChangeLink(idstr, c));
 
-    String s = c.getSubject();
-    if (s.length() > 80) {
-      s = s.substring(0, 80);
-    }
+    String s = Util.cropSubject(c.getSubject());
     if (c.getStatus() != null && c.getStatus() != Change.Status.NEW) {
       s += " (" + c.getStatus().name() + ")";
     }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable2.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable2.java
index fc2b418..0dd0b0f 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable2.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/ChangeTable2.java
@@ -200,10 +200,7 @@
     }
     table.setWidget(row, C_ID, new TableChangeLink(c.id_abbreviated(), c));
 
-    String subject = c.subject();
-    if (subject.length() > 80) {
-      subject = subject.substring(0, 80);
-    }
+    String subject = Util.cropSubject(c.subject());
     Change.Status status = c.status();
     if (status != Change.Status.NEW) {
       subject += " (" + Util.toLongString(status) + ")";
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
index 00baf28..ca8aedf 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
@@ -424,7 +424,8 @@
       parentsTable.setWidget(row, 0, new InlineLabel(parent.id.get()));
       ptfmt.addStyleName(row, 0, Gerrit.RESOURCES.css().noborder());
       ptfmt.addStyleName(row, 0, Gerrit.RESOURCES.css().monospace());
-      parentsTable.setWidget(row, 1, new InlineLabel(parent.shortMessage));
+      parentsTable.setWidget(row, 1,
+          new InlineLabel(Util.cropSubject(parent.shortMessage)));
       ptfmt.addStyleName(row, 1, Gerrit.RESOURCES.css().noborder());
       row++;
     }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/Util.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/Util.java
index e84cac8..590ad87 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/Util.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/Util.java
@@ -30,6 +30,10 @@
   public static final ChangeListService LIST_SVC;
   public static final ChangeManageService MANAGE_SVC;
 
+  private static final int SUBJECT_MAX_LENGTH = 80;
+  private static final String SUBJECT_CROP_APPENDIX = "...";
+  private static final int SUBJECT_CROP_RANGE = 10;
+
   static {
     DETAIL_SVC = GWT.create(ChangeDetailService.class);
     JsonUtil.bind(DETAIL_SVC, "rpc/ChangeDetailService");
@@ -60,4 +64,40 @@
         return status.name();
     }
   }
+
+  /**
+   * Crops the given change subject if needed so that it has at most
+   * {@link #SUBJECT_MAX_LENGTH} characters.
+   *
+   * If the given subject is not longer than {@link #SUBJECT_MAX_LENGTH}
+   * characters it is returned unchanged.
+   *
+   * If the length of the given subject exceeds {@link #SUBJECT_MAX_LENGTH}
+   * characters it is cropped. In this case {@link #SUBJECT_CROP_APPENDIX} is
+   * appended to the cropped subject, the cropped subject including the appendix
+   * has at most {@link #SUBJECT_MAX_LENGTH} characters.
+   *
+   * If cropping is needed, the subject will be cropped after the last space
+   * character that is found within the last {@link #SUBJECT_CROP_RANGE}
+   * characters of the potentially visible characters. If no such space is
+   * found, the subject will be cropped so that the cropped subject including
+   * the appendix has exactly {@link #SUBJECT_MAX_LENGTH} characters.
+   *
+   * @return the subject, cropped if needed
+   */
+  @SuppressWarnings("deprecation")
+  public static String cropSubject(final String subject) {
+    if (subject.length() > SUBJECT_MAX_LENGTH) {
+      final int maxLength = SUBJECT_MAX_LENGTH - SUBJECT_CROP_APPENDIX.length();
+      for (int cropPosition = maxLength; cropPosition > maxLength - SUBJECT_CROP_RANGE; cropPosition--) {
+        // Character.isWhitespace(char) can't be used because this method is not supported by GWT,
+        // see https://developers.google.com/web-toolkit/doc/1.6/RefJreEmulation#Package_java_lang
+        if (Character.isSpace(subject.charAt(cropPosition - 1))) {
+          return subject.substring(0, cropPosition) + SUBJECT_CROP_APPENDIX;
+        }
+      }
+      return subject.substring(0, maxLength) + SUBJECT_CROP_APPENDIX;
+    }
+    return subject;
+  }
 }
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 3789c6a..446b71d 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
@@ -43,7 +43,9 @@
 @eval textColor com.google.gerrit.client.Gerrit.getTheme().textColor;
 @eval trimColor com.google.gerrit.client.Gerrit.getTheme().trimColor;
 @eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor;
-
+@eval changeTableOutdatedColor com.google.gerrit.client.Gerrit.getTheme().changeTableOutdatedColor;
+@eval tableOddRowColor com.google.gerrit.client.Gerrit.getTheme().tableOddRowColor;
+@eval tableEvenRowColor com.google.gerrit.client.Gerrit.getTheme().tableEvenRowColor;
 
 @sprite .greenCheckClass {
   gwt-image: "greenCheck";
@@ -411,8 +413,16 @@
   border-spacing: 0;
 }
 
+.changeTable tr:nth-child\(even\) {
+  background: tableEvenRowColor;
+}
+
+.changeTable tr:nth-child\(odd\) {
+  background: tableOddRowColor;
+}
+
 .changeTable .outdated {
-  background: red;
+  background: changeTableOutdatedColor;
 }
 
 .changeTable .iconCell {
@@ -482,7 +492,6 @@
 
 .accountDashboard.changeTable tr {
   color: #444444;
-  background: #F6F6F6;
 }
 .accountDashboard.changeTable tr a {
   color: #444444;
@@ -492,13 +501,12 @@
 .accountDashboard.changeTable .needsReview a {
   font-weight: bold;
   color: textColor;
-  background: backgroundColor;
 }
 
 .changeTable .activeRow,
 .accountDashboard.changeTable .activeRow,
 .accountDashboard.changeTable .activeRow a {
-  background: selectionColor;
+  background: selectionColor !important;
 }
 
 .changeTable .cID {
@@ -1042,6 +1050,10 @@
   display: table;
 }
 
+.sideBySideScreenSideBySideTable .fileLine {
+  width: 50%;
+}
+
 .sideBySideScreenLinkTable {
   width: 100%;
 }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.java
index fd34729..d76bcca 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.java
@@ -31,6 +31,7 @@
   String patchHeaderPatchSet();
   String patchHeaderOld();
   String patchHeaderNew();
+  String patchSet();
 
   String patchHistoryTitle();
   String disabledOnLargeFiles();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.properties
index 23090a2..24c4140 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.properties
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchConstants.properties
@@ -15,6 +15,7 @@
 patchHeaderOld = Old Version
 patchHeaderNew = New Version
 patchHistoryTitle = Patch History
+patchSet = Patch Set
 disabledOnLargeFiles = Disabled on very large source files.
 intralineFailure = Intraline difference not available due to server error.
 illegalNumberOfColumns = The number of columns cannot be zero or negative
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScreen.java
index 77d8659..7089e2b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchScreen.java
@@ -106,6 +106,7 @@
   private HistoryTable historyTable;
   private FlowPanel topPanel;
   private FlowPanel contentPanel;
+  private PatchTableHeader header;
   private Label noDifference;
   private AbstractPatchContentTable contentTable;
   private CommitMessageBlock commitMessageBlock;
@@ -250,6 +251,8 @@
     topPanel = new FlowPanel();
     add(topPanel);
 
+    header = new PatchTableHeader(getPatchScreenType());
+
     noDifference = new Label(PatchUtil.C.noDifference());
     noDifference.setStyleName(Gerrit.RESOURCES.css().patchNoDifference());
     noDifference.setVisible(false);
@@ -264,6 +267,7 @@
     contentPanel = new FlowPanel();
     contentPanel.setStyleName(Gerrit.RESOURCES.css()
         .sideBySideScreenSideBySideTable());
+    contentPanel.add(header);
     contentPanel.add(noDifference);
     contentPanel.add(contentTable);
     add(contentPanel);
@@ -439,6 +443,8 @@
       setToken(Dispatcher.toPatchUnified(idSideA, patchKey));
     }
 
+    header.display(patchSetDetail, script, patchKey, idSideA, idSideB);
+
     if (hasDifferences) {
       contentTable.display(patchKey, idSideA, idSideB, script);
       contentTable.display(script.getCommentDetail(), script.isExpandAllComments());
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchSetSelectBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchSetSelectBox.java
new file mode 100644
index 0000000..5dd4e1f
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchSetSelectBox.java
@@ -0,0 +1,169 @@
+// Copyright (C) 2012 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.patches;
+
+import com.google.gerrit.client.Dispatcher;
+import com.google.gerrit.client.Gerrit;
+import com.google.gerrit.common.data.PatchScript;
+import com.google.gerrit.common.data.PatchSetDetail;
+import com.google.gerrit.reviewdb.client.Patch;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.dom.client.SpanElement;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+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.user.client.ui.Anchor;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.HTMLPanel;
+import com.google.gwt.user.client.ui.Label;
+import com.google.gwtorm.client.KeyUtil;
+
+import java.util.LinkedList;
+import java.util.List;
+
+public class PatchSetSelectBox extends Composite {
+  interface Binder extends UiBinder<HTMLPanel, PatchSetSelectBox> {
+  }
+
+  private static Binder uiBinder = GWT.create(Binder.class);
+
+  interface BoxStyle extends CssResource {
+    String selected();
+
+    String hidden();
+  }
+
+  public enum Side {
+    A, B
+  }
+
+  PatchScript script;
+  Patch.Key patchKey;
+  PatchSet.Id idSideA;
+  PatchSet.Id idSideB;
+  PatchSet.Id idActive;
+  Side side;
+  PatchScreen.Type screenType;
+  List<Anchor> links;
+
+  @UiField
+  HTMLPanel linkPanel;
+
+  @UiField
+  BoxStyle style;
+
+  @UiField
+  SpanElement sideMarker;
+
+  public PatchSetSelectBox(Side side, final PatchScreen.Type type) {
+    this.side = side;
+    this.screenType = type;
+
+    initWidget(uiBinder.createAndBindUi(this));
+  }
+
+  public void display(final PatchSetDetail detail, final PatchScript script, Patch.Key key,
+      PatchSet.Id idSideA, PatchSet.Id idSideB) {
+    this.script = script;
+    this.patchKey = key;
+    this.idSideA = idSideA;
+    this.idSideB = idSideB;
+    this.idActive = (side == Side.A) ? idSideA : idSideB;
+    this.links = new LinkedList<Anchor>();
+
+    if (screenType == PatchScreen.Type.UNIFIED) {
+      sideMarker.setInnerText((side == Side.A) ? "(-)" : "(+)");
+    }
+
+    if (detail.getInfo().getParents().size() > 1) {
+      addLink(PatchUtil.C.patchBaseAutoMerge(), null);
+    } else {
+      addLink(PatchUtil.C.patchBase(), null);
+    }
+
+    if (side == Side.B) {
+      links.get(0).setStyleName(style.hidden());
+    }
+
+    for (Patch patch : script.getHistory()) {
+      PatchSet.Id psId = patch.getKey().getParentKey();
+      addLink(Integer.toString(psId.get()), psId);
+    }
+
+    if (idActive == null && side == Side.A) {
+      links.get(0).setStyleName(style.selected());
+    } else {
+      links.get(idActive.get()).setStyleName(style.selected());
+    }
+
+    Anchor downloadLink = getDownloadLink();
+    if (downloadLink != null) {
+      linkPanel.add(new Label(" - "));
+      linkPanel.add(downloadLink);
+    }
+  }
+
+  private void addLink(String label, final PatchSet.Id id) {
+    final Anchor anchor = new Anchor(label);
+    anchor.addClickHandler(new ClickHandler() {
+      @Override
+      public void onClick(ClickEvent event) {
+        if (side == Side.A) {
+          idSideA = id;
+        } else {
+          idSideB = id;
+        }
+
+        Patch.Key keySideB = new Patch.Key(idSideB, patchKey.get());
+
+        switch (screenType) {
+          case SIDE_BY_SIDE:
+            Gerrit.display(Dispatcher.toPatchSideBySide(idSideA, keySideB));
+            break;
+          case UNIFIED:
+            Gerrit.display(Dispatcher.toPatchUnified(idSideA, keySideB));
+            break;
+        }
+      }
+
+    });
+
+    links.add(anchor);
+    linkPanel.add(anchor);
+  }
+
+  private Anchor getDownloadLink() {
+    boolean isCommitMessage = Patch.COMMIT_MSG.equals(script.getNewName());
+
+    if (isCommitMessage || (side == Side.A && 0 >= script.getA().size())
+        || (side == Side.B && 0 >= script.getB().size())) {
+      return null;
+    }
+
+    Patch.Key key =
+        (idSideA == null) ? patchKey : (new Patch.Key(idSideA, patchKey.get()));
+
+    String sideURL = (side == Side.A) ? "1" : "0";
+    final String base = GWT.getHostPageBaseURL() + "cat/";
+
+    final Anchor anchor = new Anchor(PatchUtil.C.download());
+    anchor.setHref(base + KeyUtil.encode(key.toString()) + "^" + sideURL);
+
+    return anchor;
+  }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchSetSelectBox.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchSetSelectBox.ui.xml
new file mode 100644
index 0000000..2c4bd5d
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchSetSelectBox.ui.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2012 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.
+-->
+
+<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
+    xmlns:g='urn:import:com.google.gwt.user.client.ui'>
+
+
+  <ui:with field='res' type='com.google.gerrit.client.GerritResources'/>
+  <ui:with field='cons' type='com.google.gerrit.client.patches.PatchConstants'/>
+  <ui:style type='com.google.gerrit.client.patches.PatchSetSelectBox.BoxStyle'>
+    @eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor;
+    @eval trimColor com.google.gerrit.client.Gerrit.getTheme().trimColor;
+    @eval backgroundColor com.google.gerrit.client.Gerrit.getTheme().backgroundColor;
+
+    .wrapper {
+      width: 100%;
+    }
+
+    .patchSetLabel {
+      font-weight: bold;
+    }
+
+    .linkPanel > div {
+      display: inline-block;
+      padding: 3px;
+    }
+
+    .linkPanel {
+      font-size: 12px;
+    }
+
+    .linkPanel > a {
+      padding: 3px;
+      display: inline-block;
+      text-decoration: none;
+    }
+
+    .selected {
+      font-weight: bold;
+      background-color: selectionColor;
+    }
+
+    .sideMarker {
+      font-family: monospace;
+    }
+
+    .hidden {
+      visibility: hidden;
+    }
+  </ui:style>
+
+  <g:HTMLPanel styleName='wrapper'>
+    <g:HTMLPanel styleName='{style.linkPanel}' ui:field='linkPanel'><span class='{style.patchSetLabel}'><ui:text from="{cons.patchSet}" /></span> <span class='{style.sideMarker}' ui:field='sideMarker'></span>: </g:HTMLPanel>
+  </g:HTMLPanel>
+</ui:UiBinder>
+
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchTableHeader.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchTableHeader.java
new file mode 100644
index 0000000..3dd8908
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchTableHeader.java
@@ -0,0 +1,71 @@
+// Copyright (C) 2012 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.patches;
+
+import com.google.gerrit.common.data.PatchScript;
+import com.google.gerrit.common.data.PatchSetDetail;
+import com.google.gerrit.reviewdb.client.Patch;
+import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.uibinder.client.UiTemplate;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.HTMLPanel;
+import com.google.gwt.user.client.ui.SimplePanel;
+
+public class PatchTableHeader extends Composite {
+
+  @UiTemplate("PatchTableHeaderSideBySide.ui.xml")
+  interface SideBySideBinder extends UiBinder<HTMLPanel, PatchTableHeader> {
+  }
+
+  @UiTemplate("PatchTableHeaderUnified.ui.xml")
+  interface UnifiedBinder extends UiBinder<HTMLPanel, PatchTableHeader> {
+  }
+
+  private static SideBySideBinder uiBinderS = GWT.create(SideBySideBinder.class);
+  private static UnifiedBinder uiBinderU = GWT.create(UnifiedBinder.class);
+
+  @UiField
+  SimplePanel sideAPanel;
+
+  @UiField
+  SimplePanel sideBPanel;
+
+  PatchSetSelectBox listA;
+  PatchSetSelectBox listB;
+
+  public PatchTableHeader(PatchScreen.Type type) {
+    listA = new PatchSetSelectBox(PatchSetSelectBox.Side.A, type);
+    listB = new PatchSetSelectBox(PatchSetSelectBox.Side.B, type);
+
+    if (type == PatchScreen.Type.SIDE_BY_SIDE) {
+      initWidget(uiBinderS.createAndBindUi(this));
+    } else {
+      initWidget(uiBinderU.createAndBindUi(this));
+    }
+
+    sideAPanel.add(listA);
+    sideBPanel.add(listB);
+  }
+
+
+  public void display(final PatchSetDetail detail, PatchScript script, final Patch.Key patchKey,
+      final PatchSet.Id idSideA, final PatchSet.Id idSideB) {
+    listA.display(detail, script, patchKey, idSideA, idSideB);
+    listB.display(detail, script, patchKey, idSideA, idSideB);
+  }
+}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchTableHeaderSideBySide.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchTableHeaderSideBySide.ui.xml
new file mode 100644
index 0000000..424e6e5
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchTableHeaderSideBySide.ui.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2012 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.
+-->
+
+<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
+    xmlns:g='urn:import:com.google.gwt.user.client.ui'>
+
+
+  <ui:style>
+    @eval trimColor com.google.gerrit.client.Gerrit.getTheme().trimColor;
+
+    .wrapper {
+      width: 100%;
+      background-color: trimColor;
+      overflow: hidden;
+    }
+
+    .wrapper .box {
+      width: 100%;
+      text-align: center;
+    }
+
+    .leftWrapper {
+      width: 50%;
+      float: left;
+    }
+
+    .rightWrapper {
+      width: 50%;
+      overflow: hidden;
+    }
+
+    .leftBox {
+      float:left;
+    }
+
+    .rightBox {
+      float: right;
+    }
+  </ui:style>
+
+  <g:HTMLPanel styleName="{style.wrapper}">
+    <div class='{style.leftWrapper}'>
+      <g:SimplePanel addStyleNames='{style.box} {style.leftBox}' ui:field='sideAPanel'/>
+    </div>
+    <div class='{style.rightWrapper}'>
+      <g:SimplePanel addStyleNames='{style.box} {style.rightBox}' ui:field='sideBPanel'/>
+    </div>
+  </g:HTMLPanel>
+</ui:UiBinder>
+
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchTableHeaderUnified.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchTableHeaderUnified.ui.xml
new file mode 100644
index 0000000..e26e96a
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/PatchTableHeaderUnified.ui.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Copyright (C) 2012 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.
+-->
+
+<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
+    xmlns:g='urn:import:com.google.gwt.user.client.ui'>
+
+  <ui:style>
+    @eval trimColor com.google.gerrit.client.Gerrit.getTheme().trimColor;
+
+    .wrapper {
+      width: 100%;
+      background-color: trimColor;
+    }
+
+    .wrapper .box {
+      width: 100%;
+      text-align: left;
+      margin-left: 3px;
+    }
+  </ui:style>
+
+  <g:HTMLPanel styleName="{style.wrapper}">
+    <g:SimplePanel addStyleNames='{style.box}' ui:field='sideAPanel'/>
+    <g:SimplePanel addStyleNames='{style.box}' ui:field='sideBPanel'/>
+  </g:HTMLPanel>
+</ui:UiBinder>
+
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/SideBySideTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/SideBySideTable.java
index 964ba4b..ec63a83 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/SideBySideTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/patches/SideBySideTable.java
@@ -25,10 +25,7 @@
 import com.google.gerrit.common.data.PatchScript.FileMode;
 import com.google.gerrit.prettify.common.EditList;
 import com.google.gerrit.prettify.common.SparseHtmlFile;
-import com.google.gerrit.reviewdb.client.Patch;
-import com.google.gerrit.reviewdb.client.Patch.ChangeType;
 import com.google.gerrit.reviewdb.client.PatchLineComment;
-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.user.client.ui.Anchor;
@@ -38,8 +35,6 @@
 import com.google.gwt.user.client.ui.InlineLabel;
 import com.google.gwtexpui.safehtml.client.SafeHtml;
 import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
-import com.google.gwtorm.client.KeyUtil;
-
 import org.eclipse.jgit.diff.Edit;
 
 import java.util.ArrayList;
@@ -89,9 +84,6 @@
         script.getDiffPrefs().isIntralineDifference()
             && script.hasIntralineDifference();
 
-    appendHeader(script, nc);
-    lines.add(null);
-
     if (script.getFileModeA() != FileMode.FILE
         || script.getFileModeB() != FileMode.FILE) {
       openLine(nc);
@@ -262,71 +254,6 @@
     return row;
   }
 
-  private void appendHeader(PatchScript script, final SafeHtmlBuilder m) {
-    boolean isCommitMessage = Patch.COMMIT_MSG.equals(script.getNewName());
-
-    m.openTr();
-
-    m.openTd();
-    m.addStyleName(Gerrit.RESOURCES.css().iconCell());
-    m.addStyleName(Gerrit.RESOURCES.css().fileColumnHeader());
-    m.closeTd();
-
-    m.openTd();
-    m.addStyleName(Gerrit.RESOURCES.css().fileColumnHeader());
-    m.addStyleName(Gerrit.RESOURCES.css().lineNumber());
-    m.closeTd();
-
-    m.openTd();
-    m.setStyleName(Gerrit.RESOURCES.css().fileColumnHeader());
-    m.setAttribute("width", "50%");
-    if (script.getChangeType() == ChangeType.RENAMED
-        || script.getChangeType() == ChangeType.COPIED) {
-      m.append(script.getOldName());
-    } else {
-      m.append(PatchUtil.C.patchHeaderOld());
-    }
-    if (!isCommitMessage) {
-      m.br();
-      if (0 < script.getA().size()) {
-        if (idSideA == null) {
-          downloadLink(m, patchKey, "1");
-        } else {
-          downloadLink(m, new Patch.Key(idSideA, patchKey.get()), "0");
-        }
-      }
-    }
-    m.closeTd();
-
-    m.openTd();
-    m.setStyleName(Gerrit.RESOURCES.css().fileColumnHeader());
-    m.setAttribute("width", "50%");
-    m.append(PatchUtil.C.patchHeaderNew());
-    if (!isCommitMessage) {
-      m.br();
-      if (0 < script.getB().size()) {
-        downloadLink(m, new Patch.Key(idSideB, patchKey.get()), "0");
-      }
-    }
-    m.closeTd();
-
-    m.openTd();
-    m.addStyleName(Gerrit.RESOURCES.css().fileColumnHeader());
-    m.addStyleName(Gerrit.RESOURCES.css().lineNumber());
-    m.closeTd();
-
-    m.closeTr();
-  }
-
-  private void downloadLink(final SafeHtmlBuilder m, final Patch.Key key,
-      final String side) {
-    final String base = GWT.getHostPageBaseURL() + "cat/";
-    m.openAnchor();
-    m.setAttribute("href", base + KeyUtil.encode(key.toString()) + "^" + side);
-    m.append(PatchUtil.C.download());
-    m.closeAnchor();
-  }
-
   private void appendSkipLine(final SafeHtmlBuilder m, final int skipCnt) {
     m.openTr();
 
diff --git a/gerrit-httpd/.settings/org.eclipse.core.resources.prefs b/gerrit-httpd/.settings/org.eclipse.core.resources.prefs
index 9df523e..839d647 100644
--- a/gerrit-httpd/.settings/org.eclipse.core.resources.prefs
+++ b/gerrit-httpd/.settings/org.eclipse.core.resources.prefs
@@ -1,4 +1,3 @@
-#Thu Jul 28 11:02:36 PDT 2011
 eclipse.preferences.version=1
 encoding//src/main/java=UTF-8
 encoding//src/main/resources=UTF-8
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
index f92f13d..72bb0c2 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/GerritConfigProvider.java
@@ -90,6 +90,10 @@
         config.setAllowedOpenIDs(authConfig.getAllowedOpenIDs());
         break;
 
+      case OPENID_SSO:
+        config.setOpenIdSsoUrl(authConfig.getOpenIdSsoUrl());
+        break;
+
       case LDAP:
       case LDAP_BIND:
         config.setRegisterUrl(cfg.getString("auth", null, "registerurl"));
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
index 0d14b79..1a48bb5 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebModule.java
@@ -108,6 +108,7 @@
         break;
 
       case OPENID:
+      case OPENID_SSO:
         // OpenID support is bound in WebAppInitializer and Daemon.
       case CUSTOM_EXTENSION:
         break;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/ThemeFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/ThemeFactory.java
index 68379d7..a2f4c99 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/ThemeFactory.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/raw/ThemeFactory.java
@@ -43,6 +43,9 @@
     theme.trimColor = color(name, "trimColor", "#D4E9A9");
     theme.selectionColor = color(name, "selectionColor", "#FFFFCC");
     theme.topMenuColor = color(name, "topMenuColor", theme.trimColor);
+    theme.changeTableOutdatedColor = color(name, "changeTableOutdatedColor", "#FF0000");
+    theme.tableOddRowColor = color(name, "tableOddRowColor", "transparent");
+    theme.tableEvenRowColor = color(name, "tableEvenRowColor", "transparent");
     return theme;
   }
 
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupAdminServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupAdminServiceImpl.java
index c7b4c79..aca2e05 100644
--- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupAdminServiceImpl.java
+++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupAdminServiceImpl.java
@@ -31,15 +31,20 @@
 import com.google.gerrit.reviewdb.client.AccountGroupIncludeAudit;
 import com.google.gerrit.reviewdb.client.AccountGroupMember;
 import com.google.gerrit.reviewdb.client.AccountGroupMemberAudit;
+import com.google.gerrit.reviewdb.client.AuthType;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.IdentifiedUser;
 import com.google.gerrit.server.account.AccountCache;
+import com.google.gerrit.server.account.AccountException;
+import com.google.gerrit.server.account.AccountManager;
 import com.google.gerrit.server.account.AccountResolver;
+import com.google.gerrit.server.account.AuthRequest;
 import com.google.gerrit.server.account.GroupBackend;
 import com.google.gerrit.server.account.GroupBackends;
 import com.google.gerrit.server.account.GroupCache;
 import com.google.gerrit.server.account.GroupControl;
 import com.google.gerrit.server.account.GroupIncludeCache;
+import com.google.gerrit.server.config.AuthConfig;
 import com.google.gwtjsonrpc.common.AsyncCallback;
 import com.google.gwtjsonrpc.common.VoidResult;
 import com.google.gwtorm.server.OrmException;
@@ -54,6 +59,8 @@
     GroupAdminService {
   private final AccountCache accountCache;
   private final AccountResolver accountResolver;
+  private final AccountManager accountManager;
+  private final AuthType authType;
   private final GroupCache groupCache;
   private final GroupBackend groupBackend;
   private final GroupIncludeCache groupIncludeCache;
@@ -70,6 +77,8 @@
       final AccountCache accountCache,
       final GroupIncludeCache groupIncludeCache,
       final AccountResolver accountResolver,
+      final AccountManager accountManager,
+      final AuthConfig authConfig,
       final GroupCache groupCache,
       final GroupBackend groupBackend,
       final GroupControl.Factory groupControlFactory,
@@ -81,6 +90,8 @@
     this.accountCache = accountCache;
     this.groupIncludeCache = groupIncludeCache;
     this.accountResolver = accountResolver;
+    this.accountManager = accountManager;
+    this.authType = authConfig.getAuthType();
     this.groupCache = groupCache;
     this.groupBackend = groupBackend;
     this.groupControlFactory = groupControlFactory;
@@ -366,13 +377,38 @@
 
   private Account findAccount(final String nameOrEmail) throws OrmException,
       Failure {
-    final Account r = accountResolver.find(nameOrEmail);
+    Account r = accountResolver.find(nameOrEmail);
     if (r == null) {
-      throw new Failure(new NoSuchAccountException(nameOrEmail));
+      switch (authType) {
+        case HTTP_LDAP:
+        case CLIENT_SSL_CERT_LDAP:
+        case LDAP:
+          r = createAccountByLdap(nameOrEmail);
+          break;
+        default:
+      }
+      if (r == null) {
+        throw new Failure(new NoSuchAccountException(nameOrEmail));
+      }
     }
     return r;
   }
 
+  private Account createAccountByLdap(String user) {
+    if (!user.matches(Account.USER_NAME_PATTERN)) {
+      return null;
+    }
+
+    try {
+      final AuthRequest req = AuthRequest.forUser(user);
+      req.setSkipAuthentication(true);
+      return accountCache.get(accountManager.authenticate(req).getAccountId())
+          .getAccount();
+    } catch (AccountException e) {
+      return null;
+    }
+  }
+
   private AccountGroup findGroup(final String name) throws OrmException,
       Failure {
     final AccountGroup g = groupCache.get(new AccountGroup.NameKey(name));
diff --git a/gerrit-launcher/.settings/org.eclipse.core.resources.prefs b/gerrit-launcher/.settings/org.eclipse.core.resources.prefs
index c780f44..e9441bb 100644
--- a/gerrit-launcher/.settings/org.eclipse.core.resources.prefs
+++ b/gerrit-launcher/.settings/org.eclipse.core.resources.prefs
@@ -1,4 +1,3 @@
-#Thu Jul 28 11:02:36 PDT 2011
 eclipse.preferences.version=1
 encoding//src/main/java=UTF-8
 encoding/<project>=UTF-8
diff --git a/gerrit-main/.settings/org.eclipse.core.resources.prefs b/gerrit-main/.settings/org.eclipse.core.resources.prefs
index c780f44..e9441bb 100644
--- a/gerrit-main/.settings/org.eclipse.core.resources.prefs
+++ b/gerrit-main/.settings/org.eclipse.core.resources.prefs
@@ -1,4 +1,3 @@
-#Thu Jul 28 11:02:36 PDT 2011
 eclipse.preferences.version=1
 encoding//src/main/java=UTF-8
 encoding/<project>=UTF-8
diff --git a/gerrit-openid/.settings/org.eclipse.core.resources.prefs b/gerrit-openid/.settings/org.eclipse.core.resources.prefs
index fc11c3f..f9fe345 100644
--- a/gerrit-openid/.settings/org.eclipse.core.resources.prefs
+++ b/gerrit-openid/.settings/org.eclipse.core.resources.prefs
@@ -1,4 +1,3 @@
-#Thu Jul 28 11:02:36 PDT 2011
 eclipse.preferences.version=1
 encoding//src/main/java=UTF-8
 encoding//src/test/java=UTF-8
diff --git a/gerrit-patch-commonsnet/.settings/org.eclipse.core.resources.prefs b/gerrit-patch-commonsnet/.settings/org.eclipse.core.resources.prefs
index 589908f..e9441bb 100644
--- a/gerrit-patch-commonsnet/.settings/org.eclipse.core.resources.prefs
+++ b/gerrit-patch-commonsnet/.settings/org.eclipse.core.resources.prefs
@@ -1,4 +1,3 @@
-#Thu Jul 28 11:02:35 PDT 2011
 eclipse.preferences.version=1
 encoding//src/main/java=UTF-8
 encoding/<project>=UTF-8
diff --git a/gerrit-patch-jgit/.settings/org.eclipse.core.resources.prefs b/gerrit-patch-jgit/.settings/org.eclipse.core.resources.prefs
index 589908f..e9441bb 100644
--- a/gerrit-patch-jgit/.settings/org.eclipse.core.resources.prefs
+++ b/gerrit-patch-jgit/.settings/org.eclipse.core.resources.prefs
@@ -1,4 +1,3 @@
-#Thu Jul 28 11:02:35 PDT 2011
 eclipse.preferences.version=1
 encoding//src/main/java=UTF-8
 encoding/<project>=UTF-8
diff --git a/gerrit-pgm/.settings/org.eclipse.core.resources.prefs b/gerrit-pgm/.settings/org.eclipse.core.resources.prefs
index 9df523e..839d647 100644
--- a/gerrit-pgm/.settings/org.eclipse.core.resources.prefs
+++ b/gerrit-pgm/.settings/org.eclipse.core.resources.prefs
@@ -1,4 +1,3 @@
-#Thu Jul 28 11:02:36 PDT 2011
 eclipse.preferences.version=1
 encoding//src/main/java=UTF-8
 encoding//src/main/resources=UTF-8
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
index 282bbc9..02b0a1d 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
@@ -367,7 +367,8 @@
     }
 
     AuthConfig authConfig = cfgInjector.getInstance(AuthConfig.class);
-    if (authConfig.getAuthType() == AuthType.OPENID) {
+    if (authConfig.getAuthType() == AuthType.OPENID ||
+        authConfig.getAuthType() == AuthType.OPENID_SSO) {
       modules.add(new OpenIdModule());
     }
     modules.add(sysInjector.getInstance(GetUserFilter.Module.class));
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ExportReviewNotes.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ExportReviewNotes.java
index f100372..525360d 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ExportReviewNotes.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ExportReviewNotes.java
@@ -21,7 +21,6 @@
 import com.google.gerrit.lifecycle.LifecycleModule;
 import com.google.gerrit.pgm.util.SiteProgram;
 import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.account.AccountCacheImpl;
@@ -35,6 +34,7 @@
 import com.google.gerrit.server.git.CreateCodeReviewNotes;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.LocalDiskRepositoryManager;
+import com.google.gerrit.server.git.NotesBranchUtil;
 import com.google.gerrit.server.schema.SchemaVersionCheck;
 import com.google.gwtorm.server.OrmException;
 import com.google.gwtorm.server.SchemaFactory;
@@ -44,7 +44,6 @@
 import com.google.inject.Scopes;
 
 import org.eclipse.jgit.errors.RepositoryNotFoundException;
-import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.lib.TextProgressMonitor;
 import org.eclipse.jgit.lib.ThreadSafeProgressMonitor;
@@ -104,6 +103,7 @@
           @Override
           protected void configure() {
             factory(CreateCodeReviewNotes.Factory.class);
+            factory(NotesBranchUtil.Factory.class);
           }
         });
         install(new LifecycleModule() {
@@ -172,21 +172,8 @@
     }
     try {
       CreateCodeReviewNotes notes = codeReviewNotesFactory.create(db, git);
-      try {
-        notes.loadBase();
-        for (Change change : changes) {
-          monitor.update(1);
-          PatchSet ps = db.patchSets().get(change.currentPatchSetId());
-          if (ps == null) {
-            continue;
-          }
-          notes.add(change, ObjectId.fromString(ps.getRevision().get()));
-        }
-        notes.commit("Exported prior reviews from Gerrit Code Review\n");
-        notes.updateRef();
-      } finally {
-        notes.release();
-      }
+      notes.create(changes, null,
+          "Exported prior reviews from Gerrit Code Review\n", monitor);
     } finally {
       git.close();
     }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
index 2d56453..95b8487f 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
@@ -141,12 +141,12 @@
       }
 
       final StringBuilder buf = new StringBuilder();
-      buf.append(why.getMessage());
-      why = why.getCause();
       while (why != null) {
-        buf.append("\n  caused by ");
-        buf.append(why.toString());
+        buf.append(why.getMessage());
         why = why.getCause();
+        if (why != null) {
+          buf.append("\n  caused by ");
+        }
       }
       throw die(buf.toString(), new RuntimeException("InitInjector failed", ce));
     }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/ProjectQoSFilter.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/ProjectQoSFilter.java
index ee7c794..2d6db63 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/ProjectQoSFilter.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/http/jetty/ProjectQoSFilter.java
@@ -97,7 +97,7 @@
     this.userProvider = userProvider;
     this.queue = queue;
     this.context = context;
-    this.maxWait = getTimeUnit(cfg, "httpd", null, "maxwait", 5, MINUTES);
+    this.maxWait = MINUTES.toMillis(getTimeUnit(cfg, "httpd", null, "maxwait", 5, MINUTES));
   }
 
   @Override
diff --git a/gerrit-plugin-archetype/.settings/org.eclipse.core.resources.prefs b/gerrit-plugin-archetype/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..abdea9ac
--- /dev/null
+++ b/gerrit-plugin-archetype/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,4 @@
+eclipse.preferences.version=1
+encoding//src/main/java=UTF-8
+encoding//src/main/resources=UTF-8
+encoding/<project>=UTF-8
diff --git a/gerrit-plugin-archetype/.settings/org.eclipse.jdt.core.prefs b/gerrit-plugin-archetype/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..470942d
--- /dev/null
+++ b/gerrit-plugin-archetype/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,269 @@
+#Thu Jul 28 11:02:36 PDT 2011
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
+org.eclipse.jdt.core.compiler.source=1.6
+org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_assignment=16
+org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_compact_if=16
+org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
+org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16
+org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16
+org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16
+org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16
+org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16
+org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
+org.eclipse.jdt.core.formatter.blank_lines_after_package=1
+org.eclipse.jdt.core.formatter.blank_lines_before_field=0
+org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
+org.eclipse.jdt.core.formatter.blank_lines_before_imports=0
+org.eclipse.jdt.core.formatter.blank_lines_before_member_type=0
+org.eclipse.jdt.core.formatter.blank_lines_before_method=1
+org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
+org.eclipse.jdt.core.formatter.blank_lines_before_package=0
+org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
+org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=2
+org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
+org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
+org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
+org.eclipse.jdt.core.formatter.comment.format_block_comments=true
+org.eclipse.jdt.core.formatter.comment.format_header=true
+org.eclipse.jdt.core.formatter.comment.format_html=true
+org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
+org.eclipse.jdt.core.formatter.comment.format_line_comments=true
+org.eclipse.jdt.core.formatter.comment.format_source_code=true
+org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false
+org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
+org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
+org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
+org.eclipse.jdt.core.formatter.comment.line_length=80
+org.eclipse.jdt.core.formatter.compact_else_if=true
+org.eclipse.jdt.core.formatter.continuation_indentation=2
+org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
+org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
+org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
+org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_empty_lines=false
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
+org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
+org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true
+org.eclipse.jdt.core.formatter.indentation.size=4
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
+org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
+org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
+org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
+org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert
+org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
+org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
+org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
+org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
+org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
+org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
+org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
+org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=true
+org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
+org.eclipse.jdt.core.formatter.lineSplit=80
+org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
+org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
+org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=3
+org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=false
+org.eclipse.jdt.core.formatter.tabulation.char=space
+org.eclipse.jdt.core.formatter.tabulation.size=2
+org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
+org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
diff --git a/gerrit-prettify/.settings/org.eclipse.core.resources.prefs b/gerrit-prettify/.settings/org.eclipse.core.resources.prefs
index e7d6680..abdea9ac 100644
--- a/gerrit-prettify/.settings/org.eclipse.core.resources.prefs
+++ b/gerrit-prettify/.settings/org.eclipse.core.resources.prefs
@@ -1,4 +1,3 @@
-#Thu Jul 28 11:02:35 PDT 2011
 eclipse.preferences.version=1
 encoding//src/main/java=UTF-8
 encoding//src/main/resources=UTF-8
diff --git a/gerrit-reviewdb/.settings/org.eclipse.core.resources.prefs b/gerrit-reviewdb/.settings/org.eclipse.core.resources.prefs
index e7d6680..abdea9ac 100644
--- a/gerrit-reviewdb/.settings/org.eclipse.core.resources.prefs
+++ b/gerrit-reviewdb/.settings/org.eclipse.core.resources.prefs
@@ -1,4 +1,3 @@
-#Thu Jul 28 11:02:35 PDT 2011
 eclipse.preferences.version=1
 encoding//src/main/java=UTF-8
 encoding//src/main/resources=UTF-8
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AuthType.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AuthType.java
index 962426b..b615fc5 100644
--- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AuthType.java
+++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/client/AuthType.java
@@ -18,6 +18,9 @@
   /** Login relies upon the OpenID standard: {@link "http://openid.net/"} */
   OPENID,
 
+  /** Login relies upon the OpenID standard: {@link "http://openid.net/"} in Single Sign On mode */
+  OPENID_SSO,
+
   /**
    * Login relies upon the container/web server security.
    * <p>
diff --git a/gerrit-server/.settings/org.eclipse.core.resources.prefs b/gerrit-server/.settings/org.eclipse.core.resources.prefs
index 7d5f965..29abf99 100644
--- a/gerrit-server/.settings/org.eclipse.core.resources.prefs
+++ b/gerrit-server/.settings/org.eclipse.core.resources.prefs
@@ -1,4 +1,3 @@
-#Thu Jul 28 11:02:36 PDT 2011
 eclipse.preferences.version=1
 encoding//src/main/java=UTF-8
 encoding//src/main/resources=UTF-8
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java
index 6f1096c..dc36988 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java
@@ -39,6 +39,7 @@
   private final boolean userNameToLowerCase;
   private final boolean gitBasicAuth;
   private final String logoutUrl;
+  private final String openIdSsoUrl;
   private final List<OpenIdProviderPattern> trustedOpenIDs;
   private final List<OpenIdProviderPattern> allowedOpenIDs;
   private final String cookiePath;
@@ -53,6 +54,7 @@
     authType = toType(cfg);
     httpHeader = cfg.getString("auth", null, "httpheader");
     logoutUrl = cfg.getString("auth", null, "logouturl");
+    openIdSsoUrl = cfg.getString("auth", null, "openidssourl");
     trustedOpenIDs = toPatterns(cfg, "trustedOpenID");
     allowedOpenIDs = toPatterns(cfg, "allowedOpenID");
     cookiePath = cfg.getString("auth", null, "cookiepath");
@@ -111,6 +113,10 @@
     return logoutUrl;
   }
 
+  public String getOpenIdSsoUrl() {
+    return openIdSsoUrl;
+  }
+
   public String getCookiePath() {
     return cookiePath;
   }
@@ -161,6 +167,10 @@
         //
         return true;
 
+      case OPENID_SSO:
+        // There's only one provider in SSO mode, so it must be okay.
+        return true;
+
       case OPENID:
         // All identities must be trusted in order to trust the account.
         //
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
index 71676ad..ba54c56 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
@@ -35,6 +35,7 @@
 import com.google.gerrit.server.git.CreateCodeReviewNotes;
 import com.google.gerrit.server.git.MergeOp;
 import com.google.gerrit.server.git.MetaDataUpdate;
+import com.google.gerrit.server.git.NotesBranchUtil;
 import com.google.gerrit.server.git.SubmoduleOp;
 import com.google.gerrit.server.mail.AbandonedSender;
 import com.google.gerrit.server.mail.AddReviewerSender;
@@ -82,6 +83,7 @@
     factory(SubmoduleOp.Factory.class);
     factory(MergeOp.Factory.class);
     factory(CreateCodeReviewNotes.Factory.class);
+    factory(NotesBranchUtil.Factory.class);
     install(new AsyncReceiveCommits.Module());
 
     // Not really per-request, but dammit, I don't know where else to
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/BanCommit.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/BanCommit.java
index 4bfee9c..da38573 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/BanCommit.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/BanCommit.java
@@ -16,6 +16,7 @@
 
 import static com.google.gerrit.server.git.GitRepositoryManager.REF_REJECT_COMMITS;
 
+import com.google.common.base.Strings;
 import com.google.gerrit.common.errors.PermissionDeniedException;
 import com.google.gerrit.server.GerritPersonIdent;
 import com.google.gerrit.server.IdentifiedUser;
@@ -23,34 +24,25 @@
 import com.google.inject.Inject;
 import com.google.inject.Provider;
 
-import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
 import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.lib.CommitBuilder;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
 import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.RefUpdate.Result;
 import org.eclipse.jgit.lib.Repository;
 import org.eclipse.jgit.notes.Note;
 import org.eclipse.jgit.notes.NoteMap;
-import org.eclipse.jgit.notes.NoteMapMerger;
-import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevWalk;
 
 import java.io.IOException;
+import java.io.UnsupportedEncodingException;
 import java.util.Date;
 import java.util.List;
 import java.util.TimeZone;
 
 public class BanCommit {
-
-  private static final int MAX_LOCK_FAILURE_CALLS = 10;
-  private static final int SLEEP_ON_LOCK_FAILURE_MS = 25;
-
   public interface Factory {
     BanCommit create();
   }
@@ -58,49 +50,37 @@
   private final Provider<IdentifiedUser> currentUser;
   private final GitRepositoryManager repoManager;
   private final PersonIdent gerritIdent;
+  private NotesBranchUtil.Factory notesBranchUtilFactory;
 
   @Inject
   BanCommit(final Provider<IdentifiedUser> currentUser,
       final GitRepositoryManager repoManager,
-      @GerritPersonIdent final PersonIdent gerritIdent) {
+      @GerritPersonIdent final PersonIdent gerritIdent,
+      final NotesBranchUtil.Factory notesBranchUtilFactory) {
     this.currentUser = currentUser;
     this.repoManager = repoManager;
     this.gerritIdent = gerritIdent;
+    this.notesBranchUtilFactory = notesBranchUtilFactory;
   }
 
   public BanCommitResult ban(final ProjectControl projectControl,
       final List<ObjectId> commitsToBan, final String reason)
       throws PermissionDeniedException, IOException,
-      InterruptedException, MergeException {
+      InterruptedException, MergeException, ConcurrentRefUpdateException {
     if (!projectControl.isOwner()) {
       throw new PermissionDeniedException(
           "No project owner: not permitted to ban commits");
     }
 
     final BanCommitResult result = new BanCommitResult();
-
-    final PersonIdent currentUserIdent = createPersonIdent();
+    NoteMap banCommitNotes = NoteMap.newEmptyMap();
+    // add a note for each banned commit to notes
     final Repository repo =
         repoManager.openRepository(projectControl.getProject().getNameKey());
     try {
       final RevWalk revWalk = new RevWalk(repo);
       final ObjectInserter inserter = repo.newObjectInserter();
       try {
-        NoteMap baseNoteMap = null;
-        RevCommit baseCommit = null;
-        final Ref notesBranch = repo.getRef(REF_REJECT_COMMITS);
-        if (notesBranch != null) {
-          baseCommit = revWalk.parseCommit(notesBranch.getObjectId());
-          baseNoteMap = NoteMap.read(revWalk.getObjectReader(), baseCommit);
-        }
-
-        final NoteMap ourNoteMap;
-        if (baseCommit != null) {
-          ourNoteMap = NoteMap.read(repo.newObjectReader(), baseCommit);
-        } else {
-          ourNoteMap = NoteMap.newEmptyMap();
-        }
-
         for (final ObjectId commitToBan : commitsToBan) {
           try {
             revWalk.parseCommit(commitToBan);
@@ -110,31 +90,22 @@
             result.notACommit(commitToBan, e.getMessage());
             continue;
           }
+          banCommitNotes.set(commitToBan, createNoteContent(reason, inserter));
+        }
+        inserter.flush();
+        NotesBranchUtil notesBranchUtil = notesBranchUtilFactory.create(repo);
+        NoteMap newlyCreated =
+            notesBranchUtil.commitNewNotes(banCommitNotes, REF_REJECT_COMMITS,
+                createPersonIdent(), buildCommitMessage(commitsToBan, reason));
 
-          final Note note = ourNoteMap.getNote(commitToBan);
-          if (note != null) {
-            result.commitAlreadyBanned(commitToBan);
-            continue;
+        for (Note n : banCommitNotes) {
+          if (newlyCreated.contains(n)) {
+            result.commitBanned(n);
+          } else {
+            result.commitAlreadyBanned(n);
           }
-
-          final String noteContent = reason != null ? reason : "";
-          final ObjectId noteContentId =
-              inserter
-                  .insert(Constants.OBJ_BLOB, noteContent.getBytes("UTF-8"));
-          ourNoteMap.set(commitToBan, noteContentId);
-          result.commitBanned(commitToBan);
         }
-
-        if (result.getNewlyBannedCommits().isEmpty()) {
-          return result;
-        }
-
-        final ObjectId ourCommit =
-            commit(ourNoteMap, inserter, currentUserIdent, baseCommit, result,
-                reason);
-
-        updateRef(repo, revWalk, inserter, ourNoteMap, ourCommit, baseNoteMap,
-            baseCommit);
+        return result;
       } finally {
         revWalk.release();
         inserter.release();
@@ -142,8 +113,15 @@
     } finally {
       repo.close();
     }
+  }
 
-    return result;
+  private ObjectId createNoteContent(String reason, ObjectInserter inserter)
+      throws UnsupportedEncodingException, IOException {
+    String noteContent = reason != null ? reason : "";
+    if (noteContent.length() > 0 && !noteContent.endsWith("\n")) {
+      noteContent = noteContent + "\n";
+    }
+    return inserter.insert(Constants.OBJ_BLOB, noteContent.getBytes("UTF-8"));
   }
 
   private PersonIdent createPersonIdent() {
@@ -152,35 +130,6 @@
     return currentUser.get().newCommitterIdent(now, tz);
   }
 
-  private static ObjectId commit(final NoteMap noteMap,
-      final ObjectInserter inserter, final PersonIdent personIdent,
-      final ObjectId baseCommit, final BanCommitResult result,
-      final String reason) throws IOException {
-    final String commitMsg =
-        buildCommitMessage(result.getNewlyBannedCommits(), reason);
-    if (baseCommit != null) {
-      return createCommit(noteMap, inserter, personIdent, commitMsg, baseCommit);
-    } else {
-      return createCommit(noteMap, inserter, personIdent, commitMsg);
-    }
-  }
-
-  private static ObjectId createCommit(final NoteMap noteMap,
-      final ObjectInserter inserter, final PersonIdent personIdent,
-      final String message, final ObjectId... parents) throws IOException {
-    final CommitBuilder b = new CommitBuilder();
-    b.setTreeId(noteMap.writeTree(inserter));
-    b.setAuthor(personIdent);
-    b.setCommitter(personIdent);
-    if (parents.length > 0) {
-      b.setParentIds(parents);
-    }
-    b.setMessage(message);
-    final ObjectId commitId = inserter.insert(b);
-    inserter.flush();
-    return commitId;
-  }
-
   private static String buildCommitMessage(final List<ObjectId> bannedCommits,
       final String reason) {
     final StringBuilder commitMsg = new StringBuilder();
@@ -205,61 +154,4 @@
     commitMsg.append(commitList);
     return commitMsg.toString();
   }
-
-  public void updateRef(final Repository repo, final RevWalk revWalk,
-      final ObjectInserter inserter, final NoteMap ourNoteMap,
-      final ObjectId oursCommit, final NoteMap baseNoteMap,
-      final ObjectId baseCommit) throws IOException, InterruptedException,
-      MissingObjectException, IncorrectObjectTypeException,
-      CorruptObjectException, MergeException {
-
-    int remainingLockFailureCalls = MAX_LOCK_FAILURE_CALLS;
-    RefUpdate refUpdate = createRefUpdate(repo, oursCommit, baseCommit);
-
-    for (;;) {
-      final Result result = refUpdate.update();
-
-      if (result == Result.LOCK_FAILURE) {
-        if (--remainingLockFailureCalls > 0) {
-          Thread.sleep(SLEEP_ON_LOCK_FAILURE_MS);
-        } else {
-          throw new MergeException("Failed to lock the ref: "
-              + REF_REJECT_COMMITS);
-        }
-
-      } else if (result == Result.REJECTED) {
-        final RevCommit theirsCommit =
-            revWalk.parseCommit(refUpdate.getOldObjectId());
-        final NoteMap theirNoteMap =
-            NoteMap.read(revWalk.getObjectReader(), theirsCommit);
-        final NoteMapMerger merger = new NoteMapMerger(repo);
-        final NoteMap merged =
-            merger.merge(baseNoteMap, ourNoteMap, theirNoteMap);
-        final ObjectId mergeCommit =
-            createCommit(merged, inserter, gerritIdent,
-                "Merged note commits\n", oursCommit, theirsCommit);
-        refUpdate = createRefUpdate(repo, mergeCommit, theirsCommit);
-        remainingLockFailureCalls = MAX_LOCK_FAILURE_CALLS;
-
-      } else if (result == Result.IO_FAILURE) {
-        throw new IOException(
-            "Couldn't create commit reject notes because of IO_FAILURE");
-      } else {
-        break;
-      }
-    }
-  }
-
-  private static RefUpdate createRefUpdate(final Repository repo,
-      final ObjectId newObjectId, final ObjectId expectedOldObjectId)
-      throws IOException {
-    RefUpdate refUpdate = repo.updateRef(REF_REJECT_COMMITS);
-    refUpdate.setNewObjectId(newObjectId);
-    if (expectedOldObjectId == null) {
-      refUpdate.setExpectedOldObjectId(ObjectId.zeroId());
-    } else {
-      refUpdate.setExpectedOldObjectId(expectedOldObjectId);
-    }
-    return refUpdate;
-  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/CreateCodeReviewNotes.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/CreateCodeReviewNotes.java
index 6fea8f1..b067a49 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/CreateCodeReviewNotes.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/CreateCodeReviewNotes.java
@@ -20,6 +20,7 @@
 import com.google.gerrit.common.data.ApprovalTypes;
 import com.google.gerrit.reviewdb.client.ApprovalCategory;
 import com.google.gerrit.reviewdb.client.Change;
+import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.PatchSetApproval;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.GerritPersonIdent;
@@ -31,23 +32,15 @@
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 
-import org.eclipse.jgit.errors.CorruptObjectException;
-import org.eclipse.jgit.errors.IncorrectObjectTypeException;
-import org.eclipse.jgit.errors.MissingObjectException;
-import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
 import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.NullProgressMonitor;
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.lib.ObjectInserter;
-import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.PersonIdent;
-import org.eclipse.jgit.lib.Ref;
-import org.eclipse.jgit.lib.RefUpdate;
-import org.eclipse.jgit.lib.RefUpdate.Result;
+import org.eclipse.jgit.lib.ProgressMonitor;
 import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.notes.Note;
 import org.eclipse.jgit.notes.NoteMap;
-import org.eclipse.jgit.notes.NoteMapMerger;
-import org.eclipse.jgit.notes.NoteMerger;
 import org.eclipse.jgit.revwalk.FooterKey;
 import org.eclipse.jgit.revwalk.RevCommit;
 import org.eclipse.jgit.revwalk.RevWalk;
@@ -68,30 +61,22 @@
     CreateCodeReviewNotes create(ReviewDb reviewDb, Repository db);
   }
 
-  private static final int MAX_LOCK_FAILURE_CALLS = 10;
-  private static final int SLEEP_ON_LOCK_FAILURE_MS = 25;
   private static final FooterKey CHANGE_ID = new FooterKey("Change-Id");
 
-  private final ReviewDb schema;
-  private final PersonIdent gerritIdent;
   private final AccountCache accountCache;
   private final ApprovalTypes approvalTypes;
   private final String canonicalWebUrl;
   private final String anonymousCowardName;
+  private final ReviewDb schema;
   private final Repository db;
-  private final RevWalk revWalk;
-  private final ObjectInserter inserter;
-  private final ObjectReader reader;
 
-  private RevCommit baseCommit;
-  private NoteMap base;
-
-  private RevCommit oursCommit;
-  private NoteMap ours;
-
-  private List<CodeReviewCommit> commits;
   private PersonIdent author;
 
+  private RevWalk revWalk;
+  private ObjectInserter inserter;
+
+  private final NotesBranchUtil.Factory notesBranchUtilFactory;
+
   @Inject
   CreateCodeReviewNotes(
       @GerritPersonIdent final PersonIdent gerritIdent,
@@ -99,90 +84,87 @@
       final ApprovalTypes approvalTypes,
       final @Nullable @CanonicalWebUrl String canonicalWebUrl,
       final @AnonymousCowardName String anonymousCowardName,
+      final NotesBranchUtil.Factory notesBranchUtilFactory,
       final @Assisted  ReviewDb reviewDb,
       final @Assisted  Repository db) {
-    schema = reviewDb;
     this.author = gerritIdent;
-    this.gerritIdent = gerritIdent;
     this.accountCache = accountCache;
     this.approvalTypes = approvalTypes;
     this.canonicalWebUrl = canonicalWebUrl;
     this.anonymousCowardName = anonymousCowardName;
+    this.notesBranchUtilFactory = notesBranchUtilFactory;
+    schema = reviewDb;
     this.db = db;
-
-    revWalk = new RevWalk(db);
-    inserter = db.newObjectInserter();
-    reader = db.newObjectReader();
   }
 
   public void create(List<CodeReviewCommit> commits, PersonIdent author)
       throws CodeReviewNoteCreationException {
     try {
-      this.commits = commits;
-      this.author = author;
-      loadBase();
-      applyNotes();
-      updateRef();
+      revWalk = new RevWalk(db);
+      inserter = db.newObjectInserter();
+      if (author != null) {
+        this.author = author;
+      }
+
+      NoteMap notes = NoteMap.newEmptyMap();
+      StringBuilder message =
+          new StringBuilder("Update notes for submitted changes\n\n");
+      for (CodeReviewCommit c : commits) {
+        notes.set(c, createNoteContent(c.change, c));
+        message.append("* ").append(c.getShortMessage()).append("\n");
+      }
+
+      NotesBranchUtil notesBranchUtil = notesBranchUtilFactory.create(db);
+      notesBranchUtil.commitAllNotes(notes, REFS_NOTES_REVIEW, author,
+          message.toString());
     } catch (IOException e) {
       throw new CodeReviewNoteCreationException(e);
-    } catch (InterruptedException e) {
+    } catch (ConcurrentRefUpdateException e) {
       throw new CodeReviewNoteCreationException(e);
     } finally {
-      release();
+      revWalk.release();
+      inserter.release();
     }
   }
 
-  public void loadBase() throws IOException {
-    Ref notesBranch = db.getRef(REFS_NOTES_REVIEW);
-    if (notesBranch != null) {
-      baseCommit = revWalk.parseCommit(notesBranch.getObjectId());
-      base = NoteMap.read(revWalk.getObjectReader(), baseCommit);
-    }
-    if (baseCommit != null) {
-      ours = NoteMap.read(db.newObjectReader(), baseCommit);
-    } else {
-      ours = NoteMap.newEmptyMap();
+  public void create(List<Change> changes, PersonIdent author,
+      String commitMessage, ProgressMonitor monitor) throws OrmException,
+      IOException, CodeReviewNoteCreationException {
+    try {
+      revWalk = new RevWalk(db);
+      inserter = db.newObjectInserter();
+      if (author != null) {
+        this.author = author;
+      }
+      if (monitor == null) {
+        monitor = NullProgressMonitor.INSTANCE;
+      }
+
+      NoteMap notes = NoteMap.newEmptyMap();
+      for (Change c : changes) {
+        monitor.update(1);
+        PatchSet ps = schema.patchSets().get(c.currentPatchSetId());
+        ObjectId commitId = ObjectId.fromString(ps.getRevision().get());
+        notes.set(commitId, createNoteContent(c, commitId));
+      }
+
+      NotesBranchUtil notesBranchUtil = notesBranchUtilFactory.create(db);
+      notesBranchUtil.commitAllNotes(notes, REFS_NOTES_REVIEW, author,
+          commitMessage);
+    } catch (ConcurrentRefUpdateException e) {
+      throw new CodeReviewNoteCreationException(e);
+    } finally {
+      revWalk.release();
+      inserter.release();
     }
   }
 
-  private void applyNotes() throws IOException, CodeReviewNoteCreationException {
-    StringBuilder message =
-        new StringBuilder("Update notes for submitted changes\n\n");
-    for (CodeReviewCommit c : commits) {
-      add(c.change, c);
-      message.append("* ").append(c.getShortMessage()).append("\n");
-    }
-    commit(message.toString());
-  }
-
-  public void commit(String message) throws IOException {
-    if (baseCommit != null) {
-      oursCommit = createCommit(ours, author, message, baseCommit);
-    } else {
-      oursCommit = createCommit(ours, author, message);
-    }
-  }
-
-  public void add(Change change, ObjectId commit)
-      throws MissingObjectException, IncorrectObjectTypeException, IOException,
-      CodeReviewNoteCreationException {
+  private ObjectId createNoteContent(Change change, ObjectId commit)
+      throws CodeReviewNoteCreationException, IOException  {
     if (!(commit instanceof RevCommit)) {
       commit = revWalk.parseCommit(commit);
     }
-
-    RevCommit c = (RevCommit) commit;
-    ObjectId noteContent = createNoteContent(change, c);
-    if (ours.contains(c)) {
-      // merge the existing and the new note as if they are both new
-      // means: base == null
-      // there is not really a common ancestry for these two note revisions
-      // use the same NoteMerger that is used from the NoteMapMerger
-      NoteMerger noteMerger = new ReviewNoteMerger();
-      Note newNote = new Note(c, noteContent);
-      noteContent = noteMerger.merge(null, newNote, ours.getNote(c),
-          reader, inserter).getData();
-    }
-    ours.set(c, noteContent);
+    return createNoteContent(change, (RevCommit) commit);
   }
 
   private ObjectId createNoteContent(Change change, RevCommit commit)
@@ -227,83 +209,4 @@
       throw new CodeReviewNoteCreationException(commit, e);
     }
   }
-
-  public void updateRef() throws IOException, InterruptedException,
-      CodeReviewNoteCreationException, MissingObjectException,
-      IncorrectObjectTypeException, CorruptObjectException {
-    if (baseCommit != null && oursCommit.getTree().equals(baseCommit.getTree())) {
-      // If the trees are identical, there is no change in the notes.
-      // Avoid saving this commit as it has no new information.
-      return;
-    }
-
-    int remainingLockFailureCalls = MAX_LOCK_FAILURE_CALLS;
-    RefUpdate refUpdate = createRefUpdate(oursCommit, baseCommit);
-
-    for (;;) {
-      Result result = refUpdate.update();
-
-      if (result == Result.LOCK_FAILURE) {
-        if (--remainingLockFailureCalls > 0) {
-          Thread.sleep(SLEEP_ON_LOCK_FAILURE_MS);
-        } else {
-          throw new CodeReviewNoteCreationException(
-              "Failed to lock the ref: " + REFS_NOTES_REVIEW);
-        }
-
-      } else if (result == Result.REJECTED) {
-        RevCommit theirsCommit =
-            revWalk.parseCommit(refUpdate.getOldObjectId());
-        NoteMap theirs =
-            NoteMap.read(revWalk.getObjectReader(), theirsCommit);
-        NoteMapMerger merger = new NoteMapMerger(db);
-        NoteMap merged = merger.merge(base, ours, theirs);
-        RevCommit mergeCommit =
-            createCommit(merged, gerritIdent, "Merged note commits\n",
-                theirsCommit, oursCommit);
-        refUpdate = createRefUpdate(mergeCommit, theirsCommit);
-        remainingLockFailureCalls = MAX_LOCK_FAILURE_CALLS;
-
-      } else if (result == Result.IO_FAILURE) {
-        throw new CodeReviewNoteCreationException(
-            "Couldn't create code review notes because of IO_FAILURE");
-      } else {
-        break;
-      }
-    }
-  }
-
-  public void release() {
-    reader.release();
-    inserter.release();
-    revWalk.release();
-  }
-
-  private RevCommit createCommit(NoteMap map, PersonIdent author,
-      String message, RevCommit... parents) throws IOException {
-    CommitBuilder b = new CommitBuilder();
-    b.setTreeId(map.writeTree(inserter));
-    b.setAuthor(author != null ? author : gerritIdent);
-    b.setCommitter(gerritIdent);
-    if (parents.length > 0) {
-      b.setParentIds(parents);
-    }
-    b.setMessage(message);
-    ObjectId commitId = inserter.insert(b);
-    inserter.flush();
-    return revWalk.parseCommit(commitId);
-  }
-
-
-  private RefUpdate createRefUpdate(ObjectId newObjectId,
-      ObjectId expectedOldObjectId) throws IOException {
-    RefUpdate refUpdate = db.updateRef(REFS_NOTES_REVIEW);
-    refUpdate.setNewObjectId(newObjectId);
-    if (expectedOldObjectId == null) {
-      refUpdate.setExpectedOldObjectId(ObjectId.zeroId());
-    } else {
-      refUpdate.setExpectedOldObjectId(expectedOldObjectId);
-    }
-    return refUpdate;
-  }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/NotesBranchUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/NotesBranchUtil.java
new file mode 100644
index 0000000..17cfea8
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/NotesBranchUtil.java
@@ -0,0 +1,271 @@
+// Copyright (C) 2012 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.git;
+
+import com.google.gerrit.server.GerritPersonIdent;
+import com.google.inject.Inject;
+import com.google.inject.assistedinject.Assisted;
+
+import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
+import org.eclipse.jgit.errors.CorruptObjectException;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
+import org.eclipse.jgit.lib.CommitBuilder;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectInserter;
+import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.RefUpdate.Result;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.merge.MergeStrategy;
+import org.eclipse.jgit.notes.Note;
+import org.eclipse.jgit.notes.NoteMap;
+import org.eclipse.jgit.notes.NoteMapMerger;
+import org.eclipse.jgit.notes.NoteMerger;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
+
+import java.io.IOException;
+
+/**
+ * A utility class for updating a notes branch with automatic merge of note
+ * trees.
+ */
+public class NotesBranchUtil {
+  public interface Factory {
+    NotesBranchUtil create(Repository db);
+  }
+
+  private static final int MAX_LOCK_FAILURE_CALLS = 10;
+  private static final int SLEEP_ON_LOCK_FAILURE_MS = 25;
+
+  private PersonIdent gerritIdent;
+  private final Repository db;
+
+  private RevCommit baseCommit;
+  private NoteMap base;
+
+  private RevCommit oursCommit;
+  private NoteMap ours;
+
+  private RevWalk revWalk;
+  private ObjectInserter inserter;
+  private ObjectReader reader;
+  private boolean overwrite;
+
+  private ReviewNoteMerger noteMerger;
+
+  @Inject
+  public NotesBranchUtil(@GerritPersonIdent final PersonIdent gerritIdent,
+      @Assisted Repository db) {
+    this.gerritIdent = gerritIdent;
+    this.db = db;
+  }
+
+  /**
+   * Create a new commit in the <code>notesBranch</code> by updating existing
+   * or creating new notes from the <code>notes</code> map.
+   *
+   * @param notes map of notes
+   * @param notesBranch notes branch to update
+   * @param commitAuthor author of the commit in the notes branch
+   * @param commitMessage for the commit in the notes branch
+   * @throws IOException
+   * @throws ConcurrentRefUpdateException
+   */
+  public final void commitAllNotes(NoteMap notes, String notesBranch,
+      PersonIdent commitAuthor, String commitMessage) throws IOException,
+      ConcurrentRefUpdateException {
+    this.overwrite = true;
+    commitNotes(notes, notesBranch, commitAuthor, commitMessage);
+  }
+
+  /**
+   * Create a new commit in the <code>notesBranch</code> by creating not yet
+   * existing notes from the <code>notes</code> map. The notes from the
+   * <code>notes</code> map which already exist in the note-tree of the
+   * tip of the <code>notesBranch</code> will not be updated.
+   *
+   * @param notes map of notes
+   * @param notesBranch notes branch to update
+   * @param commitAuthor author of the commit in the notes branch
+   * @param commitMessage for the commit in the notes branch
+   * @return map with those notes from the <code>notes</code> that were newly
+   *         created
+   * @throws IOException
+   * @throws ConcurrentRefUpdateException
+   */
+  public final NoteMap commitNewNotes(NoteMap notes, String notesBranch,
+      PersonIdent commitAuthor, String commitMessage) throws IOException,
+      ConcurrentRefUpdateException {
+    this.overwrite = false;
+    commitNotes(notes, notesBranch, commitAuthor, commitMessage);
+    NoteMap newlyCreated = NoteMap.newEmptyMap();
+    for (Note n : notes) {
+      if (base == null || !base.contains(n)) {
+        newlyCreated.set(n, n.getData());
+      }
+    }
+    return newlyCreated;
+  }
+
+  private void commitNotes(NoteMap notes, String notesBranch,
+      PersonIdent commitAuthor, String commitMessage) throws IOException,
+      ConcurrentRefUpdateException {
+    try {
+      revWalk = new RevWalk(db);
+      inserter = db.newObjectInserter();
+      reader = db.newObjectReader();
+      loadBase(notesBranch);
+      if (overwrite) {
+        addAllNotes(notes);
+      } else {
+        addNewNotes(notes);
+      }
+      if (base != null) {
+        oursCommit = createCommit(ours, commitAuthor, commitMessage, baseCommit);
+      } else {
+        oursCommit = createCommit(ours, commitAuthor, commitMessage);
+      }
+      updateRef(notesBranch);
+    } finally {
+      revWalk.release();
+      inserter.release();
+      reader.release();
+    }
+  }
+
+  private void addNewNotes(NoteMap notes) throws IOException {
+    for (Note n : notes) {
+      if (! ours.contains(n)) {
+        ours.set(n, n.getData());
+      }
+    }
+  }
+
+  private void addAllNotes(NoteMap notes) throws IOException {
+    for (Note n : notes) {
+      if (ours.contains(n)) {
+        // Merge the existing and the new note as if they are both new,
+        // means: base == null
+        // There is no really a common ancestry for these two note revisions
+        ObjectId noteContent = getNoteMerger().merge(null, n, ours.getNote(n),
+            reader, inserter).getData();
+        ours.set(n, noteContent);
+      } else {
+        ours.set(n, n.getData());
+      }
+    }
+  }
+
+  private NoteMerger getNoteMerger() {
+    if (noteMerger == null) {
+      noteMerger = new ReviewNoteMerger();
+    }
+    return noteMerger;
+  }
+
+  private void loadBase(String notesBranch) throws IOException {
+    Ref branch = db.getRef(notesBranch);
+    if (branch != null) {
+      baseCommit = revWalk.parseCommit(branch.getObjectId());
+      base = NoteMap.read(revWalk.getObjectReader(), baseCommit);
+    }
+    if (baseCommit != null) {
+      ours = NoteMap.read(revWalk.getObjectReader(), baseCommit);
+    } else {
+      ours = NoteMap.newEmptyMap();
+    }
+  }
+
+  private RevCommit createCommit(NoteMap map, PersonIdent author,
+      String message, RevCommit... parents) throws IOException {
+    CommitBuilder b = new CommitBuilder();
+    b.setTreeId(map.writeTree(inserter));
+    b.setAuthor(author != null ? author : gerritIdent);
+    b.setCommitter(gerritIdent);
+    if (parents.length > 0) {
+      b.setParentIds(parents);
+    }
+    b.setMessage(message);
+    ObjectId commitId = inserter.insert(b);
+    inserter.flush();
+    return revWalk.parseCommit(commitId);
+  }
+
+  private void updateRef(String notesBranch) throws IOException,
+      MissingObjectException, IncorrectObjectTypeException,
+      CorruptObjectException, ConcurrentRefUpdateException {
+    if (baseCommit != null && oursCommit.getTree().equals(baseCommit.getTree())) {
+      // If the trees are identical, there is no change in the notes.
+      // Avoid saving this commit as it has no new information.
+      return;
+    }
+
+    int remainingLockFailureCalls = MAX_LOCK_FAILURE_CALLS;
+    RefUpdate refUpdate = createRefUpdate(notesBranch, oursCommit, baseCommit);
+
+    for (;;) {
+      Result result = refUpdate.update();
+
+      if (result == Result.LOCK_FAILURE) {
+        if (--remainingLockFailureCalls > 0) {
+          try {
+            Thread.sleep(SLEEP_ON_LOCK_FAILURE_MS);
+          } catch (InterruptedException e) {
+            // ignore
+          }
+        } else {
+          throw new ConcurrentRefUpdateException("Failed to lock the ref: "
+              + notesBranch, db.getRef(notesBranch), result);
+        }
+
+      } else if (result == Result.REJECTED) {
+        RevCommit theirsCommit =
+            revWalk.parseCommit(refUpdate.getOldObjectId());
+        NoteMap theirs =
+            NoteMap.read(revWalk.getObjectReader(), theirsCommit);
+        NoteMapMerger merger =
+            new NoteMapMerger(db, getNoteMerger(), MergeStrategy.RESOLVE);
+        NoteMap merged = merger.merge(base, ours, theirs);
+        RevCommit mergeCommit =
+            createCommit(merged, gerritIdent, "Merged note commits\n",
+                theirsCommit, oursCommit);
+        refUpdate = createRefUpdate(notesBranch, mergeCommit, theirsCommit);
+        remainingLockFailureCalls = MAX_LOCK_FAILURE_CALLS;
+
+      } else if (result == Result.IO_FAILURE) {
+        throw new IOException("Couldn't update " + notesBranch + ". "
+            + result.name());
+      } else {
+        break;
+      }
+    }
+  }
+
+  private RefUpdate createRefUpdate(String notesBranch, ObjectId newObjectId,
+      ObjectId expectedOldObjectId) throws IOException {
+    RefUpdate refUpdate = db.updateRef(notesBranch);
+    refUpdate.setNewObjectId(newObjectId);
+    if (expectedOldObjectId == null) {
+      refUpdate.setExpectedOldObjectId(ObjectId.zeroId());
+    } else {
+      refUpdate.setExpectedOldObjectId(expectedOldObjectId);
+    }
+    return refUpdate;
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
index 7387fd1..41f2aa6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
@@ -211,7 +211,8 @@
 
   /** Can this user restore this change? */
   public boolean canRestore() {
-    return canAbandon(); // Anyone who can abandon the change can restore it back
+    return canAbandon() // Anyone who can abandon the change can restore it back
+        && getRefControl().canUpload(); // as long as you can upload too
   }
 
   /** All value ranges of any allowed label permission. */
diff --git a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeSubject.vm b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeSubject.vm
index a67c38c..bb3c127 100644
--- a/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeSubject.vm
+++ b/gerrit-server/src/main/resources/com/google/gerrit/server/mail/ChangeSubject.vm
@@ -32,6 +32,6 @@
 ## subject line for ALL emails related to changes.
 ##
 #macro(elipses $length $str)
-#if($str.length() > $length)${str.substring(0,$length)}...#else$str#end
+#if(($str.length()+3) > $length)${str.substring(0,$length)}...#else$str#end
 #end
 Change in $projectName.replaceAll('/.*/', '...')[$branch.shortName]: #elipses(60, $change.subject)
diff --git a/gerrit-sshd/.settings/org.eclipse.core.resources.prefs b/gerrit-sshd/.settings/org.eclipse.core.resources.prefs
index 0871ea8..839d647 100644
--- a/gerrit-sshd/.settings/org.eclipse.core.resources.prefs
+++ b/gerrit-sshd/.settings/org.eclipse.core.resources.prefs
@@ -1,4 +1,3 @@
-#Tue May 15 09:21:12 PDT 2012
 eclipse.preferences.version=1
 encoding//src/main/java=UTF-8
 encoding//src/main/resources=UTF-8
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/BanCommitCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
index 4350d1e..939d68a 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/BanCommitCommand.java
@@ -22,6 +22,7 @@
 import com.google.gerrit.sshd.SshCommand;
 import com.google.inject.Inject;
 
+import org.eclipse.jgit.api.errors.ConcurrentRefUpdateException;
 import org.eclipse.jgit.lib.ObjectId;
 import org.kohsuke.args4j.Argument;
 import org.kohsuke.args4j.Option;
@@ -80,6 +81,8 @@
       throw die(e);
     } catch (InterruptedException e) {
       throw die(e);
+    } catch (ConcurrentRefUpdateException e) {
+      throw die(e);
     }
   }
 
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
index 8ce1e82..5ebb6c7 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
@@ -68,7 +68,8 @@
 
   private final Set<PatchSet.Id> patchSetIds = new HashSet<PatchSet.Id>();
 
-  @Argument(index = 0, required = true, multiValued = true, metaVar = "{COMMIT | CHANGE,PATCHSET}", usage = "patch to review")
+  @Argument(index = 0, required = true, multiValued = true, metaVar = "{COMMIT | CHANGE,PATCHSET}",
+      usage = "list of commits or patch sets to review")
   void addPatchSetId(final String token) {
     try {
       patchSetIds.addAll(parsePatchSetId(token));
@@ -79,29 +80,29 @@
     }
   }
 
-  @Option(name = "--project", aliases = "-p", usage = "project containing the patch set")
+  @Option(name = "--project", aliases = "-p", usage = "project containing the specified patch set(s)")
   private ProjectControl projectControl;
 
-  @Option(name = "--message", aliases = "-m", usage = "cover message to publish on change", metaVar = "MESSAGE")
+  @Option(name = "--message", aliases = "-m", usage = "cover message to publish on change(s)", metaVar = "MESSAGE")
   private String changeComment;
 
-  @Option(name = "--abandon", usage = "abandon the patch set")
+  @Option(name = "--abandon", usage = "abandon the specified change(s)")
   private boolean abandonChange;
 
-  @Option(name = "--restore", usage = "restore an abandoned the patch set")
+  @Option(name = "--restore", usage = "restore the specified abandoned change(s)")
   private boolean restoreChange;
 
-  @Option(name = "--submit", aliases = "-s", usage = "submit the patch set")
+  @Option(name = "--submit", aliases = "-s", usage = "submit the specified patch set(s)")
   private boolean submitChange;
 
   @Option(name = "--force-message", usage = "publish the message, "
-      + "even if the label score cannot be applied due to change being closed")
+      + "even if the label score cannot be applied due to the change being closed")
   private boolean forceMessage = false;
 
-  @Option(name = "--publish", usage = "publish a draft patch set")
+  @Option(name = "--publish", usage = "publish the specified draft patch set(s)")
   private boolean publishPatchSet;
 
-  @Option(name = "--delete", usage = "delete a draft patch set")
+  @Option(name = "--delete", usage = "delete the specified draft patch set(s)")
   private boolean deleteDraftPatchSet;
 
   @Inject
@@ -266,7 +267,7 @@
           errMsg += "rule error";
           break;
         case NOT_A_DRAFT:
-          errMsg += "change is not a draft";
+          errMsg += "change/patch set is not a draft";
           break;
         case GIT_ERROR:
           errMsg += "error writing change to git repository";
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/StreamEvents.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/StreamEvents.java
old mode 100755
new mode 100644
diff --git a/gerrit-util-cli/.settings/org.eclipse.core.resources.prefs b/gerrit-util-cli/.settings/org.eclipse.core.resources.prefs
index c780f44..e9441bb 100644
--- a/gerrit-util-cli/.settings/org.eclipse.core.resources.prefs
+++ b/gerrit-util-cli/.settings/org.eclipse.core.resources.prefs
@@ -1,4 +1,3 @@
-#Thu Jul 28 11:02:36 PDT 2011
 eclipse.preferences.version=1
 encoding//src/main/java=UTF-8
 encoding/<project>=UTF-8
diff --git a/gerrit-util-ssl/.settings/org.eclipse.core.resources.prefs b/gerrit-util-ssl/.settings/org.eclipse.core.resources.prefs
index 589908f..e9441bb 100644
--- a/gerrit-util-ssl/.settings/org.eclipse.core.resources.prefs
+++ b/gerrit-util-ssl/.settings/org.eclipse.core.resources.prefs
@@ -1,4 +1,3 @@
-#Thu Jul 28 11:02:35 PDT 2011
 eclipse.preferences.version=1
 encoding//src/main/java=UTF-8
 encoding/<project>=UTF-8
diff --git a/gerrit-war/.settings/org.eclipse.core.resources.prefs b/gerrit-war/.settings/org.eclipse.core.resources.prefs
index d404b00..abdea9ac 100644
--- a/gerrit-war/.settings/org.eclipse.core.resources.prefs
+++ b/gerrit-war/.settings/org.eclipse.core.resources.prefs
@@ -1,4 +1,3 @@
-#Thu Jul 28 11:02:37 PDT 2011
 eclipse.preferences.version=1
 encoding//src/main/java=UTF-8
 encoding//src/main/resources=UTF-8
diff --git a/pom.xml b/pom.xml
index 23c9d09..d7b5988 100644
--- a/pom.xml
+++ b/pom.xml
@@ -478,7 +478,7 @@
       <dependency>
         <groupId>com.google.guava</groupId>
         <artifactId>guava</artifactId>
-        <version>12.0</version>
+        <version>12.0.1</version>
       </dependency>
 
       <dependency>