Merge "Make project state check in DELETE explicit"
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 952891c..fd8c3fe 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -4408,8 +4408,6 @@
 * `diffie-hellman-group1-sha1`
 
 By default, all supported key exchange algorithms are available.
-Without Bouncy Castle, `diffie-hellman-group1-sha1` is the only
-available algorithm.
 
 It is strongly recommended to disable at least `diffie-hellman-group1-sha1`
 as it's known to be vulnerable (logjam attack). Additionally, if your setup
diff --git a/Documentation/dev-bazel.txt b/Documentation/dev-bazel.txt
index fc71d26..b482de1 100644
--- a/Documentation/dev-bazel.txt
+++ b/Documentation/dev-bazel.txt
@@ -1,13 +1,14 @@
 = Gerrit Code Review - Building with Bazel
 
 [[installation]]
-== Installation
+== Prerequisites
 
-You need to use Java 8 and Node.js for building gerrit.
+To build Gerrit from source, you need:
 
-You can install Bazel from the bazel.io:
-https://www.bazel.io/versions/master/docs/install.html
-
+* A Linux or macOS system (Windows is not supported at this time)
+* A JDK for Java 8
+* Node.js
+* link:https://www.bazel.io/versions/master/docs/install.html[Bazel]
 
 [[build]]
 == Building on the Command Line
diff --git a/Documentation/install.txt b/Documentation/install.txt
index c6977a4..0f121a0 100644
--- a/Documentation/install.txt
+++ b/Documentation/install.txt
@@ -175,8 +175,8 @@
 [[installation_on_windows]]
 == Installation on Windows
 
-If new site is going to be initialized with Bouncy Castle cryptography,
-ssh-keygen command must be available during the init phase. If you have
+The `ssh-keygen` command must be available during the init phase to
+generate SSH host keys. If you have
 link:https://git-for-windows.github.io/[Git for Windows] installed,
 start Command Prompt and temporary add directory with ssh-keygen to the
 PATH environment variable just before running init command:
diff --git a/Documentation/intro-user.txt b/Documentation/intro-user.txt
index 6242fcf..b20c2c0 100644
--- a/Documentation/intro-user.txt
+++ b/Documentation/intro-user.txt
@@ -531,8 +531,10 @@
 [[private-changes]]
 == Private Changes
 
-Private changes are changes that are only visible to their owners and
-reviewers. Private changes are useful in a number of cases:
+Private changes are changes that are only visible to their owners, reviewers
+and users with the link:access-control.html#category_view_private_changes[
+View Private Changes] global capability. Private changes are useful in a number
+of cases:
 
 * You want to check what the change looks like before formal review starts.
   By marking the change private without reviewers, nobody can
diff --git a/WORKSPACE b/WORKSPACE
index ae6ee9e..c16200b 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -1,14 +1,17 @@
 workspace(name = "gerrit")
 
-load("//:version.bzl", "check_version")
-
-check_version("0.5.3")
-
 load("//tools/bzl:maven_jar.bzl", "maven_jar", "GERRIT", "MAVEN_LOCAL")
 load("//lib/codemirror:cm.bzl", "CM_VERSION", "DIFF_MATCH_PATCH_VERSION")
 load("//plugins:external_plugin_deps.bzl", "external_plugin_deps")
 
 http_archive(
+    name = "bazel_skylib",
+    sha256 = "bbccf674aa441c266df9894182d80de104cabd19be98be002f6d478aaa31574d",
+    strip_prefix = "bazel-skylib-2169ae1c374aab4a09aa90e65efe1a3aad4e279b",
+    urls = ["https://github.com/bazelbuild/bazel-skylib/archive/2169ae1c374aab4a09aa90e65efe1a3aad4e279b.tar.gz"],
+)
+
+http_archive(
     name = "io_bazel_rules_closure",
     sha256 = "25f5399f18d8bf9ce435f85c6bbf671ec4820bc4396b3022cc5dc4bc66303609",
     strip_prefix = "rules_closure-0.4.2",
@@ -24,6 +27,10 @@
     url = "https://raw.githubusercontent.com/google/closure-compiler/775609aad61e14aef289ebec4bfc09ad88877f9e/contrib/externs/polymer-1.0.js",
 )
 
