Merge "PutMessage: automatically add Change-Id if requireChangeId is set"
diff --git a/Documentation/dev-plugins.txt b/Documentation/dev-plugins.txt
index c8ea869..e292549 100644
--- a/Documentation/dev-plugins.txt
+++ b/Documentation/dev-plugins.txt
@@ -2451,40 +2451,6 @@
 Disabled plugins can be re-enabled using the
 link:cmd-plugin-enable.html[plugin enable] command.
 
-== Known issues and bugs
-
-=== Error handling in UI when using the REST API
-
-When a plugin invokes a REST endpoint in the UI, it provides an
-`AsyncCallback` to handle the result. At the moment the
-`onFailure(Throwable)` of the callback is never invoked, even if there
-is an error. Errors are always handled by the Gerrit core UI which
-shows the error dialog. This means currently plugins cannot do any
-error handling and e.g. ignore expected errors.
-
-In the following example the REST endpoint would return '404 Not
-Found' if the user has no username and the Gerrit core UI would
-display an error dialog for this. However having no username is
-not an error and the plugin may like to handle this case.
-
-[source,java]
-----
-new RestApi("accounts").id("self").view("username")
-    .get(new AsyncCallback<NativeString>() {
-
-  @Override
-  public void onSuccess(NativeString username) {
-    // TODO
-  }
-
-  @Override
-  public void onFailure(Throwable caught) {
-    // never invoked
-  }
-});
-----
-
-
 [[reviewer-suggestion]]
 == Reviewer Suggestion Plugins
 
diff --git a/Documentation/user-inline-edit.txt b/Documentation/user-inline-edit.txt
index f64c449..1f5e195 100644
--- a/Documentation/user-inline-edit.txt
+++ b/Documentation/user-inline-edit.txt
@@ -72,7 +72,7 @@
 
 To add a file to the change:
 
-. In the top left corner of the change, click Edit.
+. In the top right corner of the change, click Edit.
 . Next to Files, click Open:
 
 +
diff --git a/java/com/google/gerrit/extensions/api/changes/HashtagsInput.java b/java/com/google/gerrit/extensions/api/changes/HashtagsInput.java
index 8f66f12..bbc8a2e 100644
--- a/java/com/google/gerrit/extensions/api/changes/HashtagsInput.java
+++ b/java/com/google/gerrit/extensions/api/changes/HashtagsInput.java
@@ -26,4 +26,9 @@
   public HashtagsInput(Set<String> add) {
     this.add = add;
   }
+
+  public HashtagsInput(Set<String> add, Set<String> remove) {
+    this(add);
+    this.remove = remove;
+  }
 }
diff --git a/java/com/google/gerrit/server/git/receive/ReplaceOp.java b/java/com/google/gerrit/server/git/receive/ReplaceOp.java
index 5e1f178..a9a49bf 100644
--- a/java/com/google/gerrit/server/git/receive/ReplaceOp.java
+++ b/java/com/google/gerrit/server/git/receive/ReplaceOp.java
@@ -481,11 +481,7 @@
     change.setCurrentPatchSet(info);
 
     List<String> idList = commit.getFooterLines(CHANGE_ID);
