Merge "git-exproll.sh: Return nonzero on errors."
diff --git a/.gitmodules b/.gitmodules
index 0f7fdab..e45868b 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -9,3 +9,7 @@
 [submodule "plugins/commit-message-length-validator"]
 	path = plugins/commit-message-length-validator
 	url = https://gerrit.googlesource.com/plugins/commit-message-length-validator
+
+[submodule "plugins/helloworld"]
+	path = plugins/helloworld
+	url = https://gerrit.googlesource.com/plugins/helloworld
diff --git a/Documentation/rest-api-accounts.txt b/Documentation/rest-api-accounts.txt
index a78385a..9850c40 100644
--- a/Documentation/rest-api-accounts.txt
+++ b/Documentation/rest-api-accounts.txt
@@ -161,6 +161,67 @@
   HTTP/1.1 204 No Content
 ----
 
+[[get-active]]
+Get Active
+~~~~~~~~~~
+[verse]
+'GET /accounts/link:#account-id[\{account-id\}]/active'
+
+Checks if an account is active.
+
+.Request
+----
+  GET /accounts/john.doe@example.com/active HTTP/1.0
+----
+
+As response `200 OK` is returned for an active account and
+`404 Not Found` is returned for an inactive account.
+
+.Response
+----
+  HTTP/1.1 200 OK
+----
+
+[[set-active]]
+Set Active
+~~~~~~~~~~
+[verse]
+'PUT /accounts/link:#account-id[\{account-id\}]/active'
+
+Sets the account state to active.
+
+.Request
+----
+  PUT /accounts/john.doe@example.com/active HTTP/1.0
+----
+
+.Response
+----
+  HTTP/1.1 201 Created
+----
+
+If the account was already active the response is `200 OK`.
+
+[[delete-active]]
+Delete Active
+~~~~~~~~~~~~~
+[verse]
+'DELETE /accounts/link:#account-id[\{account-id\}]/active'
+
+Sets the account state to inactive.
+
+.Request
+----
+  DELETE /accounts/john.doe@example.com/active HTTP/1.0
+----
+
+.Response
+----
+  HTTP/1.1 204 No Content
+----
+
+If the account was already inactive the response is `404 Not Found`.
+
 [[list-account-emails]]
 List Account Emails
 ~~~~~~~~~~~~~~~~~~~
@@ -244,11 +305,6 @@
 .Request
 ----
   PUT /accounts/self/emails/john.doe@example.com HTTP/1.0
-  Content-Type: application/json;charset=UTF-8
-
-  {
-    "preferred": true
-  }
 ----
 
 As response the new email address is returned as
@@ -263,10 +319,28 @@
   )]}'
   {
     "email": "john.doe@example.com",
-    "preferred": true
+    "pending_confirmation": true
   }
 ----
 
+[[delete-account-email]]
+Delete Account Email
+~~~~~~~~~~~~~~~~~~~~
+[verse]
+'DELETE /accounts/link:#account-id[\{account-id\}]/emails/link:#email-id[\{email-id\}]'
+
+Deletes an email address of an account.
+
+.Request
+----
+  DELETE /accounts/self/emails/john.doe@example.com HTTP/1.0
+----
+
+.Response
+----
+  HTTP/1.1 204 No Content
+----
+
 [[set-preferred-email]]
 Set Preferred Email
 ~~~~~~~~~~~~~~~~~~~
@@ -882,7 +956,8 @@
 URL.
 |`preferred`      |`false` if not set|
 Whether the new email address should become the preferred email address