+load("@bazel_skylib//:lib.bzl", "versions")
+
+versions.check(minimum_bazel_version = "0.7.0")
+
 load("@io_bazel_rules_closure//closure:defs.bzl", "closure_repositories")
 
 # Prevent redundant loading of dependencies.
@@ -278,6 +285,7 @@
     sha1 = "4b95f4897fa13f2cd904aee711aeafc0c5295cd8",
 )
 
+# When upgrading commons-compress, also upgrade tukaani-xz
 maven_jar(
     name = "commons_compress",
     artifact = "org.apache.commons:commons-compress:1.13",
@@ -418,10 +426,11 @@
     sha1 = "514df6a7c7938de35c7f68dc8b8f22df86037f38",
 )
 
+# Transitive dependency of commons-compress
 maven_jar(
     name = "tukaani_xz",
-    artifact = "org.tukaani:xz:1.6",
-    sha1 = "05b6f921f1810bdf90e25471968f741f87168b64",
+    artifact = "org.tukaani:xz:1.4",
+    sha1 = "18a9a2ce6abf32ea1b5fd31dae5210ad93f4e5e3",
 )
 
 # When upgrading Lucene, make sure it's compatible with Elasticsearch
diff --git a/java/com/google/gerrit/server/project/ProjectCache.java b/java/com/google/gerrit/server/project/ProjectCache.java
index 8283dce..9ebcc99 100644
--- a/java/com/google/gerrit/server/project/ProjectCache.java
+++ b/java/com/google/gerrit/server/project/ProjectCache.java
@@ -68,6 +68,12 @@
    */
   void remove(Project p) throws IOException;
 
+  /**
+   * Remove information about the given project from the cache. It will no longer be returned from
+   * {@link #all()}.
+   */
+  void remove(Project.NameKey name) throws IOException;
+
   /** @return sorted iteration of projects. */
   ImmutableSortedSet<Project.NameKey> all();
 
diff --git a/java/com/google/gerrit/server/project/ProjectCacheImpl.java b/java/com/google/gerrit/server/project/ProjectCacheImpl.java
index 4d20c03..68270e2 100644
--- a/java/com/google/gerrit/server/project/ProjectCacheImpl.java
+++ b/java/com/google/gerrit/server/project/ProjectCacheImpl.java
@@ -174,18 +174,22 @@
 
   @Override
   public void remove(Project p) throws IOException {
+    remove(p.getNameKey());
+  }
+
+  @Override
+  public void remove(Project.NameKey name) throws IOException {
     listLock.lock();
     try {
       list.put(
           ListKey.ALL,
-          ImmutableSortedSet.copyOf(
-              Sets.difference(list.get(ListKey.ALL), ImmutableSet.of(p.getNameKey()))));
+          ImmutableSortedSet.copyOf(Sets.difference(list.get(ListKey.ALL), ImmutableSet.of(name))));
     } catch (ExecutionException e) {
       log.warn("Cannot list available projects", e);
     } finally {
       listLock.unlock();
     }
-    evict(p);
+    evict(name);
   }
 
   @Override
diff --git a/javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java b/javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java
index 453a89f..c986c5e 100644
--- a/javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/project/ProjectIT.java
@@ -331,6 +331,24 @@
     gApi.projects().name(project.get()).head("test");
   }
 