-    if (idList.isEmpty()) {
-      change.setKey(Change.key("I" + commitId.name()));
-    } else {
-      change.setKey(Change.key(idList.get(idList.size() - 1).trim()));
-    }
+    change.setKey(Change.key(idList.get(idList.size() - 1).trim()));
   }
 
   private List<Comment> publishComments(ChangeContext ctx, boolean workInProgress) {
diff --git a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.js b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.js
index 1c408df..64e156b 100644
--- a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.js
+++ b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.js
@@ -37,7 +37,6 @@
 
   Polymer({
     is: 'gr-permission',
-    _legacyUndefinedCheck: true,
 
     properties: {
       labels: Object,
@@ -178,7 +177,8 @@
     },
 
     _computeLabel(permission, labels) {
-      if (!permission.value.label) { return; }
+      if (!labels || !permission ||
+          !permission.value || !permission.value.label) { return; }
 
       const labelName = permission.value.label;
 
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js
index 7d2890a..7d47d08 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js
+++ b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access.js
@@ -237,7 +237,7 @@
     },
 
     _computeWebLinkClass(weblinks) {
-      return weblinks.length ? 'show' : '';
+      return weblinks && weblinks.length ? 'show' : '';
     },
 
     _computeShowInherit(inheritsFrom) {
@@ -446,12 +446,12 @@
     },
 
     _computeSaveBtnClass(ownerOf) {
-      return ownerOf.length < 0 ? 'invisible' : '';
+      return ownerOf && ownerOf.length === 0 ? 'invisible' : '';
     },
 
     _computeMainClass(ownerOf, canUpload, editing) {
       const classList = [];
-      if (ownerOf.length > 0 || canUpload) {
+      if (ownerOf && ownerOf.length > 0 || canUpload) {
         classList.push('admin');
       }
       if (editing) {
diff --git a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js
index 4102747..4b01b18 100644
--- a/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js
+++ b/polygerrit-ui/app/elements/admin/gr-rule-editor/gr-rule-editor.js
@@ -65,7 +65,6 @@
 
   Polymer({
     is: 'gr-rule-editor',
-    _legacyUndefinedCheck: true,
 
     properties: {
       hasRange: Boolean,
diff --git a/polygerrit-ui/app/elements/settings/gr-identities/gr-identities.html b/polygerrit-ui/app/elements/settings/gr-identities/gr-identities.html
index be851b1..a876611 100644
--- a/polygerrit-ui/app/elements/settings/gr-identities/gr-identities.html
+++ b/polygerrit-ui/app/elements/settings/gr-identities/gr-identities.html
@@ -27,8 +27,20 @@
   <template>
     <style include="shared-styles"></style>
     <style include="gr-form-styles">
-      td {
-        width: 5em;
+      tr th.emailAddressHeader,
+      tr th.identityHeader {
+        width: 15em;
+        padding: 0 10px;
+      }
+      tr td.statusColumn,
+      tr td.emailAddressColumn,
+      tr td.identityColumn {
+        word-break: break-word;
+      }
+      tr td.emailAddressColumn,
+      tr td.identityColumn {
+        padding: 4px 10px;
+        width: 15em;
       }
       .deleteButton {
         float: right;
@@ -36,40 +48,38 @@
       .deleteButton:not(.show) {
         display: none;
       }
-      .statusColumn {
-        white-space: nowrap;
-      }
     </style>
     <div class="gr-form-styles">
-      <table id="identities">
-        <thead>
-          <tr>
-            <th class="statusHeader">Status</th>
-            <th class="emailAddressHeader">Email Address</th>
-            <th class="identityHeader">Identity</th>
-            <th class="deleteHeader"></th>
-          </tr>
-        </thead>
-        <tbody>
-          <template is="dom-repeat" items="[[_identities]]" filter="filterIdentities">
+      <fieldset>
+        <table>
+          <thead>
             <tr>
-              <td class$="statusColumn">
-                [[_computeIsTrusted(item.trusted)]]
-              </td>
-              <td>[[item.email_address]]</td>
-              <td>[[_computeIdentity(item.identity)]]</td>
-              <td>
-                <gr-button
-                    link
-                    class$="deleteButton [[_computeHideDeleteClass(item.can_delete)]]"
-                    on-tap="_handleDeleteItem">
-                  Delete
-                </gr-button>
-              </td>
+              <th class="statusHeader">Status</th>
+              <th class="emailAddressHeader">Email Address</th>
+              <th class="identityHeader">Identity</th>
+              <th class="deleteHeader"></th>
             </tr>
-          </template>
-        </tbody>
-      </table>
+          </thead>
+          <tbody>
+            <template is="dom-repeat" items="[[_identities]]" filter="filterIdentities">
+              <tr>
+                <td class="statusColumn">
+                  [[_computeIsTrusted(item.trusted)]]
+                </td>
+                <td class="emailAddressColumn">[[item.email_address]]</td>
+                <td class="identityColumn">[[_computeIdentity(item.identity)]]</td>
+                <td class="deleteColumn">
+                  <gr-button
+                      class$="deleteButton [[_computeHideDeleteClass(item.can_delete)]]"
+                      on-tap="_handleDeleteItem">
+                    Delete
+                  </gr-button>
+                </td>
+              </tr>
+            </template>
+          </tbody>
+        </table>
+      </fieldset>
     </div>
     <gr-overlay id="overlay" with-backdrop>
       <gr-confirm-delete-item-dialog
diff --git a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.html b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.html
index 5c9b66e..1daffa2 100644
--- a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.html
+++ b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.html
@@ -28,7 +28,7 @@
         display: inline-block;
         border-radius: 50%;
         background-size: cover;
-        background-color: var(--background-color, #f1f2f3);
+        background-color: var(--avatar-background-color, #f1f2f3);
       }
     </style>
     <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
diff --git a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
index 3937424..80c8480 100644
--- a/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
+++ b/polygerrit-ui/app/elements/shared/gr-rest-api-interface/gr-rest-api-interface.js
@@ -2773,7 +2773,7 @@
       });
     },
 
-    getCapabilities(token, opt_errFn) {
+    getCapabilities(opt_errFn) {
       return this._fetchJSON({
         url: '/config/server/capabilities',
         errFn: opt_errFn,