-of the user.
+of the user (only supported if `no_confirmation` is set or if the
+authentication type is `DEVELOPMENT_BECOME_ANY_ACCOUNT`).
 |`no_confirmation`|`false` if not set|
 Whether the email address should be added without confirmation. In this
 case no verification email is sent to the user. +
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
index 053146b..ddec7d5 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
@@ -423,6 +423,14 @@
       }
     }
     if (emailPick.getItemCount() > 0) {
+      if (currentEmail == null) {
+        int index = emailListIndexOf("");
+        if (index != -1) {
+          emailPick.removeItem(index);
+        }
+        emailPick.insertItem("", 0);
+        emailPick.setSelectedIndex(0);
+      }
       emailPick.setVisible(true);
       emailPick.setEnabled(true);
       if (canRegisterNewEmail()) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CodeMirrorDemo.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CodeMirrorDemo.java
index 66abe88..70b9ff9 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CodeMirrorDemo.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/CodeMirrorDemo.java
@@ -14,19 +14,24 @@
 
 package com.google.gerrit.client.diff;
 
+import com.google.gerrit.client.diff.DiffInfo.Region;
 import com.google.gerrit.client.rpc.CallbackGroup;
 import com.google.gerrit.client.rpc.GerritCallback;
 import com.google.gerrit.client.rpc.ScreenLoadCallback;
 import com.google.gerrit.client.ui.Screen;
 import com.google.gerrit.reviewdb.client.PatchSet;
+import com.google.gwt.core.client.JsArray;
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.event.logical.shared.ResizeEvent;
 import com.google.gwt.event.logical.shared.ResizeHandler;
 import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.Window;
 
 import net.codemirror.lib.CodeMirror;
+import net.codemirror.lib.CodeMirror.LineClassWhere;
 import net.codemirror.lib.Configuration;
+import net.codemirror.lib.LineCharacter;
 import net.codemirror.lib.ModeInjector;
 
 public class CodeMirrorDemo extends Screen {
@@ -116,6 +121,7 @@
   private void display(DiffInfo diff) {
     cmA = displaySide(diff.meta_a(), diff.text_a(), diffTable.getCmA());
     cmB = displaySide(diff.meta_b(), diff.text_b(), diffTable.getCmB());
+    render(diff);
     resizeHandler = Window.addResizeHandler(new ResizeHandler() {
       @Override
       public void onResize(ResizeEvent event) {
@@ -141,13 +147,65 @@
       .set("lineNumbers", true)
       .set("tabSize", 2)
       .set("mode", getContentType(meta))
-      .set("value", contents);
+      .set("value", contents)
+      .setInfinity("viewportMargin");
     final CodeMirror cm = CodeMirror.create(ele, cfg);
     cm.setWidth("100%");
     cm.setHeight(Window.getClientHeight() - HEADER_FOOTER);
     return cm;
   }
 
+  private void addPadding(CodeMirror cm, int line) {
+    Element div = DOM.createDiv();
+    div.setClassName(diffTable.style.padding());
+    cm.addLineWidget(line, div, null);
+  }
+
+  private void render(DiffInfo diff) {
+    JsArray<Region> regions = diff.content();
+    Configuration insertOpt = Configuration.create()
+        .set("className", diffTable.style.insert())
+        .set("readOnly", true);
+    Configuration deleteOpt = Configuration.create()
+        .set("className", diffTable.style.delete())
+        .set("readOnly", true);
+    int lineA = 0, lineB = 0;
+    for (int i = 0; i < regions.length(); i++) {
+      Region current = regions.get(i);
+      if (current.ab() != null) {
+        lineA += current.ab().length();
+        lineB += current.ab().length();
+      } else if (current.a() == null && current.b() != null) {
+        int delta = current.b().length();
+        for (int j = 0; j < delta; j++) {
+          addPadding(cmA, lineA - 1);
+        }
+        for (int j = 0; j < delta; j++) {
+          cmB.addLineClass(lineB, LineClassWhere.WRAP,
+              diffTable.style.insert());
+          LineCharacter from = LineCharacter.create(lineB, 0);
+          cmB.markText(from, from, insertOpt);
+          lineB++;
+        }
+      } else if (current.a() != null && current.b() == null) {
+        int delta = current.a().length();
+        for (int j = 0; j < delta; j++) {
+          addPadding(cmB, lineB - 1);
+        }
+        for (int j = 0; j < delta; j++) {
+          cmA.addLineClass(lineA, LineClassWhere.WRAP,
+              diffTable.style.delete());
+          LineCharacter from = LineCharacter.create(lineA, 0);
+          cmA.markText(from, from, deleteOpt);
+          lineA++;
+        }
+      } else { // TODO: Handle intraline edit.
+        lineA += current.a().length();
+        lineB += current.a().length();
+      }
+    }
+  }
+
   private static String getContentType(DiffInfo.FileMeta meta) {
     return meta != null && meta.content_type() != null
         ? ModeInjector.getContentType(meta.content_type())
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java
index bb223e1..e681630 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.java
@@ -16,6 +16,7 @@
 
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.dom.client.DivElement;
+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.Composite;
@@ -29,12 +30,22 @@
   interface Binder extends UiBinder<HTMLPanel, DiffTable> {}
   private static Binder uiBinder = GWT.create(Binder.class);
 
+  interface LineStyle extends CssResource {
+    String insert();
+    String delete();
+    String intraline();
+    String padding();
+  }
+
   @UiField
   DivElement cmA;
 
   @UiField
   DivElement cmB;
 
+  @UiField
+  LineStyle style;
+
   DiffTable() {
     initWidget(uiBinder.createAndBindUi(this));
   }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml
index 735a0e6..5e4cf0d 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/diff/DiffTable.ui.xml
@@ -16,7 +16,49 @@
 -->
 <ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
     xmlns:g='urn:import:com.google.gwt.user.client.ui'>
-  <g:HTMLPanel>
+  <ui:style field='css'>
+    @external .CodeMirror, .CodeMirror-selectedtext, .CodeMirror-scroll;
+
+    .difftable .CodeMirror {
+      border: 1px solid #eee;
+      height: auto;
+    }
+    .difftable .CodeMirror pre {
+      padding: 0;
+      overflow: hidden;
+    }
+    .difftable .CodeMirror-selectedtext.intraline,
+    .difftable .CodeMirror-selectedtext.insert {
+      opacity: 0.5;
+    }
+    .difftable .CodeMirror-scroll {
+      overflow-x: hidden;
+      overflow-y: hidden;
+    }
+    <!--.difftable .CodeMirror-vscrollbar {
+      display: none !important;
+    }-->
+  </ui:style>
+  <ui:style type='com.google.gerrit.client.diff.DiffTable.LineStyle'>
+    @external .CodeMirror-linenumber;
+
+    .insert,
+    .insert .CodeMirror-linenumber {
+      background-color: #dfd;
+    }
+    .delete,
+    .delete .CodeMirror-linenumber {
+      background-color: #fee;
+    }
+    .intraline {
+      background-color: #9f9;
+    }
+    .padding {
+      background-color: #eee;
+      height: 1em;
+    }
+  </ui:style>
+  <g:HTMLPanel styleName='{css.difftable}'>
     <table>
       <tr>
         <td><div ui:field='cmA'></div></td>
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/CodeMirror.gwt.xml b/gerrit-gwtui/src/main/java/net/codemirror/CodeMirror.gwt.xml
index 64ac16d..a3641a553 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/CodeMirror.gwt.xml
+++ b/gerrit-gwtui/src/main/java/net/codemirror/CodeMirror.gwt.xml
@@ -16,6 +16,7 @@
 <module>
   <inherits name='com.google.gwt.logging.Logging'/>
   <inherits name='com.google.gwt.resources.Resources'/>
+  <source path='addon'/>
   <source path='lib'/>
   <source path='mode'/>
 </module>
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/addon/Addons.java b/gerrit-gwtui/src/main/java/net/codemirror/addon/Addons.java
new file mode 100644
index 0000000..1cddc7f
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/net/codemirror/addon/Addons.java
@@ -0,0 +1,28 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package net.codemirror.addon;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.resources.client.ClientBundle;
+import com.google.gwt.resources.client.DataResource;
+import com.google.gwt.resources.client.DataResource.DoNotEmbed;
+
+public interface Addons extends ClientBundle {
+  public static final Addons I = GWT.create(Addons.class);
+
+  @Source("selection/mark-selection.js")
+  @DoNotEmbed
+  DataResource mark_selection();
+}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java
index 11d10df..42664b7 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java
+++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/CodeMirror.java
@@ -28,7 +28,8 @@
     Loader.initLibrary(cb);
   }
 
-  public static native CodeMirror create(Element parent, Configuration cfg) /*-{
+  public static native CodeMirror create(Element parent,
+      Configuration cfg) /*-{
     return $wnd.CodeMirror(parent, cfg);
   }-*/;
 
@@ -42,6 +43,30 @@
   public final native void refresh() /*-{ this.refresh(); }-*/;
   public final native Element getWrapperElement() /*-{ return this.getWrapperElement(); }-*/;
 
+  public final native void markText(LineCharacter from, LineCharacter to,
+      Configuration options) /*-{
+    this.markText(from, to, options);
+  }-*/;
+
+  public enum LineClassWhere {
+    TEXT, BACKGROUND, WRAP;
+  }
+
+  public final void addLineClass(int line, LineClassWhere where,
+      String className) {
+    addLineClassNative(line, where.name().toLowerCase(), className);
+  }
+
+  private final native void addLineClassNative(int line, String where,
+      String lineClass) /*-{
+    this.addLineClass(line, where, lineClass);
+  }-*/;
+
+  public final native void addLineWidget(int line, Element node,
+      Configuration options) /*-{
+    this.addLineWidget(line, node, options);
+  }-*/;
+
   protected CodeMirror() {
   }
 }
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/Configuration.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/Configuration.java
index a121697..57fcc2b 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/Configuration.java
+++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/Configuration.java
@@ -36,6 +36,9 @@
   public final native Configuration set(String name, boolean val)
   /*-{ this[name] = val; return this; }-*/;
 
+  public final native Configuration setInfinity(String name)
+  /*-{ this[name] = Infinity; return this; }-*/;
+
   protected Configuration() {
   }
 }
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/LineCharacter.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/LineCharacter.java
new file mode 100644
index 0000000..8a8e9d5
--- /dev/null
+++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/LineCharacter.java
@@ -0,0 +1,39 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package net.codemirror.lib;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+/** {line, ch} objects used within CodeMirror. */
+public class LineCharacter extends JavaScriptObject {
+  public static LineCharacter create(int line, int ch) {
+    LineCharacter lineCh = createObject().cast();
+    return lineCh.setLine(line).setCh(ch);
+  }
+
+  private final native LineCharacter setLine(int line) /*-{
+    this.line = line; return this;
+  }-*/;
+
+  private final native LineCharacter setCh(int ch) /*-{
+    this.ch = ch; return this;
+  }-*/;
+
+  public final native int getLine() /*-{ return this.line; }-*/;
+  public final native int getCh() /*-{ return this.ch; }-*/;
+
+  protected LineCharacter() {
+  }
+}
diff --git a/gerrit-gwtui/src/main/java/net/codemirror/lib/Loader.java b/gerrit-gwtui/src/main/java/net/codemirror/lib/Loader.java
index 0637a1b..6373906 100644
--- a/gerrit-gwtui/src/main/java/net/codemirror/lib/Loader.java
+++ b/gerrit-gwtui/src/main/java/net/codemirror/lib/Loader.java
@@ -14,6 +14,7 @@
 
 package net.codemirror.lib;
 
+import com.google.gerrit.client.rpc.CallbackGroup;
 import com.google.gwt.core.client.Callback;
 import com.google.gwt.core.client.ScriptInjector;
 import com.google.gwt.dom.client.ScriptElement;
@@ -25,6 +26,8 @@
 import com.google.gwt.safehtml.shared.SafeUri;
 import com.google.gwt.user.client.rpc.AsyncCallback;
 
+import net.codemirror.addon.Addons;
+
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -36,8 +39,14 @@
     if (isLibLoaded()) {
       cb.onSuccess(null);
     } else {
+      CallbackGroup group = new CallbackGroup();
       injectCss(Lib.I.css());
-      injectScript(Lib.I.js().getSafeUri(), cb);
+      injectScript(
+          Lib.I.js().getSafeUri(), group.add(new AsyncCallback<Void>() {
+            public void onFailure(Throwable caught) {}
+            public void onSuccess(Void result) {}
+          }));
+      injectScript(Addons.I.mark_selection().getSafeUri(), group.add(cb));
     }
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
index 9228714..62d29dd 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/ChangeUtil.java
@@ -15,14 +15,11 @@
 package com.google.gerrit.server;
 
 import com.google.gerrit.common.ChangeHooks;
-import com.google.gerrit.common.data.LabelTypes;
 import com.google.gerrit.common.errors.EmailException;
-import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.ChangeMessage;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.PatchSetAncestor;
-import com.google.gerrit.reviewdb.client.PatchSetInfo;
 import com.google.gerrit.reviewdb.client.RevId;
 import com.google.gerrit.reviewdb.client.TrackingId;
 import com.google.gerrit.reviewdb.server.ReviewDb;
@@ -196,7 +193,7 @@
       ReviewDb db, RevertedSender.Factory revertedSenderFactory,
       ChangeHooks hooks, Repository git,
       PatchSetInfoFactory patchSetInfoFactory, PersonIdent myIdent,
-      ChangeInserter changeInserter)
+      ChangeInserter.Factory changeInserterFactory)
           throws NoSuchChangeException, EmailException,
       OrmException, MissingObjectException, IncorrectObjectTypeException,
       IOException, InvalidChangeOperationException {
@@ -251,13 +248,9 @@
           user.getAccountId(),
           changeToRevert.getDest());
       change.setTopic(changeToRevert.getTopic());
-
-      PatchSet.Id id =
-          new PatchSet.Id(change.getId(), Change.INITIAL_PATCH_SET_ID);
-      final PatchSet ps = new PatchSet(id);
-      ps.setCreatedOn(change.getCreatedOn());
-      ps.setUploader(change.getOwner());
-      ps.setRevision(new RevId(revertCommit.name()));
+      ChangeInserter ins =
+          changeInserterFactory.create(refControl, change, revertCommit);
+      PatchSet ps = ins.getPatchSet();
 
       CommitReceivedEvent commitReceivedEvent =
           new CommitReceivedEvent(new ReceiveCommand(ObjectId.zeroId(),
@@ -271,11 +264,6 @@
         throw new InvalidChangeOperationException(e.getMessage());
       }
 
-      PatchSetInfo info = patchSetInfoFactory.get(revertCommit, ps.getId());
-      change.setCurrentPatchSet(info);
-      ChangeUtil.updated(change);
-
-
       final RefUpdate ru = git.updateRef(ps.getRefName());
       ru.setExpectedOldObjectId(ObjectId.zeroId());
       ru.setNewObjectId(revertCommit);
@@ -295,9 +283,7 @@
       msgBuf.append("This patchset was reverted in change: " + change.getKey().get());
       cmsg.setMessage(msgBuf.toString());
 
-      LabelTypes labelTypes = refControl.getProjectControl().getLabelTypes();
-      changeInserter.insertChange(db, change, cmsg, ps, revertCommit,
-          labelTypes, info, Collections.<Account.Id> emptySet());
+      ins.setMessage(cmsg).insert();
 
       final RevertedSender cm = revertedSenderFactory.create(change);
       cm.setFrom(user.getAccountId());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java
index c21d375..10b34ac 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java
@@ -435,42 +435,38 @@
    *         cannot be unlinked at this time.
    */
   public AuthResult unlink(final Account.Id from, AuthRequest who)
-      throws AccountException {
+      throws AccountException, OrmException {
+    final ReviewDb db = schema.open();
     try {
-      final ReviewDb db = schema.open();
-      try {
-        who = realm.unlink(db, from, who);
+      who = realm.unlink(db, from, who);
 
-        final AccountExternalId.Key key = id(who);
-        AccountExternalId extId = db.accountExternalIds().get(key);
-        if (extId != null) {
-          if (!extId.getAccountId().equals(from)) {
-            throw new AccountException("Identity in use by another account");
+      final AccountExternalId.Key key = id(who);
+      AccountExternalId extId = db.accountExternalIds().get(key);
+      if (extId != null) {
+        if (!extId.getAccountId().equals(from)) {
+          throw new AccountException("Identity in use by another account");
+        }
+        db.accountExternalIds().delete(Collections.singleton(extId));
+
+        if (who.getEmailAddress() != null) {
+          final Account a = db.accounts().get(from);
+          if (a.getPreferredEmail() != null
+              && a.getPreferredEmail().equals(who.getEmailAddress())) {
+            a.setPreferredEmail(null);
+            db.accounts().update(Collections.singleton(a));
           }
-          db.accountExternalIds().delete(Collections.singleton(extId));
-
-          if (who.getEmailAddress() != null) {
-            final Account a = db.accounts().get(from);
-            if (a.getPreferredEmail() != null
-                && a.getPreferredEmail().equals(who.getEmailAddress())) {
-              a.setPreferredEmail(null);
-              db.accounts().update(Collections.singleton(a));
-            }
-            byEmailCache.evict(who.getEmailAddress());
-            byIdCache.evict(from);
-          }
-
-        } else {
-          throw new AccountException("Identity not found");
+          byEmailCache.evict(who.getEmailAddress());
+          byIdCache.evict(from);
         }
 
-        return new AuthResult(from, key, false);
-
-      } finally {
-        db.close();
+      } else {
+        throw new AccountException("Identity not found");
       }
-    } catch (OrmException e) {
-      throw new AccountException("Cannot unlink identity", e);
+
+      return new AuthResult(from, key, false);
+
+    } finally {
+      db.close();
     }
   }
 
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteActive.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteActive.java
new file mode 100644
index 0000000..8342d77
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteActive.java
@@ -0,0 +1,60 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.account;
+
+import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.account.DeleteActive.Input;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import java.util.Collections;
+
+@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
+public class DeleteActive implements RestModifyView<AccountResource, Input> {
+  static class Input {
+  }
+
+  private final Provider<ReviewDb> dbProvider;
+  private final AccountCache byIdCache;
+
+  @Inject
+  DeleteActive(Provider<ReviewDb> dbProvider, AccountCache byIdCache) {
+    this.dbProvider = dbProvider;
+    this.byIdCache = byIdCache;
+  }
+
+  @Override
+  public Object apply(AccountResource rsrc, Input input)
+      throws ResourceNotFoundException, OrmException {
+    Account a = dbProvider.get().accounts().get(rsrc.getUser().getAccountId());
+    if (a == null) {
+      throw new ResourceNotFoundException("account not found");
+    }
+    if (!a.isActive()) {
+      throw new ResourceNotFoundException();
+    }
+    a.setActive(false);
+    dbProvider.get().accounts().update(Collections.singleton(a));
+    byIdCache.evict(a.getId());
+    return Response.none();
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteEmail.java
new file mode 100644
index 0000000..67bb968
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/DeleteEmail.java
@@ -0,0 +1,75 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.account;
+
+import com.google.gerrit.extensions.restapi.AuthException;
+import com.google.gerrit.extensions.restapi.MethodNotAllowedException;
+import com.google.gerrit.extensions.restapi.ResourceConflictException;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.reviewdb.client.Account.FieldName;
+import com.google.gerrit.reviewdb.client.AccountExternalId;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.account.DeleteEmail.Input;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+public class DeleteEmail implements RestModifyView<AccountResource.Email, Input> {
+  static class Input {
+  }
+
+  private final Provider<CurrentUser> self;
+  private final Realm realm;
+  private final Provider<ReviewDb> dbProvider;
+  private final AccountManager accountManager;
+
+  @Inject
+  DeleteEmail(Provider<CurrentUser> self, Realm realm,
+      Provider<ReviewDb> dbProvider, AccountManager accountManager) {
+    this.self = self;
+    this.realm = realm;
+    this.dbProvider = dbProvider;
+    this.accountManager = accountManager;
+  }
+
+  @Override
+  public Object apply(AccountResource.Email rsrc, Input input)
+      throws AuthException, ResourceNotFoundException,
+      ResourceConflictException, MethodNotAllowedException, OrmException {
+    if (self.get() != rsrc.getUser()
+        && !self.get().getCapabilities().canAdministrateServer()) {
+      throw new AuthException("not allowed to delete email address");
+    }
+    if (!realm.allowsEdit(FieldName.REGISTER_NEW_EMAIL)) {
+      throw new MethodNotAllowedException("realm does not allow deleting emails");
+    }
+    AccountExternalId.Key key = new AccountExternalId.Key(
+        AccountExternalId.SCHEME_MAILTO, rsrc.getEmail());
+    AccountExternalId extId = dbProvider.get().accountExternalIds().get(key);
+    if (extId == null) {
+      throw new ResourceNotFoundException(rsrc.getEmail());
+    }
+    try {
+      accountManager.unlink(rsrc.getUser().getAccountId(),
+          AuthRequest.forEmail(rsrc.getEmail()));
+    } catch (AccountException e) {
+      throw new ResourceConflictException(e.getMessage());
+    }
+    return Response.none();
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GetActive.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetActive.java
new file mode 100644
index 0000000..76c7ddb
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GetActive.java
@@ -0,0 +1,29 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.account;
+
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestReadView;
+
+public class GetActive implements RestReadView<AccountResource> {
+  @Override
+  public Object apply(AccountResource rsrc) throws ResourceNotFoundException {
+    if (rsrc.getUser().getAccount().isActive()) {
+      return Response.ok("");
+    }
+    throw new ResourceNotFoundException();
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java
index 860a96e..335ac79 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/Module.java
@@ -37,9 +37,13 @@
     get(ACCOUNT_KIND, "name").to(GetName.class);
     put(ACCOUNT_KIND, "name").to(PutName.class);
     delete(ACCOUNT_KIND, "name").to(PutName.class);
+    get(ACCOUNT_KIND, "active").to(GetActive.class);
+    put(ACCOUNT_KIND, "active").to(PutActive.class);
+    delete(ACCOUNT_KIND, "active").to(DeleteActive.class);
     child(ACCOUNT_KIND, "emails").to(Emails.class);
     get(EMAIL_KIND).to(GetEmail.class);
     put(EMAIL_KIND).to(PutEmail.class);
+    delete(EMAIL_KIND).to(DeleteEmail.class);
     put(EMAIL_KIND, "preferred").to(PutPreferred.class);
     get(ACCOUNT_KIND, "avatar").to(GetAvatar.class);
     get(ACCOUNT_KIND, "avatar.change.url").to(GetAvatarChangeUrl.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PutActive.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutActive.java
new file mode 100644
index 0000000..18a1329
--- /dev/null
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PutActive.java
@@ -0,0 +1,60 @@
+// Copyright (C) 2013 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package com.google.gerrit.server.account;
+
+import com.google.gerrit.common.data.GlobalCapability;
+import com.google.gerrit.extensions.annotations.RequiresCapability;
+import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
+import com.google.gerrit.extensions.restapi.Response;
+import com.google.gerrit.extensions.restapi.RestModifyView;
+import com.google.gerrit.reviewdb.client.Account;
+import com.google.gerrit.reviewdb.server.ReviewDb;
+import com.google.gerrit.server.account.PutActive.Input;
+import com.google.gwtorm.server.OrmException;
+import com.google.inject.Inject;
+import com.google.inject.Provider;
+
+import java.util.Collections;
+
+@RequiresCapability(GlobalCapability.ADMINISTRATE_SERVER)
+public class PutActive implements RestModifyView<AccountResource, Input> {
+  static class Input {
+  }
+
+  private final Provider<ReviewDb> dbProvider;
+  private final AccountCache byIdCache;
+
+  @Inject
+  PutActive(Provider<ReviewDb> dbProvider, AccountCache byIdCache) {
+    this.dbProvider = dbProvider;
+    this.byIdCache = byIdCache;
+  }
+
+  @Override
+  public Object apply(AccountResource rsrc, Input input)
+      throws ResourceNotFoundException, OrmException {
+    Account a = dbProvider.get().accounts().get(rsrc.getUser().getAccountId());
+    if (a == null) {
+      throw new ResourceNotFoundException("account not found");
+    }
+    if (a.isActive()) {
+      return Response.ok("");
+    }
+    a.setActive(true);
+    dbProvider.get().accounts().update(Collections.singleton(a));
+    byIdCache.evict(a.getId());
+    return Response.created("");
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java
index afe3f74..8a169b8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeInserter.java
@@ -14,6 +14,8 @@
 
 package com.google.gerrit.server.change;
 
+import static com.google.gerrit.reviewdb.client.Change.INITIAL_PATCH_SET_ID;
+
 import com.google.gerrit.common.ChangeHooks;
 import com.google.gerrit.common.data.LabelTypes;
 import com.google.gerrit.reviewdb.client.Account;
@@ -21,13 +23,18 @@
 import com.google.gerrit.reviewdb.client.ChangeMessage;
 import com.google.gerrit.reviewdb.client.PatchSet;
 import com.google.gerrit.reviewdb.client.PatchSetInfo;
+import com.google.gerrit.reviewdb.client.RevId;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ApprovalsUtil;
 import com.google.gerrit.server.ChangeUtil;
 import com.google.gerrit.server.config.TrackingFooters;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
+import com.google.gerrit.server.patch.PatchSetInfoFactory;
+import com.google.gerrit.server.project.RefControl;
 import com.google.gwtorm.server.OrmException;
 import com.google.inject.Inject;
+import com.google.inject.Provider;
+import com.google.inject.assistedinject.Assisted;
 
 import org.eclipse.jgit.lib.ObjectId;
 import org.eclipse.jgit.revwalk.RevCommit;
@@ -36,40 +43,95 @@
 import java.util.Set;
 
 public class ChangeInserter {
+  public static interface Factory {
+    ChangeInserter create(RefControl ctl, Change c, RevCommit rc);
+  }
+
+  private final Provider<ReviewDb> dbProvider;
   private final GitReferenceUpdated gitRefUpdated;
   private final ChangeHooks hooks;
   private final ApprovalsUtil approvalsUtil;
   private final TrackingFooters trackingFooters;
 
+  private final RefControl refControl;
+  private final Change change;
+  private final PatchSet patchSet;
+  private final RevCommit commit;
+  private final PatchSetInfo patchSetInfo;
+
+  private ChangeMessage changeMessage;
+  private Set<Account.Id> reviewers;
+  private boolean draft;
+
   @Inject
-  public ChangeInserter(final GitReferenceUpdated gitRefUpdated,
-      ChangeHooks hooks, ApprovalsUtil approvalsUtil,
-      TrackingFooters trackingFooters) {
+  ChangeInserter(Provider<ReviewDb> dbProvider,
+      PatchSetInfoFactory patchSetInfoFactory,
+      GitReferenceUpdated gitRefUpdated,
+      ChangeHooks hooks,
+      ApprovalsUtil approvalsUtil,
+      TrackingFooters trackingFooters,
+      @Assisted RefControl refControl,
+      @Assisted Change change,
+      @Assisted RevCommit commit) {
+    this.dbProvider = dbProvider;
     this.gitRefUpdated = gitRefUpdated;
     this.hooks = hooks;
     this.approvalsUtil = approvalsUtil;
     this.trackingFooters = trackingFooters;
+    this.refControl = refControl;
+    this.change = change;
+    this.commit = commit;
+
+    this.reviewers = Collections.emptySet();
+
+    patchSet =
+        new PatchSet(new PatchSet.Id(change.getId(), INITIAL_PATCH_SET_ID));
+    patchSet.setCreatedOn(change.getCreatedOn());
+    patchSet.setUploader(change.getOwner());
+    patchSet.setRevision(new RevId(commit.name()));
+    if (draft) {
+      change.setStatus(Change.Status.DRAFT);
+      patchSet.setDraft(true);
+    }
+    patchSetInfo = patchSetInfoFactory.get(commit, patchSet.getId());
+    change.setCurrentPatchSet(patchSetInfo);
+    ChangeUtil.computeSortKey(change);
   }
 
-  public void insertChange(ReviewDb db, Change change, PatchSet ps,
-      RevCommit commit, LabelTypes labelTypes, PatchSetInfo info,
-      Set<Account.Id> reviewers) throws OrmException {
-    insertChange(db, change, null, ps, commit, labelTypes, info, reviewers);
+  public ChangeInserter setMessage(ChangeMessage changeMessage) {
+    this.changeMessage = changeMessage;
+    return this;
   }
 
-  public void insertChange(ReviewDb db, Change change,
-      ChangeMessage changeMessage, PatchSet ps, RevCommit commit,
-      LabelTypes labelTypes, PatchSetInfo info, Set<Account.Id> reviewers)
-      throws OrmException {
+  public ChangeInserter setReviewers(Set<Account.Id> reviewers) {
+    this.reviewers = reviewers;
+    return this;
+  }
 
+  public ChangeInserter setDraft(boolean draft) {
+    this.draft = draft;
+    return this;
+  }
+
+  public PatchSet getPatchSet() {
+    return patchSet;
+  }
+
+  public PatchSetInfo getPatchSetInfo() {
+    return patchSetInfo;
+  }
+
+  public void insert() throws OrmException {
+    ReviewDb db = dbProvider.get();
     db.changes().beginTransaction(change.getId());
     try {
-      ChangeUtil.insertAncestors(db, ps.getId(), commit);
-      db.patchSets().insert(Collections.singleton(ps));
+      ChangeUtil.insertAncestors(db, patchSet.getId(), commit);
+      db.patchSets().insert(Collections.singleton(patchSet));
       db.changes().insert(Collections.singleton(change));
       ChangeUtil.updateTrackingIds(db, change, trackingFooters, commit.getFooterLines());
-      approvalsUtil.addReviewers(db, labelTypes, change, ps, info, reviewers,
-          Collections.<Account.Id> emptySet());
+      LabelTypes labelTypes = refControl.getProjectControl().getLabelTypes();
+      approvalsUtil.addReviewers(db, labelTypes, change, patchSet, patchSetInfo,
+          reviewers, Collections.<Account.Id> emptySet());
       db.commit();
     } finally {
       db.rollback();
@@ -78,8 +140,8 @@
       db.changeMessages().insert(Collections.singleton(changeMessage));
     }
 
-    gitRefUpdated.fire(change.getProject(), ps.getRefName(), ObjectId.zeroId(),
-        commit);
-    hooks.doPatchsetCreatedHook(change, ps, db);
+    gitRefUpdated.fire(change.getProject(), patchSet.getRefName(),
+        ObjectId.zeroId(), commit);
+    hooks.doPatchsetCreatedHook(change, patchSet, db);
   }
 }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java
index 3c5e83d..945118c 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/CherryPickChange.java
@@ -14,16 +14,12 @@
 
 package com.google.gerrit.server.change;
 
-import com.google.gerrit.common.data.LabelTypes;
 import com.google.gerrit.common.errors.EmailException;
-import com.google.gerrit.reviewdb.client.Account;
 import com.google.gerrit.reviewdb.client.Branch;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.client.ChangeMessage;
 import com.google.gerrit.reviewdb.client.PatchSet;
-import com.google.gerrit.reviewdb.client.PatchSetInfo;
 import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RevId;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.ChangeUtil;
 import com.google.gerrit.server.GerritPersonIdent;
@@ -59,8 +55,6 @@
 import org.eclipse.jgit.util.ChangeIdUtil;
 
 import java.io.IOException;
-import java.sql.Timestamp;
-import java.util.Collections;
 import java.util.List;
 
 public class CherryPickChange {
@@ -73,7 +67,7 @@
   private final PersonIdent myIdent;
   private final IdentifiedUser currentUser;
   private final CommitValidators.Factory commitValidatorsFactory;
-  private final ChangeInserter changeInserter;
+  private final ChangeInserter.Factory changeInserterFactory;
   private final PatchSetInserter.Factory patchSetInserterFactory;
   final MergeUtil.Factory mergeUtilFactory;
 
@@ -82,7 +76,7 @@
       final ReviewDb db, @GerritPersonIdent final PersonIdent myIdent,
       final GitRepositoryManager gitManager, final IdentifiedUser currentUser,
       final CommitValidators.Factory commitValidatorsFactory,
-      final ChangeInserter changeInserter,
+      final ChangeInserter.Factory changeInserterFactory,
       final PatchSetInserter.Factory patchSetInserterFactory,
       final MergeUtil.Factory mergeUtilFactory) {
     this.patchSetInfoFactory = patchSetInfoFactory;
@@ -91,7 +85,7 @@
     this.myIdent = myIdent;
     this.currentUser = currentUser;
     this.commitValidatorsFactory = commitValidatorsFactory;
-    this.changeInserter = changeInserter;
+    this.changeInserterFactory = changeInserterFactory;
     this.patchSetInserterFactory = patchSetInserterFactory;
     this.mergeUtilFactory = mergeUtilFactory;
   }
@@ -217,11 +211,9 @@
         new Change(changeKey, new Change.Id(db.nextChangeId()),
             currentUser.getAccountId(), new Branch.NameKey(project,
                 destRef.getName()));
-    PatchSet.Id id = new PatchSet.Id(change.getId(), Change.INITIAL_PATCH_SET_ID);
-    PatchSet newPatchSet = new PatchSet(id);
-    newPatchSet.setCreatedOn(new Timestamp(System.currentTimeMillis()));
-    newPatchSet.setUploader(change.getOwner());
-    newPatchSet.setRevision(new RevId(cherryPickCommit.name()));
+    ChangeInserter ins =
+        changeInserterFactory.create(refControl, change, cherryPickCommit);
+    PatchSet newPatchSet = ins.getPatchSet();
 
     CommitValidators commitValidators =
         commitValidatorsFactory.create(refControl, new NoSshInfo(), git);
@@ -247,18 +239,7 @@
           change.getDest().getParentKey().get(), ru.getResult()));
     }
 
-    LabelTypes labelTypes =
-        refControl.getProjectControl().getLabelTypes();
-
-    PatchSetInfo newPatchSetInfo =
-        patchSetInfoFactory.get(cherryPickCommit, newPatchSet.getId());
-    change.setCurrentPatchSet(newPatchSetInfo);
-    ChangeUtil.updated(change);
-
-    changeInserter.insertChange(db, change,
-        buildChangeMessage(patchSetId, change), newPatchSet,
-        cherryPickCommit, labelTypes, newPatchSetInfo,
-        Collections.<Account.Id> emptySet());
+    ins.setMessage(buildChangeMessage(patchSetId, change)).insert();
 
     return change.getId();
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
index 146e58c..72c07b6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Module.java
@@ -93,6 +93,7 @@
         factory(ReviewerResource.Factory.class);
         factory(AccountInfo.Loader.Factory.class);
         factory(EmailReviewComments.Factory.class);
+        factory(ChangeInserter.Factory.class);
         factory(PatchSetInserter.Factory.class);
       }
     });
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/Revert.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/Revert.java
index 154bd64..a2f0a9b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/change/Revert.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/Revert.java
@@ -49,7 +49,7 @@
   private final GitRepositoryManager gitManager;
   private final PersonIdent myIdent;
   private final PatchSetInfoFactory patchSetInfoFactory;
-  private final ChangeInserter changeInserter;
+  private final ChangeInserter.Factory changeInserterFactory;
 
   public static class Input {
     public String message;
@@ -64,7 +64,7 @@
       GitRepositoryManager gitManager,
       final PatchSetInfoFactory patchSetInfoFactory,
       @GerritPersonIdent final PersonIdent myIdent,
-      final ChangeInserter changeInserter) {
+      final ChangeInserter.Factory changeInserterFactory) {
     this.hooks = hooks;
     this.revertedSenderFactory = revertedSenderFactory;
     this.commitValidatorsFactory = commitValidatorsFactory;
@@ -72,7 +72,7 @@
     this.json = json;
     this.gitManager = gitManager;
     this.myIdent = myIdent;
-    this.changeInserter = changeInserter;
+    this.changeInserterFactory = changeInserterFactory;
     this.patchSetInfoFactory = patchSetInfoFactory;
   }
 
@@ -97,7 +97,7 @@
               commitValidators,
               Strings.emptyToNull(input.message), dbProvider.get(),
               revertedSenderFactory, hooks, git, patchSetInfoFactory,
-              myIdent, changeInserter);
+              myIdent, changeInserterFactory);
 
       return json.format(revertedChangeId);
     } catch (InvalidChangeOperationException e) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
index 2b8521b..9370a89 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -65,7 +65,6 @@
 import com.google.gerrit.server.auth.ldap.LdapModule;
 import com.google.gerrit.server.avatar.AvatarProvider;
 import com.google.gerrit.server.cache.CacheRemovalListener;
-import com.google.gerrit.server.change.ChangeInserter;
 import com.google.gerrit.server.events.EventFactory;
 import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
 import com.google.gerrit.server.git.ChangeCache;
@@ -222,7 +221,6 @@
     bind(TransferConfig.class);
 
     bind(ApprovalsUtil.class);
-    bind(ChangeInserter.class);
     bind(ChangeMergeQueue.class).in(SINGLETON);
     bind(MergeQueue.class).to(ChangeMergeQueue.class).in(SINGLETON);
     factory(ReloadSubmitQueueOp.Factory.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
index 2177954..482a211 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -14,10 +14,10 @@
 
 package com.google.gerrit.server.git;
 
-import static com.google.gerrit.reviewdb.client.Change.INITIAL_PATCH_SET_ID;
 import static com.google.gerrit.server.git.MultiProgressMonitor.UNKNOWN;
 import static com.google.gerrit.server.mail.MailUtil.getRecipientsFromApprovals;
 import static com.google.gerrit.server.mail.MailUtil.getRecipientsFromFooters;
+
 import static org.eclipse.jgit.lib.Constants.R_HEADS;
 import static org.eclipse.jgit.transport.ReceiveCommand.Result.NOT_ATTEMPTED;
 import static org.eclipse.jgit.transport.ReceiveCommand.Result.OK;
@@ -259,7 +259,7 @@
   private final CommitValidators.Factory commitValidatorsFactory;
   private final TrackingFooters trackingFooters;
   private final TagCache tagCache;
-  private final ChangeInserter changeInserter;
+  private final ChangeInserter.Factory changeInserterFactory;
   private final WorkQueue workQueue;
   private final ListeningExecutorService changeUpdateExector;
   private final RequestScopePropagator requestScopePropagator;
@@ -315,7 +315,7 @@
       final GitRepositoryManager repoManager,
       final TagCache tagCache,
       final ChangeCache changeCache,
-      final ChangeInserter changeInserter,
+      final ChangeInserter.Factory changeInserterFactory,
       final CommitValidators.Factory commitValidatorsFactory,
       @CanonicalWebUrl @Nullable final String canonicalWebUrl,
       @GerritPersonIdent final PersonIdent gerritIdent,
@@ -348,7 +348,7 @@
     this.canonicalWebUrl = canonicalWebUrl;
     this.trackingFooters = trackingFooters;
     this.tagCache = tagCache;
-    this.changeInserter = changeInserter;
+    this.changeInserterFactory = changeInserterFactory;
     this.commitValidatorsFactory = commitValidatorsFactory;
     this.workQueue = workQueue;
     this.changeUpdateExector = changeUpdateExector;
@@ -1342,7 +1342,7 @@
         Change.Key changeKey = new Change.Key("I" + c.name());
         final List<String> idList = c.getFooterLines(CHANGE_ID);
         if (idList.isEmpty()) {
-          newChanges.add(new CreateRequest(c, changeKey));
+          newChanges.add(new CreateRequest(magicBranch.ctl, c, changeKey));
           continue;
         }
 
@@ -1392,7 +1392,7 @@
 
           newChangeIds.add(p.changeKey);
         }
-        newChanges.add(new CreateRequest(p.commit, p.changeKey));
+        newChanges.add(new CreateRequest(magicBranch.ctl, p.commit, p.changeKey));
       }
     } catch (IOException e) {
       // Should never happen, the core receive process would have
@@ -1458,34 +1458,21 @@
   private class CreateRequest {
     final RevCommit commit;
     final Change change;
-    final PatchSet ps;
     final ReceiveCommand cmd;
-    private final PatchSetInfo info;
+    final ChangeInserter ins;
     boolean created;
 
-    CreateRequest(RevCommit c, Change.Key changeKey) throws OrmException {
+    CreateRequest(RefControl ctl, RevCommit c, Change.Key changeKey)
+        throws OrmException {
       commit = c;
-
       change = new Change(changeKey,
           new Change.Id(db.nextChangeId()),
           currentUser.getAccountId(),
           magicBranch.dest);
       change.setTopic(magicBranch.topic);
-
-      ps = new PatchSet(new PatchSet.Id(change.getId(), INITIAL_PATCH_SET_ID));
-      ps.setCreatedOn(change.getCreatedOn());
-      ps.setUploader(change.getOwner());
-      ps.setRevision(toRevId(c));
-
-      if (magicBranch.isDraft()) {
-        change.setStatus(Change.Status.DRAFT);
-        ps.setDraft(true);
-      }
-
-      info = patchSetInfoFactory.get(c, ps.getId());
-      change.setCurrentPatchSet(info);
-      ChangeUtil.updated(change);
-      cmd = new ReceiveCommand(ObjectId.zeroId(), c, ps.getRefName());
+      ins = changeInserterFactory.create(ctl, change, c);
+      cmd = new ReceiveCommand(ObjectId.zeroId(), c,
+          ins.getPatchSet().getRefName());
     }
 
     CheckedFuture<Void, OrmException> insertChange() throws IOException {
@@ -1516,6 +1503,7 @@
     }
 
     private void insertChange(ReviewDb db) throws OrmException {
+      final PatchSet ps = ins.getPatchSet();
       final Account.Id me = currentUser.getAccountId();
       final List<FooterLine> footerLines = commit.getFooterLines();
       final MailRecipients recipients = new MailRecipients();
@@ -1525,9 +1513,7 @@
       recipients.add(getRecipientsFromFooters(accountResolver, ps, footerLines));
       recipients.remove(me);
 
-      changeInserter.insertChange(db, change, ps, commit, labelTypes, info,
-          recipients.getReviewers());
-
+      ins.setReviewers(recipients.getReviewers()).insert();
       created = true;
 
       workQueue.getDefaultQueue()
@@ -1538,7 +1524,7 @@
             CreateChangeSender cm =
                 createChangeSenderFactory.create(change);
             cm.setFrom(me);
-            cm.setPatchSet(ps, info);
+            cm.setPatchSet(ps, ins.getPatchSetInfo());
             cm.addReviewers(recipients.getReviewers());
             cm.addExtraCC(recipients.getCcOnly());
             cm.send();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java
index b16ab2a..716a22b 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/CommentSender.java
@@ -14,6 +14,7 @@
 
 package com.google.gerrit.server.mail;
 
+import com.google.common.collect.Ordering;
 import com.google.gerrit.common.errors.EmailException;
 import com.google.gerrit.reviewdb.client.AccountProjectWatch.NotifyType;
 import com.google.gerrit.reviewdb.client.Change;
@@ -29,7 +30,6 @@
 import org.eclipse.jgit.lib.Repository;
 
 import java.io.IOException;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
@@ -62,9 +62,7 @@
         paths.add(p.getFileName());
       }
     }
-    String[] names = paths.toArray(new String[paths.size()]);
-    Arrays.sort(names);
-    changeData.setCurrentFilePaths(names);
+    changeData.setCurrentFilePaths(Ordering.natural().sortedCopy(paths));
   }
 
   @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
index bf3b605..d1fe4ec 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java
@@ -51,7 +51,6 @@
 import java.io.IOException;
 import java.sql.Timestamp;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -141,7 +140,7 @@
   private ListMultimap<PatchSet.Id, PatchSetApproval> limitedApprovals;
   private ListMultimap<PatchSet.Id, PatchSetApproval> allApprovals;
   private List<PatchSetApproval> currentApprovals;
-  private String[] currentFiles;
+  private List<String> currentFiles;
   private Collection<PatchLineComment> comments;
   private Collection<TrackingId> trackingIds;
   private CurrentUser visibleTo;
@@ -180,11 +179,11 @@
     return limitedIds;
   }
 
-  public void setCurrentFilePaths(String[] filePaths) {
+  public void setCurrentFilePaths(List<String> filePaths) {
     currentFiles = filePaths;
   }
 
-  public String[] currentFilePaths(Provider<ReviewDb> db,
+  public List<String> currentFilePaths(Provider<ReviewDb> db,
       PatchListCache cache) throws OrmException {
     if (currentFiles == null) {
       Change c = change(db);
@@ -200,7 +199,7 @@
       try {
         p = cache.get(c, ps);
       } catch (PatchListNotAvailableException e) {
-        currentFiles = new String[0];
+        currentFiles = Collections.emptyList();
         return currentFiles;
       }
 
@@ -226,8 +225,8 @@
             break;
         }
       }
-      currentFiles = r.toArray(new String[r.size()]);
-      Arrays.sort(currentFiles);
+      Collections.sort(r);
+      currentFiles = Collections.unmodifiableList(r);
     }
     return currentFiles;
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java
index 6e9e79c..cb64801 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeStatusPredicate.java
@@ -14,6 +14,9 @@
 
 package com.google.gerrit.server.query.change;
 
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.collect.ImmutableBiMap;
 import com.google.gerrit.reviewdb.client.Change;
 import com.google.gerrit.reviewdb.server.ReviewDb;
 import com.google.gerrit.server.query.OperatorPredicate;
@@ -22,11 +25,7 @@
 import com.google.inject.Provider;
 
 import java.util.ArrayList;
-import java.util.EnumMap;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
-
 
 /**
  * Predicate for a {@link Change.Status}.
@@ -36,17 +35,15 @@
  * searching for changes. Either operator name has the same meaning.
  */
 final class ChangeStatusPredicate extends OperatorPredicate<ChangeData> {
-  private static final Map<String, Change.Status> byName;
-  private static final EnumMap<Change.Status, String> byEnum;
+  private static final ImmutableBiMap<Change.Status, String> VALUES;
 
   static {
-    byName = new HashMap<String, Change.Status>();
-    byEnum = new EnumMap<Change.Status, String>(Change.Status.class);
-    for (final Change.Status s : Change.Status.values()) {
-      final String name = s.name().toLowerCase();
-      byName.put(name, s);
-      byEnum.put(s, name);
+    ImmutableBiMap.Builder<Change.Status, String> values =
+        ImmutableBiMap.builder();
+    for (Change.Status s : Change.Status.values()) {
+      values.put(s, s.name().toLowerCase());
     }
+    VALUES = values.build();
   }
 
   static Predicate<ChangeData> open(Provider<ReviewDb> dbProvider) {
@@ -69,23 +66,18 @@
     return r.size() == 1 ? r.get(0) : or(r);
   }
 
-  private static Change.Status parse(final String value) {
-    final Change.Status s = byName.get(value);
-    if (s == null) {
-      throw new IllegalArgumentException();
-    }
-    return s;
-  }
-
   private final Provider<ReviewDb> dbProvider;
   private final Change.Status status;
 
   ChangeStatusPredicate(Provider<ReviewDb> dbProvider, String value) {
-    this(dbProvider, parse(value));
+    super(ChangeQueryBuilder.FIELD_STATUS, value);
+    this.dbProvider = dbProvider;
+    status = VALUES.inverse().get(value);
+    checkArgument(status != null, "invalid change status: %s", value);
   }
 
   ChangeStatusPredicate(Provider<ReviewDb> dbProvider, Change.Status status) {
-    super(ChangeQueryBuilder.FIELD_STATUS, byEnum.get(status));
+    super(ChangeQueryBuilder.FIELD_STATUS, VALUES.get(status));
     this.dbProvider = dbProvider;
     this.status = status;
   }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RegexFilePredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RegexFilePredicate.java
index 11856e4..e642860 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RegexFilePredicate.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/RegexFilePredicate.java
@@ -24,7 +24,8 @@
 import dk.brics.automaton.RegExp;
 import dk.brics.automaton.RunAutomaton;
 
-import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 
 class RegexFilePredicate extends OperatorPredicate<ChangeData> {
   private final Provider<ReviewDb> db;
@@ -67,7 +68,7 @@
 
   @Override
   public boolean match(ChangeData object) throws OrmException {
-    String[] files = object.currentFilePaths(db, cache);
+    List<String> files = object.currentFilePaths(db, cache);
     if (files != null) {
       int begin, end;
 
@@ -76,7 +77,7 @@
         end = find(files, prefixEnd);
       } else {
         begin = 0;
-        end = files.length;
+        end = files.size();
       }
 
       if (prefixOnly) {
@@ -84,7 +85,7 @@
       }
 
       while (begin < end) {
-        if (pattern.run(files[begin++])) {
+        if (pattern.run(files.get(begin++))) {
           return true;
         }
       }
@@ -100,8 +101,8 @@
     }
   }
 
-  private static int find(String[] files, String p) {
-    int r = Arrays.binarySearch(files, p);
+  private static int find(List<String> files, String p) {
+    int r = Collections.binarySearch(files, p);
     return r < 0 ? -(r + 1) : r;
   }
 
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/RegexFilePredicateTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/RegexFilePredicateTest.java
index ce7b25c..1500272 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/RegexFilePredicateTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/RegexFilePredicateTest.java
@@ -76,7 +76,7 @@
   private static ChangeData change(String... files) {
     Arrays.sort(files);
     ChangeData cd = new ChangeData(new Change.Id(1));
-    cd.setCurrentFilePaths(files);
+    cd.setCurrentFilePaths(Arrays.asList(files));
     return cd;
   }
 }
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
index 4c23833..8d24757 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/SetAccountCommand.java
@@ -260,6 +260,8 @@
       manager.unlink(id, AuthRequest.forEmail(mailAddress));
     } catch (AccountException ex) {
       throw die(ex.getMessage());
+    } catch (OrmException ex) {
+      throw die(ex.getMessage());
     }
   }
 
diff --git a/lib/codemirror/BUCK b/lib/codemirror/BUCK
index 5c64335..cd2804b 100644
--- a/lib/codemirror/BUCK
+++ b/lib/codemirror/BUCK
@@ -2,10 +2,17 @@
 SHA1 = '7a83ae686d75afd30bb152d7683f2dc27e59ea82'
 URL = 'http://codemirror.net/codemirror-%s.zip' % VERSION
 
+prebuilt_jar(
+  name = 'codemirror',
+  binary_jar = genfile('codemirror.jar'),
+  deps = [':codemirror__jar'],
+  visibility = ['PUBLIC'],
+)
+
 # TODO(sop) Repackage by license boundaries.
 # TODO(sop) Minify with Closure JS compiler.
 genrule(
-  name = 'codemirror',
+  name = 'codemirror__jar',
   cmd = ';'.join([
     'cd $TMP',
     'mkdir net META-INF',
@@ -20,7 +27,6 @@
     '//lib:LICENSE-codemirror',
   ],
   out = 'codemirror.jar',
-  visibility = ['PUBLIC'],
 )
 
 genrule(
diff --git a/plugins/helloworld b/plugins/helloworld
new file mode 160000
index 0000000..d3c7dcc
--- /dev/null
+++ b/plugins/helloworld
@@ -0,0 +1 @@
+Subproject commit d3c7dccd0824d1500976a0cd9077c1b878564bba