+  @Test
+  public void nonActiveProjectCanBeMadeActive() throws Exception {
+    for (ProjectState nonActiveState :
+        ImmutableList.of(ProjectState.READ_ONLY, ProjectState.HIDDEN)) {
+      // ACTIVE => NON_ACTIVE
+      ConfigInput ci1 = new ConfigInput();
+      ci1.state = nonActiveState;
+      gApi.projects().name(project.get()).config(ci1);
+      assertThat(gApi.projects().name(project.get()).config().state).isEqualTo(nonActiveState);
+      // NON_ACTIVE => ACTIVE
+      ConfigInput ci2 = new ConfigInput();
+      ci2.state = ProjectState.ACTIVE;
+      gApi.projects().name(project.get()).config(ci2);
+      // ACTIVE is represented as null in the API
+      assertThat(gApi.projects().name(project.get()).config().state).isNull();
+    }
+  }
+
   private ConfigInput createTestConfigInput() {
     ConfigInput input = new ConfigInput();
     input.description = "some description";
diff --git a/javatests/com/google/gerrit/server/project/RefControlTest.java b/javatests/com/google/gerrit/server/project/RefControlTest.java
index f57a3d4..8892a50 100644
--- a/javatests/com/google/gerrit/server/project/RefControlTest.java
+++ b/javatests/com/google/gerrit/server/project/RefControlTest.java
@@ -237,6 +237,9 @@
           public void remove(Project p) {}
 
           @Override
+          public void remove(Project.NameKey name) {}
+
+          @Override
           public ImmutableSortedSet<Project.NameKey> all() {
             return ImmutableSortedSet.of();
           }
diff --git a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.html b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.html
index 98a1a61..4013b37 100644
--- a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.html
+++ b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.html
@@ -16,6 +16,7 @@
 
 <link rel="import" href="../../../bower_components/polymer/polymer.html">
 <link rel="import" href="../../../behaviors/gr-access-behavior/gr-access-behavior.html">
+<link rel="import" href="../../../bower_components/paper-toggle-button/paper-toggle-button.html">
 <link rel="import" href="../../../styles/gr-form-styles.html">
 <link rel="import" href="../../../styles/gr-menu-page-styles.html">
 <link rel="import" href="../../../styles/shared-styles.html">
@@ -52,8 +53,13 @@
       #removeBtn {
         display: none;
       }
+      .right {
+        display: flex;
+        align-items: center;
+      }
       .editing #removeBtn {
         display: block;
+        margin-left: 1.5em;
       }
       .editing #addRule {
         display: block;
@@ -82,10 +88,17 @@
       <div id="mainContainer">
         <div class="header">
           <span class="title">[[name]]</span>
-          <gr-button
-              link
-              id="removeBtn"
-              on-tap="_handleRemovePermission">Remove</gr-button>
+          <div class="right">
+            <paper-toggle-button
+                id="exclusiveToggle"
+                checked="{{permission.value.exclusive}}"
+                on-change="_handleValueChange"
+                disabled$="[[!editing]]"></paper-toggle-button>Exclusive
+            <gr-button
+                link
+                id="removeBtn"
+                on-tap="_handleRemovePermission">Remove</gr-button>
+          </div>
         </div><!-- end header -->
         <div class="rules">
           <template
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 8a0403d..f9c04e60 100644
--- a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.js
+++ b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission.js
@@ -58,6 +58,7 @@
         type: Boolean,
         value: false,
       },
+      _originalExclusiveValue: Boolean,
     },
 
     behaviors: [
@@ -68,6 +69,26 @@
       '_handleRulesChanged(_rules.splices)',
     ],
 
+    listeners: {
+      'access-saved': '_handleAccessSaved',
+    },
+
+    ready() {
+      this._setupValues();
+    },
+
+    _setupValues() {
+      if (!this.permission) { return; }
+      this._originalExclusiveValue = !!this.permission.value.exclusive;
+      Polymer.dom.flush();
+    },
+
+    _handleAccessSaved() {
+      // Set a new 'original' value to keep track of after the value has been
+      // saved.
+      this._setupValues();
+    },
+
     _handleEditingChanged(editing, editingOld) {
       // Ignore when editing gets set initially.
       if (!editingOld) { return; }
@@ -76,9 +97,19 @@
         this._deleted = false;
         this._groupFilter = '';
         this._rules = this._rules.filter(rule => !rule.value.added);
+
+        // Restore exclusive bit to original.
+        this.set(['permission', 'value', 'exclusive'],
+            this._originalExclusiveValue);
       }
     },
 
+    _handleValueChange() {
+      this.permission.value.modified = true;
+      // Allows overall access page to know a change has been made.
+      this.dispatchEvent(new CustomEvent('access-modified', {bubbles: true}));
+    },
+
     _handleRemovePermission() {
       this._deleted = true;
       this.permission.value.deleted = true;
diff --git a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_test.html b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_test.html
index 88a7472..b67d705 100644
--- a/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-permission/gr-permission_test.html
@@ -288,6 +288,7 @@
             },
           },
         };
+        element._setupValues();
         flushAsynchronousOperations();
       });
 
@@ -326,6 +327,32 @@
         assert.isFalse(element.$.permission.classList.contains('deleted'));
         assert.isFalse(element._deleted);
       });
+
+      test('modify a permission', () => {
+        element.editing = true;
+        element.name = 'Priority';
+        element.section = 'refs/*';
+
+        assert.isFalse(element._originalExclusiveValue);
+        assert.isNotOk(element.permission.value.modified);
+        MockInteractions.tap(element.$.exclusiveToggle);
+        flushAsynchronousOperations();
+        assert.isTrue(element.permission.value.exclusive);
+        assert.isTrue(element.permission.value.modified);
+        assert.isFalse(element._originalExclusiveValue);
+        element.editing = false;
+        assert.isFalse(element.permission.value.exclusive);
+      });
+
+      test('_handleValueChange', () => {
+        const modifiedHandler = sandbox.stub();
+        element.permission = {value: {rules: {}}};
+        element.addEventListener('access-modified', modifiedHandler);
+        assert.isNotOk(element.permission.value.modified);
+        element._handleValueChange();
+        assert.isTrue(element.permission.value.modified);
+        assert.isTrue(modifiedHandler.called);
+      });
     });
   });
 </script>
diff --git a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.html b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.html
index 256c59f..b26121c 100644
--- a/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.html
+++ b/polygerrit-ui/app/elements/admin/gr-repo-access/gr-repo-access_test.html
@@ -76,30 +76,6 @@
         'Code-Review': {},
       },
     };
-    const repoAccessInput = {
-      add: {
-        'refs/*': {
-          permissions: {
-            owner: {
-              rules: {
-                123: {action: 'DENY', modified: true},
-              },
-            },
-          },
-        },
-      },
-      remove: {
-        'refs/*': {
-          permissions: {
-            owner: {
-              rules: {
-                123: null,
-              },
-            },
-          },
-        },
-      },
-    };
     setup(() => {
       sandbox = sinon.sandbox.create();
       element = fixture('basic');
@@ -126,14 +102,13 @@
           name: 'Create Account',
         },
       };
-
       const accessStub = sandbox.stub(element.$.restAPI,
           'getRepoAccessRights');
 
-
-      accessStub.withArgs('New Repo').returns(Promise.resolve(accessRes));
+      accessStub.withArgs('New Repo').returns(
+          Promise.resolve(JSON.parse(JSON.stringify(accessRes))));
       accessStub.withArgs('Another New Repo')
-          .returns(Promise.resolve(accessRes2));
+          .returns(Promise.resolve(JSON.parse(JSON.stringify(accessRes2))));
       const capabilitiesStub = sandbox.stub(element.$.restAPI,
           'getCapabilities');
       capabilitiesStub.returns(Promise.resolve(capabilitiesRes));
@@ -168,26 +143,13 @@
           name: 'Access Database',
         },
       };
-      const accessRes = {
-        local: {
-          GLOBAL_CAPABILITIES: {
-            permissions: {
-              accessDatabase: {
-                rules: {
-                  123: {},
-                },
-              },
-            },
-          },
-        },
-      };
       const repoRes = {
         labels: {
           'Code-Review': {},
         },
       };
-      const accessStub = sandbox.stub(element.$.restAPI,
-          'getRepoAccessRights').returns(Promise.resolve(accessRes));
+      const accessStub = sandbox.stub(element.$.restAPI, 'getRepoAccessRights')
+          .returns(Promise.resolve(JSON.parse(JSON.stringify(accessRes2))));
       const capabilitiesStub = sandbox.stub(element.$.restAPI,
           'getCapabilities').returns(Promise.resolve(capabilitiesRes));
       const repoStub = sandbox.stub(element.$.restAPI, 'getRepo').returns(
@@ -228,7 +190,8 @@
 
     suite('with defined sections', () => {
       setup(() => {
-        element._sections = element.toSortedArray(accessRes.local);
+        element._sections =
+            element.toSortedArray(JSON.parse(JSON.stringify(accessRes.local)));
         flushAsynchronousOperations();
       });
 
@@ -268,7 +231,7 @@
         element._local = JSON.parse(JSON.stringify(accessRes.local));
         assert.deepEqual(element._computeAddAndRemove(), {add: {}, remove: {}});
         element._local['refs/*'].permissions.owner.rules[123].deleted = true;
-        const expectedInput = {
+        let expectedInput = {
           add: {},
           remove: {
             'refs/*': {
@@ -285,19 +248,26 @@
         assert.deepEqual(element._computeAddAndRemove(), expectedInput);
         delete element._local['refs/*'].permissions.owner.rules[123].deleted;
         element._local['refs/*'].permissions.owner.rules[123].modified = true;
-        assert.deepEqual(element._computeAddAndRemove(), repoAccessInput);
-      });
-
-      test('_computeAddAndRemove permissions', () => {
-        element._local = JSON.parse(JSON.stringify(accessRes.local));
-        assert.deepEqual(element._computeAddAndRemove(), {add: {}, remove: {}});
-        element._local['refs/*'].permissions.owner.deleted = true;
-        const expectedInput = {
-          add: {},
+        expectedInput = {
+          add: {
+            'refs/*': {
+              permissions: {
+                owner: {
+                  rules: {
+                    123: {action: 'DENY', modified: true},
+                  },
+                },
+              },
+            },
+          },
           remove: {
             'refs/*': {
               permissions: {
-                owner: {rules: {}},
+                owner: {
+                  rules: {
+                    123: null,
+                  },
+                },
               },
             },
           },
@@ -309,7 +279,7 @@
         element._local = JSON.parse(JSON.stringify(accessRes.local));
         assert.deepEqual(element._computeAddAndRemove(), {add: {}, remove: {}});
         element._local['refs/*'].permissions.owner.deleted = true;
-        const expectedInput = {
+        let expectedInput = {
           add: {},
           remove: {
             'refs/*': {
@@ -320,6 +290,31 @@
           },
         };
         assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+        delete element._local['refs/*'].permissions.owner.deleted;
+        element._local['refs/*'].permissions.owner.modified = true;
+        expectedInput = {
+          add: {
+            'refs/*': {
+              permissions: {
+                owner: {
+                  modified: true,
+                  rules: {
+                    234: {action: 'ALLOW'},
+                    123: {action: 'DENY'},
+                  },
+                },
+              },
+            },
+          },
+          remove: {
+            'refs/*': {
+              permissions: {
+                owner: {rules: {}},
+              },
+            },
+          },
+        };
+        assert.deepEqual(element._computeAddAndRemove(), expectedInput);
       });
 
       test('_computeAddAndRemove combinations', () => {
@@ -368,11 +363,65 @@
           },
         };
         assert.deepEqual(element._computeAddAndRemove(), expectedInput);
+        // Modify both permissions with an exclusive bit. Owner is still
+        // deleted.
+        element._local['refs/*'].permissions.owner.exclusive = true;
+        element._local['refs/*'].permissions.owner.modified = true;
+        element._local['refs/*'].permissions.read.exclusive = true;
+        element._local['refs/*'].permissions.read.modified = true;
+        expectedInput = {
+          add: {
+            'refs/*': {
+              permissions: {
+                read: {
+                  exclusive: true,
+                  modified: true,
+                  rules: {
+                    234: {action: 'ALLOW'},
+                  },
+                },
+              },
+            },
+          },
+          remove: {
+            'refs/*': {
+              permissions: {
+                owner: {rules: {}},
+                read: {rules: {}},
+              },
+            },
+          },
+        };
+        assert.deepEqual(element._computeAddAndRemove(), expectedInput);
       });
 
       test('_handleSaveForReview', done => {
-        sandbox.stub(element.$.restAPI, 'getRepoAccessRights')
-            .returns(Promise.resolve(accessRes));
+        const repoAccessInput = {
+          add: {
+            'refs/*': {
+              permissions: {
+                owner: {
+                  rules: {
+                    123: {action: 'DENY', modified: true},
+                  },
+                },
+              },
+            },
+          },
+          remove: {
+            'refs/*': {
+              permissions: {
+                owner: {
+                  rules: {
+                    123: null,
+                  },
+                },
+              },
+            },
+          },
+        };
+        sandbox.stub(element.$.restAPI, 'getRepoAccessRights').returns(
+            Promise.resolve(JSON.parse(JSON.stringify(accessRes))));
         sandbox.stub(element.$.restAPI, 'getRepo')
             .returns(Promise.resolve({}));
         sandbox.stub(Gerrit.Nav, 'navigateToChange');
@@ -381,8 +430,7 @@
             .returns(Promise.resolve({_number: 1}));
 
         element.repo = 'test-repo';
-        sandbox.stub(element, '_computeAddAndRemove')
-            .returns(repoAccessInput);
+        sandbox.stub(element, '_computeAddAndRemove').returns(repoAccessInput);
 
         element._handleSaveForReview().then(() => {
           assert.isTrue(saveForReviewStub.called);
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html
index ca94356..66ab290 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.html
@@ -28,6 +28,7 @@
 <link rel="import" href="../../shared/gr-editable-label/gr-editable-label.html">
 <link rel="import" href="../../shared/gr-label/gr-label.html">
 <link rel="import" href="../../shared/gr-linked-chip/gr-linked-chip.html">
+<link rel="import" href="../../shared/gr-tooltip-content/gr-tooltip-content.html">
 <link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
 <link rel="import" href="../gr-commit-info/gr-commit-info.html">
 <link rel="import" href="../gr-reviewer-list/gr-reviewer-list.html">
@@ -135,6 +136,13 @@
       .parentList gr-commit-info {
         display: inline-block;
       }
+      #parentNotCurrentMessage {
+        display: none;
+      }
+      .parentList.notCurrent.nonMerge #parentNotCurrentMessage {
+        --arrow-color: #ffa62f;
+        display: inline-block;
+      }
       @media screen and (max-width: 50em), screen and (min-width: 75em) {
         :host {
           display: table;
@@ -236,13 +244,18 @@
       <section>
         <span class="title">[[_computeParentsLabel(_currentParents)]]</span>
         <span class="value">
-          <ol class$="[[_computeParentListClass(_currentParents)]]">
+          <ol class$="[[_computeParentListClass(_currentParents, parentIsCurrent)]]">
             <template is="dom-repeat" items="[[_currentParents]]" as="parent">
               <li>
                 <gr-commit-info
                     change="[[change]]"
                     commit-info="[[parent]]"
                     server-config="[[serverConfig]]"></gr-commit-info>
+                <gr-tooltip-content
+                    id="parentNotCurrentMessage"
+                    has-tooltip
+                    show-icon
+                    title$="[[_notCurrentMessage]]"></gr-tooltip-content>
               </li>
             </template>
           </ol>
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
index 92763ee..ddee577 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata.js
@@ -25,6 +25,8 @@
     CHERRY_PICK: 'Cherry Pick',
   };
 
+  const NOT_CURRENT_MESSAGE = 'Not current - rebase possible';
+
   Polymer({
     is: 'gr-change-metadata',
 
@@ -46,6 +48,12 @@
        * @type {{ note_db_enabled: string }}
        */
       serverConfig: Object,
+      parentIsCurrent: Boolean,
+      _notCurrentMessage: {
+        type: String,
+        value: NOT_CURRENT_MESSAGE,
+        readOnly: true,
+      },
       _topicReadOnly: {
         type: Boolean,
         computed: '_computeTopicReadOnly(mutable, change)',
@@ -425,8 +433,12 @@
       return parents.length > 1 ? 'Parents' : 'Parent';
     },
 
-    _computeParentListClass(parents) {
-      return parents.length > 1 ? 'parentList merge' : 'parentList';
+    _computeParentListClass(parents, parentIsCurrent) {
+      return [
+        'parentList',
+        parents.length > 1 ? 'merge' : 'nonMerge',
+        parentIsCurrent ? 'current' : 'notCurrent',
+      ].join(' ');
     },
   });
 })();
diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html
index f6d020e..9ee09ea 100644
--- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html
@@ -336,6 +336,18 @@
           'Parents');
     });
 
+    test('_computeParentListClass', () => {
+      const parent = {commit: 'abc123', subject: 'My parent commit'};
+      assert.equal(element._computeParentListClass([parent], true),
+          'parentList nonMerge current');
+      assert.equal(element._computeParentListClass([parent], false),
+          'parentList nonMerge notCurrent');
+      assert.equal(element._computeParentListClass([parent, parent], false),
+          'parentList merge notCurrent');
+      assert.equal(element._computeParentListClass([parent, parent], true),
+          'parentList merge current');
+    });
+
     test('_showAddTopic', () => {
       assert.isTrue(element._showAddTopic(null, false));
       assert.isTrue(element._showAddTopic({base: {topic: null}}, false));
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
index 7db2bc4..7e661c7 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.html
@@ -370,6 +370,7 @@
               server-config="[[_serverConfig]]"
               missing-labels="[[_missingLabels]]"
               mutable="[[_loggedIn]]"
+              parent-is-current="[[!_rebaseOriginallyEnabled]]"
               on-show-reply-dialog="_handleShowReplyDialog">
           </gr-change-metadata>
           <!-- Plugins insert content into following container.
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
index 9cf029b..fd9a490 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view.js
@@ -224,6 +224,7 @@
         value: false,
         observer: '_updateToggleContainerClass',
       },
+      _rebaseOriginallyEnabled: Boolean,
     },
 
     behaviors: [
@@ -937,6 +938,7 @@
       if (revisionActions && revisionActions.rebase) {
         revisionActions.rebase.rebaseOnCurrent =
             !!revisionActions.rebase.enabled;
+        this._rebaseOriginallyEnabled = !!revisionActions.rebase.enabled;
         revisionActions.rebase.enabled = true;
       }
       return revisionActions;
diff --git a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html
index f8d4ba8..df9ce54 100644
--- a/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html
+++ b/polygerrit-ui/app/elements/change/gr-messages-list/gr-messages-list.html
@@ -62,10 +62,6 @@
         align-items: center;
         display: flex;
       }
-      paper-toggle-button {
-        --paper-toggle-button-checked-bar-color: var(--color-link);
-        --paper-toggle-button-checked-button-color: var(--color-link);
-      }
     </style>
     <div class="header">
       <h3>Messages</h3>
diff --git a/polygerrit-ui/app/elements/gr-app.html b/polygerrit-ui/app/elements/gr-app.html
index 7b279c7..ef93794 100644
--- a/polygerrit-ui/app/elements/gr-app.html
+++ b/polygerrit-ui/app/elements/gr-app.html
@@ -211,6 +211,7 @@
     </gr-overlay>
     <gr-overlay id="registration" with-backdrop>
       <gr-registration-dialog
+          settings-url="[[_settingsUrl]]"
           on-account-detail-update="_handleAccountDetailUpdate"
           on-close="_handleRegistrationDialogClose">
       </gr-registration-dialog>
diff --git a/polygerrit-ui/app/elements/gr-app.js b/polygerrit-ui/app/elements/gr-app.js
index 722fff8..e98aaac 100644
--- a/polygerrit-ui/app/elements/gr-app.js
+++ b/polygerrit-ui/app/elements/gr-app.js
@@ -84,6 +84,7 @@
         type: String,
         computed: '_computePluginScreenName(params)',
       },
+      _settingsUrl: String,
     },
 
     listeners: {
@@ -122,6 +123,10 @@
         this._version = version;
       });
 
+      // Note: this is evaluated here to ensure that it only happens after the
+      // router has been initialized. @see Issue 7837
+      this._settingsUrl = Gerrit.Nav.getUrlForSettings();
+
       this.$.reporting.appStarted();
       this._viewState = {
         changeView: {
diff --git a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html
index 0cbd1f6..1b3d9d4 100644
--- a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html
+++ b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.html
@@ -94,7 +94,7 @@
         <hr>
         <p>
           More configuration options for Gerrit may be found in the
-          <a on-tap="close" href$="[[_computeSettingsUrl(_account)]]">settings</a>.
+          <a on-tap="close" href$="[[settingsUrl]]">settings</a>.
         </p>
       </main>
       <footer>
diff --git a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js
index 406d16c..dace2ca 100644
--- a/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js
+++ b/polygerrit-ui/app/elements/settings/gr-registration-dialog/gr-registration-dialog.js
@@ -30,6 +30,7 @@
      */
 
     properties: {
+      settingsUrl: String,
       /** @type {?} */
       _account: {
         type: Object,
@@ -89,9 +90,5 @@
     _computeSaveDisabled(name, username, email, saving) {
       return !name || !username || !email || saving;
     },
-
-    _computeSettingsUrl() {
-      return Gerrit.Nav.getUrlForSettings();
-    },
   });
 })();
diff --git a/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.html b/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.html
index e80cbe5..68db696 100644
--- a/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.html
+++ b/polygerrit-ui/app/elements/shared/gr-tooltip-content/gr-tooltip-content.html
@@ -19,6 +19,11 @@
 
 <dom-module id="gr-tooltip-content">
   <template>
+    <style>
+      .arrow {
+        color: var(--arrow-color);
+      }
+    </style>
     <slot></slot><!--
  --><span class="arrow" hidden$="[[!showIcon]]">&#9432;</span>
   </template>
diff --git a/polygerrit-ui/app/styles/shared-styles.html b/polygerrit-ui/app/styles/shared-styles.html
index c6e179f..a3cf247 100644
--- a/polygerrit-ui/app/styles/shared-styles.html
+++ b/polygerrit-ui/app/styles/shared-styles.html
@@ -96,6 +96,10 @@
       .separator.transparent {
         background-color: transparent;
       }
+      paper-toggle-button {
+        --paper-toggle-button-checked-bar-color: var(--color-link);
+        --paper-toggle-button-checked-button-color: var(--color-link);
+      }
     </style>
   </template>
 </dom-module>
diff --git a/version.bzl b/version.bzl
index 340ba87..62d841f 100644
--- a/version.bzl
+++ b/version.bzl
@@ -3,10 +3,3 @@
 # when talking to the destination repository.
 #
 GERRIT_VERSION = "2.16-SNAPSHOT"
-
-def check_version(x):
-    if native.bazel_version == "":
-        # experimental / unreleased Bazel.
-        return
-    if native.bazel_version < x:
-        fail("\nERROR: Current Bazel version is {}, expected at least {}\n".format(native.bazel_version, x))