Merge "Update syntax highlighting class"
diff --git a/Documentation/config-themes.txt b/Documentation/config-themes.txt
index 38bfc46..a83c747 100644
--- a/Documentation/config-themes.txt
+++ b/Documentation/config-themes.txt
@@ -4,34 +4,28 @@
 the browser, allowing organizations to alter the look and
 feel of the application to fit with their general scheme.
 
-Configuration can either be sitewide or per-project. Projects without a
-specified theme inherit from their parents, or from the sitewide theme
-for `All-Projects`.
+== HTML Header/Footer and CSS
 
-Sitewide themes are stored in `'$site_path'/etc`, and per-project
-themes are stored in `'$site_path'/themes/{project-name}`. Files are
-only served from a single theme directory; if you want to modify or
-extend an inherited theme, you must copy it into the appropriate
-per-project directory.
-
-== HTML Header/Footer
+The HTML header, footer and CSS may be customized for login
+screens (LDAP, OAuth, OpenId) and the internally managed
+Gitweb servlet.
 
 At startup Gerrit reads the following files (if they exist) and
 uses them to customize the HTML page it sends to clients:
 
-* `<theme-dir>/GerritSiteHeader.html`
+* `etc/GerritSiteHeader.html`
 +
 HTML is inserted below the menu bar, but above any page content.
 This is a good location for an organizational logo, or links to
 other systems like bug tracking.
 
-* `<theme-dir>/GerritSiteFooter.html`
+* `etc/GerritSiteFooter.html`
 +
 HTML is inserted at the bottom of the page, below all other content,
 but just above the footer rule and the "Powered by Gerrit Code
 Review (v....)" message shown at the extreme bottom.
 
-* `<theme-dir>/GerritSite.css`
+* `etc/GerritSite.css`
 +
 The CSS rules are inlined into the top of the HTML page, inside
 of a `<style>` tag.  These rules can be used to support styling
diff --git a/Documentation/intro-project-owner.txt b/Documentation/intro-project-owner.txt
index cdc9c4e..1f98291 100644
--- a/Documentation/intro-project-owner.txt
+++ b/Documentation/intro-project-owner.txt
@@ -614,18 +614,6 @@
 are inherited by the child projects. A child project can overwrite an
 inherited download command, or remove it by assigning no value to it.
 
-[[theme]]
-== Theme
-
-Gerrit supports project-specific themes for customizing the appearance
-of the change screen and the diff screens. It is possible to define an
-HTML header and footer and to adapt Gerrit's CSS. Details about themes
-are explained in the link:config-themes.html[Themes] section.
-
-Project-specific themes can only be installed by Gerrit administrators
-since the theme files must be copied into the Gerrit installation
-folder.
-
 [[tool-integration]]
 == Integration with other tools
 
diff --git a/Documentation/rest-api-projects.txt b/Documentation/rest-api-projects.txt
index 9c24c79..9803c19 100644
--- a/Documentation/rest-api-projects.txt
+++ b/Documentation/rest-api-projects.txt
@@ -3135,9 +3135,6 @@
 Map with the comment link configurations of the project. The name of
 the comment link configuration is mapped to a link:#commentlink-info[
 CommentlinkInfo] entity.
-|`theme`                                   |optional|
-The theme that is configured for the project as a link:#theme-info[
-ThemeInfo] entity.
 |`plugin_config`                           |optional|
 Plugin configuration as map which maps the plugin name to a map of
 parameter names to link:#config-parameter-info[ConfigParameterInfo]
@@ -3689,21 +3686,6 @@
 |=========================
 
 
-[[theme-info]]
-=== ThemeInfo
-The `ThemeInfo` entity describes a theme.
-
-[options="header",cols="1,^2,4"]
-|=============================
-|Field Name      ||Description
-|`css`           |optional|
-The path to the `GerritSite.css` file.
-|`header`        |optional|
-The path to the `GerritSiteHeader.html` file.
-|`footer`        |optional|
-The path to the `GerritSiteFooter.html` file.
-|=============================
-
 GERRIT
 ------
 Part of link:index.html[Gerrit Code Review]
diff --git a/WORKSPACE b/WORKSPACE
index c2629ba..a984c41 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -859,30 +859,30 @@
     sha1 = "42a25dc3219429f0e5d060061f71acb49bf010a0",
 )
 
-TRUTH_VERS = "0.42"
+TRUTH_VERS = "0.43"
 
 maven_jar(
     name = "truth",
     artifact = "com.google.truth:truth:" + TRUTH_VERS,
-    sha1 = "b5768f644b114e6cf5c3962c2ebcb072f788dcbb",
+    sha1 = "0cb9105957368f68d5fd771045bfa27d4a534836",
 )
 
 maven_jar(
     name = "truth-java8-extension",
     artifact = "com.google.truth.extensions:truth-java8-extension:" + TRUTH_VERS,
-    sha1 = "4d01dfa5b3780632a3d109e14e101f01d10cce2c",
+    sha1 = "0707dd0b4eb2101aa85c4bb7caf52d9ae32f0a43",
 )
 
 maven_jar(
     name = "truth-liteproto-extension",
     artifact = "com.google.truth.extensions:truth-liteproto-extension:" + TRUTH_VERS,
-    sha1 = "c231e6735aa6c133c7e411ae1c1c90b124900a8b",
+    sha1 = "67d833098345fc744c6fb2c38c739557b2d73742",
 )
 
 maven_jar(
     name = "truth-proto-extension",
     artifact = "com.google.truth.extensions:truth-proto-extension:" + TRUTH_VERS,
-    sha1 = "c41d22e8b4a61b4171e57c44a2959ebee0091a14",
+    sha1 = "b9641436e7b48b3642ac7aa7b8f3d1b9d04bc44b",
 )
 
 maven_jar(
diff --git a/java/com/google/gerrit/acceptance/LogThreshold.java b/java/com/google/gerrit/acceptance/LogThreshold.java
index da1fcc5..36831f3 100644
--- a/java/com/google/gerrit/acceptance/LogThreshold.java
+++ b/java/com/google/gerrit/acceptance/LogThreshold.java
@@ -17,11 +17,13 @@
 import static java.lang.annotation.ElementType.TYPE;
 import static java.lang.annotation.RetentionPolicy.RUNTIME;
 
+import java.lang.annotation.Inherited;
 import java.lang.annotation.Retention;
 import java.lang.annotation.Target;
 
 @Target({TYPE, METHOD})
 @Retention(RUNTIME)
+@Inherited
 public @interface LogThreshold {
   String level() default "DEBUG";
 }
diff --git a/java/com/google/gerrit/extensions/api/projects/ConfigInfo.java b/java/com/google/gerrit/extensions/api/projects/ConfigInfo.java
index 08ba486..fb2a0fe 100644
--- a/java/com/google/gerrit/extensions/api/projects/ConfigInfo.java
+++ b/java/com/google/gerrit/extensions/api/projects/ConfigInfo.java
@@ -48,7 +48,6 @@
   public Map<String, ActionInfo> actions;
 
   public Map<String, CommentLinkInfo> commentlinks;
-  public ThemeInfo theme;
 
   public Map<String, List<String>> extensionPanelNames;
 
diff --git a/java/com/google/gerrit/extensions/api/projects/ThemeInfo.java b/java/com/google/gerrit/extensions/api/projects/ThemeInfo.java
deleted file mode 100644
index d5d520f..0000000
--- a/java/com/google/gerrit/extensions/api/projects/ThemeInfo.java
+++ /dev/null
@@ -1,29 +0,0 @@
-// Copyright (C) 2013 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.extensions.api.projects;
-
-public class ThemeInfo {
-  public static final ThemeInfo INHERIT = new ThemeInfo(null, null, null);
-
-  public final String css;
-  public final String header;
-  public final String footer;
-
-  public ThemeInfo(String css, String header, String footer) {
-    this.css = css;
-    this.header = header;
-    this.footer = footer;
-  }
-}
diff --git a/java/com/google/gerrit/server/ProjectUtil.java b/java/com/google/gerrit/server/ProjectUtil.java
index fc14768..1db4aa3 100644
--- a/java/com/google/gerrit/server/ProjectUtil.java
+++ b/java/com/google/gerrit/server/ProjectUtil.java
@@ -45,15 +45,26 @@
     }
   }
 
+  public static String sanitizeProjectName(String name) {
+    name = stripGitSuffix(name);
+    name = stripTrailingSlash(name);
+    return name;
+  }
+
   public static String stripGitSuffix(String name) {
     if (name.endsWith(".git")) {
       // Be nice and drop the trailing ".git" suffix, which we never keep
       // in our database, but clients might mistakenly provide anyway.
       //
       name = name.substring(0, name.length() - 4);
-      while (name.endsWith("/")) {
-        name = name.substring(0, name.length() - 1);
-      }
+      name = stripTrailingSlash(name);
+    }
+    return name;
+  }
+
+  private static String stripTrailingSlash(String name) {
+    while (name.endsWith("/")) {
+      name = name.substring(0, name.length() - 1);
     }
     return name;
   }
diff --git a/java/com/google/gerrit/server/api/accounts/AccountsImpl.java b/java/com/google/gerrit/server/api/accounts/AccountsImpl.java
index 5a30113..9d29888 100644
--- a/java/com/google/gerrit/server/api/accounts/AccountsImpl.java
+++ b/java/com/google/gerrit/server/api/accounts/AccountsImpl.java
@@ -69,7 +69,7 @@
     try {
       return api.create(accounts.parse(TopLevelResource.INSTANCE, IdString.fromDecoded(id)));
     } catch (Exception e) {
-      throw asRestApiException("Cannot parse change", e);
+      throw asRestApiException("Cannot parse account", e);
     }
   }
 
diff --git a/java/com/google/gerrit/server/config/SitePaths.java b/java/com/google/gerrit/server/config/SitePaths.java
index 11ec50c..47b6336 100644
--- a/java/com/google/gerrit/server/config/SitePaths.java
+++ b/java/com/google/gerrit/server/config/SitePaths.java
@@ -43,7 +43,6 @@
   public final Path mail_dir;
   public final Path hooks_dir;
   public final Path static_dir;
-  public final Path themes_dir;
   public final Path index_dir;
 
   public final Path gerrit_sh;
@@ -67,8 +66,7 @@
   public final Path site_css;
   public final Path site_header;
   public final Path site_footer;
-  // For PolyGerrit UI only.
-  public final Path site_theme;
+  public final Path site_theme; // For PolyGerrit UI only.
   public final Path site_gitweb;
 
   /** {@code true} if {@link #site_path} has not been initialized. */
@@ -90,7 +88,6 @@
     mail_dir = etc_dir.resolve("mail");
     hooks_dir = p.resolve("hooks");
     static_dir = p.resolve("static");
-    themes_dir = p.resolve("themes");
     index_dir = p.resolve("index");
 
     gerrit_sh = bin_dir.resolve("gerrit.sh");
diff --git a/java/com/google/gerrit/server/project/ProjectState.java b/java/com/google/gerrit/server/project/ProjectState.java
index 78505ab..73c222d 100644
--- a/java/com/google/gerrit/server/project/ProjectState.java
+++ b/java/com/google/gerrit/server/project/ProjectState.java
@@ -15,12 +15,10 @@
 package com.google.gerrit.server.project;
 
 import static com.google.gerrit.common.data.PermissionRule.Action.ALLOW;
-import static java.nio.charset.StandardCharsets.UTF_8;
 
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.data.AccessSection;
@@ -32,7 +30,6 @@
 import com.google.gerrit.common.data.RefConfigSection;
 import com.google.gerrit.common.data.SubscribeSection;
 import com.google.gerrit.extensions.api.projects.CommentLinkInfo;
-import com.google.gerrit.extensions.api.projects.ThemeInfo;
 import com.google.gerrit.extensions.client.SubmitType;
 import com.google.gerrit.extensions.restapi.ResourceConflictException;
 import com.google.gerrit.index.project.ProjectData;
@@ -49,7 +46,6 @@
 import com.google.gerrit.server.account.CapabilityCollection;
 import com.google.gerrit.server.config.AllProjectsName;
 import com.google.gerrit.server.config.AllUsersName;
-import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.git.BranchOrderSection;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.TransferConfig;
@@ -57,8 +53,6 @@
 import com.google.inject.Inject;
 import com.google.inject.assistedinject.Assisted;
 import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -84,7 +78,6 @@
 
   private final boolean isAllProjects;
   private final boolean isAllUsers;
-  private final SitePaths sitePaths;
   private final AllProjectsName allProjectsName;
   private final ProjectCache projectCache;
   private final GitRepositoryManager gitMgr;
@@ -105,11 +98,6 @@
   /** Local access sections, wrapped in SectionMatchers for faster evaluation. */
   private volatile List<SectionMatcher> localAccessSections;
 
-  // TODO(dborowitz): Delete when the GWT UI gets deleted; in the meantime, don't bother with any
-  // refactoring.
-  /** Theme information loaded from site_path/themes. */
-  private volatile ThemeInfo theme;
-
   /** If this is all projects, the capabilities used by the server. */
   private final CapabilityCollection capabilities;
 
@@ -118,7 +106,6 @@
 
   @Inject
   public ProjectState(
-      SitePaths sitePaths,
       ProjectCache projectCache,
       AllProjectsName allProjectsName,
       AllUsersName allUsersName,
@@ -128,7 +115,6 @@
       TransferConfig transferConfig,
       MetricMaker metricMaker,
       @Assisted ProjectConfig config) {
-    this.sitePaths = sitePaths;
     this.projectCache = projectCache;
     this.isAllProjects = config.getProject().getNameKey().equals(allProjectsName);
     this.isAllUsers = config.getProject().getNameKey().equals(allUsersName);
@@ -548,24 +534,6 @@
     return ret;
   }
 
-  public ThemeInfo getTheme() {
-    ThemeInfo theme = this.theme;
-    if (theme == null) {
-      synchronized (this) {
-        theme = this.theme;
-        if (theme == null) {
-          theme = loadTheme();
-          this.theme = theme;
-        }
-      }
-    }
-    if (theme == ThemeInfo.INHERIT) {
-      ProjectState parent = Iterables.getFirst(parents(), null);
-      return parent != null ? parent.getTheme() : null;
-    }
-    return theme;
-  }
-
   public Set<GroupReference> getAllGroups() {
     return getGroups(getAllSections());
   }
@@ -597,26 +565,6 @@
     return all;
   }
 
-  private ThemeInfo loadTheme() {
-    String name = getConfig().getProject().getName();
-    Path dir = sitePaths.themes_dir.resolve(name);
-    if (!Files.exists(dir)) {
-      return ThemeInfo.INHERIT;
-    } else if (!Files.isDirectory(dir)) {
-      logger.atWarning().log("Bad theme for %s: not a directory", name);
-      return ThemeInfo.INHERIT;
-    }
-    try {
-      return new ThemeInfo(
-          readFile(dir.resolve(SitePaths.CSS_FILENAME)),
-          readFile(dir.resolve(SitePaths.HEADER_FILENAME)),
-          readFile(dir.resolve(SitePaths.FOOTER_FILENAME)));
-    } catch (IOException e) {
-      logger.atSevere().withCause(e).log("Error reading theme for %s", name);
-      return ThemeInfo.INHERIT;
-    }
-  }
-
   public ProjectData toProjectData() {
     ProjectData project = null;
     for (ProjectState state : treeInOrder()) {
@@ -625,10 +573,6 @@
     return project;
   }
 
-  private String readFile(Path p) throws IOException {
-    return Files.exists(p) ? new String(Files.readAllBytes(p), UTF_8) : null;
-  }
-
   private LabelTypes loadLabelTypes() {
     Map<String, LabelType> types = new LinkedHashMap<>();
     for (ProjectState s : treeInOrder()) {
diff --git a/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java b/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java
index e179896..37bc265 100644
--- a/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java
+++ b/java/com/google/gerrit/server/restapi/project/ConfigInfoImpl.java
@@ -101,7 +101,6 @@
     for (UiAction.Description d : uiActions.from(views, new ProjectResource(projectState, user))) {
       actions.put(d.getId(), new ActionInfo(d));
     }
-    this.theme = projectState.getTheme();
 
     this.extensionPanelNames = projectState.getConfig().getExtensionPanelSections();
   }
diff --git a/java/com/google/gerrit/server/restapi/project/CreateProject.java b/java/com/google/gerrit/server/restapi/project/CreateProject.java
index c703fe2..6844cac 100644
--- a/java/com/google/gerrit/server/restapi/project/CreateProject.java
+++ b/java/com/google/gerrit/server/restapi/project/CreateProject.java
@@ -115,7 +115,7 @@
     }
 
     CreateProjectArgs args = new CreateProjectArgs();
-    args.setProjectName(ProjectUtil.stripGitSuffix(name));
+    args.setProjectName(ProjectUtil.sanitizeProjectName(name));
 
     String parentName =
         MoreObjects.firstNonNull(Strings.emptyToNull(input.parent), allProjects.get());
diff --git a/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java b/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java
index e06b406..31c90e5 100644
--- a/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java
+++ b/java/com/google/gerrit/server/restapi/project/ProjectsCollection.java
@@ -32,6 +32,7 @@
 import com.google.gerrit.json.OutputFormat;
 import com.google.gerrit.reviewdb.client.Project;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.ProjectUtil;
 import com.google.gerrit.server.permissions.PermissionBackend;
 import com.google.gerrit.server.permissions.PermissionBackendException;
 import com.google.gerrit.server.permissions.ProjectPermission;
@@ -42,7 +43,6 @@
 import com.google.inject.Provider;
 import com.google.inject.Singleton;
 import java.io.IOException;
-import org.eclipse.jgit.lib.Constants;
 
 @Singleton
 public class ProjectsCollection
@@ -136,9 +136,7 @@
   @Nullable
   private ProjectResource _parse(String id, boolean checkAccess)
       throws IOException, PermissionBackendException, ResourceConflictException {
-    if (id.endsWith(Constants.DOT_GIT_EXT)) {
-      id = id.substring(0, id.length() - Constants.DOT_GIT_EXT.length());
-    }
+    id = ProjectUtil.sanitizeProjectName(id);
 
     Project.NameKey nameKey = new Project.NameKey(id);
     ProjectState state = projectCache.checkedGet(nameKey);
diff --git a/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java b/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
index cd29bf8..7d36e71 100644
--- a/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
+++ b/javatests/com/google/gerrit/acceptance/rest/project/CreateProjectIT.java
@@ -182,6 +182,28 @@
   }
 
   @Test
+  public void createProjectThatEndsWithSlash() throws Exception {
+    String newProjectName = name("newProject");
+    ProjectInfo p = gApi.projects().create(newProjectName + "/").get();
+    assertThat(p.name).isEqualTo(newProjectName);
+    ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName));
+    assertThat(projectState).isNotNull();
+    assertProjectInfo(projectState.getProject(), p);
+    assertHead(newProjectName, "refs/heads/master");
+  }
+
+  @Test
+  public void createProjectThatContainsSlash() throws Exception {
+    String newProjectName = name("newProject/newProject");
+    ProjectInfo p = gApi.projects().create(newProjectName).get();
+    assertThat(p.name).isEqualTo(newProjectName);
+    ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName));
+    assertThat(projectState).isNotNull();
+    assertProjectInfo(projectState.getProject(), p);
+    assertHead(newProjectName, "refs/heads/master");
+  }
+
+  @Test
   public void createProjectWithProperties() throws Exception {
     String newProjectName = name("newProject");
     ProjectInput in = new ProjectInput();
diff --git a/javatests/com/google/gerrit/acceptance/ssh/CreateProjectIT.java b/javatests/com/google/gerrit/acceptance/ssh/CreateProjectIT.java
index e583179..0ce05b0 100644
--- a/javatests/com/google/gerrit/acceptance/ssh/CreateProjectIT.java
+++ b/javatests/com/google/gerrit/acceptance/ssh/CreateProjectIT.java
@@ -49,4 +49,38 @@
     ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName));
     assertThat(projectState).isNull();
   }
+
+  @Test
+  public void withDotGit() throws Exception {
+    String newGroupName = "newGroup";
+    adminRestSession.put("/groups/" + newGroupName);
+    String newProjectName = name("newProject");
+    adminSshSession.exec(
+        "gerrit create-project --branch master --owner "
+            + newGroupName
+            + " "
+            + newProjectName
+            + ".git");
+    adminSshSession.assertSuccess();
+    ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName));
+    assertThat(projectState).isNotNull();
+    assertThat(projectState.getName()).isEqualTo(newProjectName);
+  }
+
+  @Test
+  public void withTrailingSlash() throws Exception {
+    String newGroupName = "newGroup";
+    adminRestSession.put("/groups/" + newGroupName);
+    String newProjectName = name("newProject");
+    adminSshSession.exec(
+        "gerrit create-project --branch master --owner "
+            + newGroupName
+            + " "
+            + newProjectName
+            + "/");
+    adminSshSession.assertSuccess();
+    ProjectState projectState = projectCache.get(new Project.NameKey(newProjectName));
+    assertThat(projectState).isNotNull();
+    assertThat(projectState.getName()).isEqualTo(newProjectName);
+  }
 }
diff --git a/javatests/com/google/gerrit/server/permissions/RefControlTest.java b/javatests/com/google/gerrit/server/permissions/RefControlTest.java
index 872cd91..3a9037c 100644
--- a/javatests/com/google/gerrit/server/permissions/RefControlTest.java
+++ b/javatests/com/google/gerrit/server/permissions/RefControlTest.java
@@ -54,7 +54,6 @@
 import com.google.gerrit.server.config.AllProjectsNameProvider;
 import com.google.gerrit.server.config.AllUsersName;
 import com.google.gerrit.server.config.AllUsersNameProvider;
-import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.git.TransferConfig;
 import com.google.gerrit.server.index.SingleVersionModule.SingleVersionListener;
 import com.google.gerrit.server.project.ProjectCache;
@@ -931,7 +930,6 @@
   }
 
   private InMemoryRepository add(ProjectConfig pc) {
-    SitePaths sitePaths = null;
     List<CommentLinkInfo> commentLinks = null;
 
     InMemoryRepository repo;
@@ -946,7 +944,6 @@
     all.put(
         pc.getName(),
         new ProjectState(
-            sitePaths,
             projectCache,
             allProjectsName,
             allUsersName,
diff --git a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.html b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.html
index 987b63d..da1c871 100644
--- a/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.html
+++ b/polygerrit-ui/app/elements/admin/gr-create-change-dialog/gr-create-change-dialog.html
@@ -32,9 +32,6 @@
   <template>
     <style include="shared-styles"></style>
     <style include="gr-form-styles">
-      :host {
-        display: inline-block;
-      }
       input:not([type="checkbox"]),
       gr-autocomplete,
       iron-autogrow-textarea {
@@ -43,13 +40,6 @@
       .value {
         width: 32em;
       }
-      section {
-        align-items: center;
-        display: flex;
-      }
-      #description {
-        align-items: initial;
-      }
       gr-autocomplete {
         --gr-autocomplete: {
           padding: 0 .15em;
@@ -58,68 +48,71 @@
       .hide {
         display: none;
       }
+      @media only screen and (max-width: 40em) {
+        .value {
+          width: 29em;
+        }
+      }
     </style>
     <div class="gr-form-styles">
-      <div id="form">
-        <section class$="[[_computeBranchClass(baseChange)]]">
-          <span class="title">Select branch for new change</span>
-          <span class="value">
-            <gr-autocomplete
-                id="branchInput"
-                text="{{branch}}"
-                query="[[_query]]"
-                placeholder="Destination branch">
-            </gr-autocomplete>
-          </span>
-        </section>
-        <section class$="[[_computeBranchClass(baseChange)]]">
-          <span class="title">Provide base commit sha1 for change</span>
-          <span class="value">
-            <input
-                is="iron-input"
-                id="baseCommitInput"
-                maxlength="40"
-                placeholder="(optional)"
-                bind-value="{{baseCommit}}">
-          </span>
-        </section>
-        <section>
-          <span class="title">Enter topic for new change</span>
-          <span class="value">
-            <input
-                is="iron-input"
-                id="tagNameInput"
-                maxlength="1024"
-                placeholder="(optional)"
-                bind-value="{{topic}}">
-          </span>
-        </section>
-        <section id="description">
-          <span class="title">Description</span>
-          <span class="value">
-            <iron-autogrow-textarea
-                id="messageInput"
-                class="message"
-                autocomplete="on"
-                rows="4"
-                max-rows="15"
-                bind-value="{{subject}}"
-                placeholder="Insert the description of the change.">
-            </iron-autogrow-textarea>
-          </span>
-        </section>
-        <section class$="[[_computePrivateSectionClass(_privateChangesEnabled)]]">
-          <label
-              class="title"
-              for="privateChangeCheckBox">Private change</label>
-          <span class="value">
-            <input
-                type="checkbox"
-                id="privateChangeCheckBox"
-                checked$="[[_formatBooleanString(privateByDefault)]]">
-          </span>
-        </section>
-      </div>
+      <section class$="[[_computeBranchClass(baseChange)]]">
+        <span class="title">Select branch for new change</span>
+        <span class="value">
+          <gr-autocomplete
+              id="branchInput"
+              text="{{branch}}"
+              query="[[_query]]"
+              placeholder="Destination branch">
+          </gr-autocomplete>
+        </span>
+      </section>
+      <section class$="[[_computeBranchClass(baseChange)]]">
+        <span class="title">Provide base commit sha1 for change</span>
+        <span class="value">
+          <input
+              is="iron-input"
+              id="baseCommitInput"
+              maxlength="40"
+              placeholder="(optional)"
+              bind-value="{{baseCommit}}">
+        </span>
+      </section>
+      <section>
+        <span class="title">Enter topic for new change</span>
+        <span class="value">
+          <input
+              is="iron-input"
+              id="tagNameInput"
+              maxlength="1024"
+              placeholder="(optional)"
+              bind-value="{{topic}}">
+        </span>
+      </section>
+      <section id="description">
+        <span class="title">Description</span>
+        <span class="value">
+          <iron-autogrow-textarea
+              id="messageInput"
+              class="message"
+              autocomplete="on"
+              rows="4"
+              max-rows="15"
+              bind-value="{{subject}}"
+              placeholder="Insert the description of the change.">
+          </iron-autogrow-textarea>
+        </span>
+      </section>
+      <section class$="[[_computePrivateSectionClass(_privateChangesEnabled)]]">
+        <label
+            class="title"
+            for="privateChangeCheckBox">Private change</label>
+        <span class="value">
+          <input
+              type="checkbox"
+              id="privateChangeCheckBox"
+              checked$="[[_formatBooleanString(privateByDefault)]]">
+        </span>
+      </section>
     </div>
     <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
   </template>
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 730f527..32b1152 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
@@ -24,7 +24,6 @@
 <link rel="import" href="../../core/gr-navigation/gr-navigation.html">
 <link rel="import" href="../../core/gr-reporting/gr-reporting.html">
 <link rel="import" href="../../diff/gr-comment-api/gr-comment-api.html">
-<link rel="import" href="../../diff/gr-diff-preferences/gr-diff-preferences.html">
 <link rel="import" href="../../edit/gr-edit-constants.html">
 <link rel="import" href="../../plugins/gr-endpoint-decorator/gr-endpoint-decorator.html">
 <link rel="import" href="../../plugins/gr-endpoint-param/gr-endpoint-param.html">
@@ -40,6 +39,7 @@
 <link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
 <link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
 <link rel="import" href="../../shared/gr-tooltip-content/gr-tooltip-content.html">
+<link rel="import" href="../../shared/revision-info/revision-info.html">
 <link rel="import" href="../gr-change-actions/gr-change-actions.html">
 <link rel="import" href="../gr-change-metadata/gr-change-metadata.html">
 <link rel="import" href="../gr-commit-info/gr-commit-info.html">
@@ -545,6 +545,7 @@
             all-patch-sets="[[_allPatchSets]]"
             change="[[_change]]"
             change-num="[[_changeNum]]"
+            revision-info="[[_revisionInfo]]"
             change-comments="[[_changeComments]]"
             commit-info="[[_commitInfo]]"
             change-url="[[_computeChangeUrl(_change)]]"
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 defbcdc..123a7b5 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
@@ -132,6 +132,7 @@
         type: Object,
         value: {},
       },
+      _prefs: Object,
       /** @type {?} */
       _changeComments: Object,
       _canStartReview: {
@@ -144,6 +145,10 @@
         type: Object,
         observer: '_changeChanged',
       },
+      _revisionInfo: {
+        type: Object,
+        computed: '_getRevisionInfo(_change)',
+      },
       /** @type {?} */
       _commitInfo: Object,
       _currentRevision: {
@@ -291,6 +296,7 @@
       'fullscreen-overlay-closed': '_handleShowBackgroundContent',
       'diff-comments-modified': '_handleReloadCommentThreads',
     },
+
     observers: [
       '_labelsChanged(_change.labels.*)',
       '_paramsAndChangeChanged(params, _change)',
@@ -375,7 +381,7 @@
     _setDiffViewMode(opt_reset) {
       if (!opt_reset && this.viewState.diffViewMode) { return; }
 
-      return this.$.restAPI.getPreferences().then( prefs => {
+      return this._getPreferences().then( prefs => {
         if (!this.viewState.diffMode) {
           this.set('viewState.diffMode', prefs.default_diff_view);
         }
@@ -847,10 +853,11 @@
 
     _changeChanged(change) {
       if (!change || !this._patchRange || !this._allPatchSets) { return; }
-      this.set('_patchRange.basePatchNum',
-          this._patchRange.basePatchNum || 'PARENT');
-      this.set('_patchRange.patchNum',
-          this._patchRange.patchNum ||
+
+      const parent = this._getBasePatchNum(change, this._patchRange);
+
+      this.set('_patchRange.basePatchNum', parent);
+      this.set('_patchRange.patchNum', this._patchRange.patchNum ||
               this.computeLatestPatchNum(this._allPatchSets));
 
       // Reset the related changes toggle in the event it was previously
@@ -861,6 +868,35 @@
       this.fire('title-change', {title});
     },
 
+    /**
+     * Gets base patch number, if is a parent try and
+     * decide from preference weather to default to `auto merge`
+     * or `Parent 1`.
+     * @param {Object} change
+     * @param {Object} patchRange
+     * @return {number|string}
+     */
+    _getBasePatchNum(change, patchRange) {
+      if (patchRange.basePatchNum &&
+          patchRange.basePatchNum !== 'PARENT') {
+        return patchRange.basePatchNum;
+      }
+
+      const revisionInfo = this._getRevisionInfo(change);
+      if (!revisionInfo) return 'PARENT';
+
+      const parentCounts = revisionInfo.getParentCountMap();
+      // check that there is at least 2 parents otherwise fall back to 1,
+      // which means there is only one parent.
+      const parentCount = parentCounts.hasOwnProperty(1) ?
+          parentCounts[1] : 1;
+
+      const preferFirst = this._prefs &&
+          this._prefs.default_base_for_merges === 'FIRST_PARENT';
+
+      return parentCount > 1 && preferFirst ? -1 : 'PARENT';
+    },
+
     _computeShowPrimaryTabs(dynamicTabContentEndpoints) {
       return dynamicTabContentEndpoints.length > 0;
     },
@@ -1120,6 +1156,10 @@
           });
     },
 
+    _getPreferences() {
+      return this.$.restAPI.getPreferences();
+    },
+
     _updateRebaseAction(revisionActions) {
       if (revisionActions && revisionActions.rebase) {
         revisionActions.rebase.rebaseOnCurrent =
@@ -1173,9 +1213,12 @@
       const detailCompletes = this.$.restAPI.getChangeDetail(
           this._changeNum, this._handleGetChangeDetailError.bind(this));
       const editCompletes = this._getEdit();
+      const prefCompletes = this._getPreferences();
 
-      return Promise.all([detailCompletes, editCompletes])
-          .then(([change, edit]) => {
+      return Promise.all([detailCompletes, editCompletes, prefCompletes])
+          .then(([change, edit, prefs]) => {
+            this._prefs = prefs;
+
             if (!change) {
               return '';
             }
@@ -1724,6 +1767,10 @@
           e.detail.starred);
     },
 
+    _getRevisionInfo(change) {
+      return new Gerrit.RevisionInfo(change);
+    },
+
     _computeCurrentRevision(currentRevision, revisions) {
       return revisions && revisions[currentRevision];
     },
diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
index a88142e..b8b48e7 100644
--- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
+++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html
@@ -297,7 +297,8 @@
       });
 
       test(', should open diff preferences', () => {
-        const stub = sandbox.stub(element.$.fileList.$.diffPreferences, 'open');
+        const stub = sandbox.stub(
+            element.$.fileList.$.diffPreferencesDialog, 'open');
         element._loggedIn = false;
         element.disableDiffPrefs = true;
         MockInteractions.pressAndReleaseKeyOn(element, 188, null, ',');
@@ -1604,8 +1605,8 @@
     });
 
     test('_selectedRevision updates when patchNum is changed', () => {
-      const revision1 = {_number: 1, commit: {}};
-      const revision2 = {_number: 2, commit: {}};
+      const revision1 = {_number: 1, commit: {parents: []}};
+      const revision2 = {_number: 2, commit: {parents: []}};
       sandbox.stub(element.$.restAPI, 'getChangeDetail').returns(
           Promise.resolve({
             revisions: {
@@ -1618,6 +1619,7 @@
             change_id: 'loremipsumdolorsitamet',
           }));
       sandbox.stub(element, '_getEdit').returns(Promise.resolve());
+      sandbox.stub(element, '_getPreferences').returns(Promise.resolve({}));
       element._patchRange = {patchNum: '2'};
       return element._getChangeDetail().then(() => {
         assert.strictEqual(element._selectedRevision, revision2);
diff --git a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.html b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.html
index 5e82cda..28d25d2 100644
--- a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.html
+++ b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.html
@@ -19,8 +19,8 @@
 
 <link rel="import" href="../../../behaviors/gr-patch-set-behavior/gr-patch-set-behavior.html">
 <link rel="import" href="../../../behaviors/rest-client-behavior/rest-client-behavior.html">
-<link rel="import" href="../../shared/gr-download-commands/gr-download-commands.html">
 <link rel="import" href="../../../styles/shared-styles.html">
+<link rel="import" href="../../shared/gr-download-commands/gr-download-commands.html">
 
 <dom-module id="gr-download-dialog">
   <template>
@@ -87,7 +87,7 @@
           selected-scheme="{{_selectedScheme}}"></gr-download-commands>
     </section>
     <section class="flexContainer">
-      <div class="patchFiles">
+      <div class="patchFiles" hidden="[[_computeHidePatchFile(change, patchNum)]]" hidden>
         <label>Patch file</label>
         <div>
           <a
diff --git a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.js b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.js
index 5fc81e8..4fe6d8f 100644
--- a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.js
+++ b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog.js
@@ -138,6 +138,17 @@
       return shortRev + '.diff.' + (opt_zip ? 'zip' : 'base64');
     },
 
+    _computeHidePatchFile(change, patchNum) {
+      for (const rev of Object.values(change.revisions || {})) {
+        if (this.patchNumEquals(rev._number, patchNum)) {
+          const parentLength = rev.commit && rev.commit.parents ?
+                rev.commit.parents.length : 0;
+          return parentLength == 0;
+        }
+      }
+      return false;
+    },
+
     _computeArchiveDownloadLink(change, patchNum, format) {
       return this.changeBaseURL(change.project, change._number, patchNum) +
           '/archive?format=' + format;
diff --git a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html
index 2915e29..ee284b9 100644
--- a/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html
+++ b/polygerrit-ui/app/elements/change/gr-download-dialog/gr-download-dialog_test.html
@@ -45,6 +45,9 @@
       revisions: {
         '34685798fe548b6d17d1e8e5edc43a26d055cc72': {
           _number: 1,
+          commit: {
+            parents: [],
+          },
           fetch: {
             repo: {
               commands: {
@@ -105,6 +108,9 @@
       revisions: {
         '34685798fe548b6d17d1e8e5edc43a26d055cc72': {
           _number: 1,
+          commit: {
+            parents: [],
+          },
           fetch: {},
         },
       },
@@ -188,5 +194,25 @@
       assert.equal(element._computeShowDownloadCommands([]), 'hidden');
       assert.equal(element._computeShowDownloadCommands(['test']), '');
     });
+
+    test('_computeHidePatchFile', () => {
+      const patchNum = '1';
+
+      const change1 = {
+        revisions: {
+          r1: {_number: 1, commit: {parents: []}},
+        },
+      };
+      assert.isTrue(element._computeHidePatchFile(change1, patchNum));
+
+      const change2 = {
+        revisions: {
+          r1: {_number: 1, commit: {parents: [
+            {commit: 'p1'},
+          ]}},
+        },
+      };
+      assert.isFalse(element._computeHidePatchFile(change2, patchNum));
+    });
   });
 </script>
diff --git a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html
index 1a307f7..f7d90eb 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.html
@@ -28,7 +28,6 @@
 <link rel="import" href="../../shared/gr-select/gr-select.html">
 <link rel="import" href="../../shared/gr-button/gr-button.html">
 <link rel="import" href="../../shared/gr-icons/gr-icons.html">
-<link rel="import" href="../../shared/revision-info/revision-info.html">
 <link rel="import" href="../gr-file-list-constants.html">
 
 <dom-module id="gr-file-list-header">
@@ -167,7 +166,7 @@
               base-patch-num="[[basePatchNum]]"
               available-patches="[[allPatchSets]]"
               revisions="[[change.revisions]]"
-              revision-info="[[_revisionInfo]]"
+              revision-info="[[revisionInfo]]"
               on-patch-range-change="_handlePatchChange">
           </gr-patch-range-select>
           <span class="separator"></span>
diff --git a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js
index 031f2d5..7b79a63 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list-header/gr-file-list-header.js
@@ -89,10 +89,7 @@
         type: Boolean,
         computed: '_computeDescriptionReadOnly(loggedIn, change, account)',
       },
-      _revisionInfo: {
-        type: Object,
-        computed: '_getRevisionInfo(change)',
-      },
+      revisionInfo: Object,
     },
 
     behaviors: [
@@ -234,10 +231,6 @@
       return 'patchInfoOldPatchSet';
     },
 
-    _getRevisionInfo(change) {
-      return new Gerrit.RevisionInfo(change);
-    },
-
     _hideIncludedIn(change) {
       return change && change.status === MERGED_STATUS ? '' : 'hide';
     },
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
index 4b6ec4c..29a12df 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.html
@@ -23,8 +23,9 @@
 <link rel="import" href="../../../styles/shared-styles.html">
 <link rel="import" href="../../core/gr-navigation/gr-navigation.html">
 <link rel="import" href="../../core/gr-reporting/gr-reporting.html">
-<link rel="import" href="../../diff/gr-diff-host/gr-diff-host.html">
 <link rel="import" href="../../diff/gr-diff-cursor/gr-diff-cursor.html">
+<link rel="import" href="../../diff/gr-diff-host/gr-diff-host.html">
+<link rel="import" href="../../diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.html">
 <link rel="import" href="../../edit/gr-edit-file-controls/gr-edit-file-controls.html">
 <link rel="import" href="../../shared/gr-button/gr-button.html">
 <link rel="import" href="../../shared/gr-cursor-manager/gr-cursor-manager.html">
@@ -464,10 +465,11 @@
         </gr-button><!--
   --></gr-tooltip-content>
     </div>
-    <gr-diff-preferences
-        id="diffPreferences"
-        prefs="{{diffPrefs}}"
-        local-prefs="{{_localPrefs}}"></gr-diff-preferences>
+    <gr-diff-preferences-dialog
+        id="diffPreferencesDialog"
+        diff-prefs="{{diffPrefs}}"
+        on-reload-diff-preference="_handleReloadingDiffPreference">
+    </gr-diff-preferences-dialog>
     <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
     <gr-storage id="storage"></gr-storage>
     <gr-diff-cursor id="diffCursor"></gr-diff-cursor>
diff --git a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
index 8bab9d0..460512a 100644
--- a/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
+++ b/polygerrit-ui/app/elements/change/gr-file-list/gr-file-list.js
@@ -125,7 +125,6 @@
       },
       /** @type {?} */
       _userPrefs: Object,
-      _localPrefs: Object,
       _showInlineDiffs: Boolean,
       numFilesShown: {
         type: Number,
@@ -269,7 +268,6 @@
         });
       }));
 
-      this._localPrefs = this.$.storage.getPreferences();
       promises.push(this._getDiffPreferences().then(prefs => {
         this.diffPrefs = prefs;
       }));
@@ -297,7 +295,7 @@
     },
 
     openDiffPrefs() {
-      this.$.diffPreferences.open();
+      this.$.diffPreferencesDialog.open();
     },
 
     _calculatePatchChange(files) {
@@ -1255,5 +1253,11 @@
 
       return 'Mark as reviewed (shortcut: r)';
     },
+
+    _handleReloadingDiffPreference() {
+      this._getDiffPreferences().then(prefs => {
+        this.diffPrefs = prefs;
+      });
+    },
   });
 })();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.html b/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.html
new file mode 100644
index 0000000..ae53f76
--- /dev/null
+++ b/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.html
@@ -0,0 +1,80 @@
+<!--
+@license
+Copyright (C) 2019 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.
+-->
+
+<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="../../../styles/shared-styles.html">
+<link rel="import" href="../../shared/gr-button/gr-button.html">
+<link rel="import" href="../../shared/gr-diff-preferences/gr-diff-preferences.html">
+<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
+
+<dom-module id="gr-diff-preferences-dialog">
+  <template>
+    <style include="shared-styles">
+      .diffHeader,
+      .diffActions {
+        padding: 1em 1.5em;
+      }
+      .diffHeader,
+      .diffActions {
+        background-color: var(--dialog-background-color);
+      }
+      .diffHeader {
+        border-bottom: 1px solid var(--border-color);
+        font-weight: var(--font-weight-bold);
+      }
+      .diffActions {
+        border-top: 1px solid var(--border-color);
+        display: flex;
+        justify-content: flex-end;
+      }
+      .diffPrefsOverlay gr-button {
+        margin-left: 1em;
+      }
+      div.edited:after {
+        color: var(--deemphasized-text-color);
+        content: ' *';
+      }
+      #diffPreferences {
+        display: flex;
+        padding: .35em 1.5em;
+      }
+    </style>
+    <gr-overlay id="diffPrefsOverlay" with-backdrop>
+      <div class$="diffHeader [[_computeHeaderClass(_diffPrefsChanged)]]">Diff Preferences</div>
+      <gr-diff-preferences
+          id="diffPreferences"
+          diff-prefs="{{diffPrefs}}"
+          has-unsaved-changes="{{_diffPrefsChanged}}"></gr-diff-preferences>
+      <div class="diffActions">
+        <gr-button
+            id="cancelButton"
+            link
+            on-tap="_handleCancelDiff">
+            Cancel
+        </gr-button>
+        <gr-button
+            id="saveButton"
+            link primary
+            on-tap="_handleSaveDiffPreferences"
+            disabled$="[[!_diffPrefsChanged]]">
+            Save
+        </gr-button>
+      </div>
+    </gr-overlay>
+  </template>
+  <script src="gr-diff-preferences-dialog.js"></script>
+</dom-module>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.js b/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.js
new file mode 100644
index 0000000..b50ef69
--- /dev/null
+++ b/polygerrit-ui/app/elements/diff/gr-diff-preferences-dialog/gr-diff-preferences-dialog.js
@@ -0,0 +1,66 @@
+/**
+ * @license
+ * Copyright (C) 2019 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.
+ */
+(function() {
+  'use strict';
+
+  Polymer({
+    is: 'gr-diff-preferences-dialog',
+
+    properties: {
+      /** @type {?} */
+      diffPrefs: Object,
+
+      _diffPrefsChanged: Boolean,
+    },
+
+    getFocusStops() {
+      return {
+        start: this.$.contextSelect,
+        end: this.$.saveButton,
+      };
+    },
+
+    resetFocus() {
+      this.$.contextSelect.focus();
+    },
+
+    _computeHeaderClass(changed) {
+      return changed ? 'edited' : '';
+    },
+
+    _handleCancelDiff(e) {
+      e.stopPropagation();
+      this.$.diffPrefsOverlay.close();
+    },
+
+    open() {
+      this.$.diffPrefsOverlay.open().then(() => {
+        const focusStops = this.getFocusStops();
+        this.$.diffPrefsOverlay.setFocusStops(focusStops);
+        this.resetFocus();
+      });
+    },
+
+    _handleSaveDiffPreferences() {
+      this.$.diffPreferences.save().then(() => {
+        this.fire('reload-diff-preference', null, {bubbles: false});
+
+        this.$.diffPrefsOverlay.close();
+      });
+    },
+  });
+})();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences.html b/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences.html
deleted file mode 100644
index a22f689..0000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences.html
+++ /dev/null
@@ -1,173 +0,0 @@
-<!--
-@license
-Copyright (C) 2016 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.
--->
-
-<link rel="import" href="../../../bower_components/polymer/polymer.html">
-<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
-<link rel="import" href="../../shared/gr-button/gr-button.html">
-<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
-<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
-<link rel="import" href="../../shared/gr-storage/gr-storage.html">
-<link rel="import" href="../../../styles/shared-styles.html">
-
-<dom-module id="gr-diff-preferences">
-  <template>
-    <style include="shared-styles">
-      :host {
-        display: block;
-      }
-      :host([disabled]) {
-        opacity: .5;
-        pointer-events: none;
-      }
-      input,
-      select {
-        font: inherit;
-      }
-      input[type="number"] {
-        width: 4em;
-      }
-      .header,
-      .actions {
-        padding: 1em 1.5em;
-      }
-      .header,
-      .mainContainer,
-      .actions {
-        background-color: var(--dialog-background-color);
-      }
-      .header {
-        border-bottom: 1px solid var(--border-color);
-        font-weight: var(--font-weight-bold);
-      }
-      .mainContainer {
-        padding: 1em 0;
-      }
-      .pref {
-        align-items: center;
-        display: flex;
-        padding: .35em 1.5em;
-        width: 25em;
-      }
-      .pref:hover {
-        background-color: var(--hover-background-color);
-      }
-      .pref label {
-        cursor: pointer;
-        flex: 1;
-      }
-      .actions {
-        border-top: 1px solid var(--border-color);
-        display: flex;
-        justify-content: flex-end;
-      }
-      gr-button {
-        margin-left: 1em;
-      }
-    </style>
-    <gr-overlay id="prefsOverlay" with-backdrop>
-      <div class="header">
-        Diff View Preferences
-      </div>
-      <div class="mainContainer">
-        <div class="pref">
-          <label for="contextSelect">Context</label>
-          <select id="contextSelect" on-change="_handleContextSelectChange">
-            <option value="3">3 lines</option>
-            <option value="10">10 lines</option>
-            <option value="25">25 lines</option>
-            <option value="50">50 lines</option>
-            <option value="75">75 lines</option>
-            <option value="100">100 lines</option>
-            <option value="-1">Whole file</option>
-          </select>
-        </div>
-        <div class="pref">
-          <label for="lineWrappingInput">Fit to screen</label>
-          <input
-              is="iron-input"
-              type="checkbox"
-              id="lineWrappingInput"
-              on-tap="_handlelineWrappingTap">
-        </div>
-        <div class="pref" id="columnsPref">
-          <label for="columnsInput">Diff width</label>
-          <input is="iron-input" type="number" id="columnsInput"
-              prevent-invalid-input
-              allowed-pattern="[0-9]"
-              bind-value="{{_newPrefs.line_length}}">
-        </div>
-        <div class="pref">
-          <label for="tabSizeInput">Tab width</label>
-          <input is="iron-input" type="number" id="tabSizeInput"
-              prevent-invalid-input
-              allowed-pattern="[0-9]"
-              bind-value="{{_newPrefs.tab_size}}">
-        </div>
-        <div class="pref" hidden$="[[!_newPrefs.font_size]]">
-          <label for="fontSizeInput">Font size</label>
-          <input is="iron-input" type="number" id="fontSizeInput"
-                prevent-invalid-input
-                allowed-pattern="[0-9]"
-                bind-value="{{_newPrefs.font_size}}">
-        </div>
-        <div class="pref">
-          <label for="showTabsInput">Show tabs</label>
-          <input is="iron-input" type="checkbox" id="showTabsInput"
-              on-tap="_handleShowTabsTap">
-        </div>
-        <div class="pref">
-          <label for="showTrailingWhitespaceInput">
-            Show trailing whitespace</label>
-          <input is="iron-input" type="checkbox"
-              id="showTrailingWhitespaceInput"
-              on-tap="_handleShowTrailingWhitespaceTap">
-        </div>
-        <div class="pref">
-          <label for="syntaxHighlightInput">Syntax highlighting</label>
-          <input is="iron-input" type="checkbox" id="syntaxHighlightInput"
-              on-tap="_handleSyntaxHighlightTap">
-        </div>
-        <div class="pref">
-          <label for="automaticReviewInput">Automatically mark viewed files reviewed</label>
-          <input
-              is="iron-input"
-              id="automaticReviewInput"
-              type="checkbox"
-              on-tap="_handleAutomaticReviewTap">
-        </div>
-        <div class="pref">
-          <label for="ignoreWhitespace">Ignore Whitespace</label>
-          <select id="ignoreWhitespace" on-change="_handleIgnoreWhitespaceChange">
-            <option value="IGNORE_NONE">None</option>
-            <option value="IGNORE_TRAILING">Trailing</option>
-            <option value="IGNORE_LEADING_AND_TRAILING">Leading & trailing</option>
-            <option value="IGNORE_ALL">All</option>
-          </select>
-        </div>
-      </div>
-      <div class="actions">
-        <gr-button id="cancelButton" link on-tap="_handleCancel">
-            Cancel</gr-button>
-        <gr-button id="saveButton" link primary on-tap="_handleSave">
-            Save</gr-button>
-      </div>
-    </gr-overlay>
-    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
-    <gr-storage id="storage"></gr-storage>
-  </template>
-  <script src="gr-diff-preferences.js"></script>
-</dom-module>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences.js b/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences.js
deleted file mode 100644
index 8fc90b9..0000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences.js
+++ /dev/null
@@ -1,144 +0,0 @@
-/**
- * @license
- * Copyright (C) 2016 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.
- */
-(function() {
-  'use strict';
-
-  Polymer({
-    is: 'gr-diff-preferences',
-
-    properties: {
-      prefs: {
-        type: Object,
-        notify: true,
-      },
-      localPrefs: {
-        type: Object,
-        notify: true,
-      },
-      disabled: {
-        type: Boolean,
-        value: false,
-        reflectToAttribute: true,
-      },
-
-      /** @type {?} */
-      _newPrefs: Object,
-      _newLocalPrefs: Object,
-    },
-
-    observers: [
-      '_prefsChanged(prefs.*)',
-      '_localPrefsChanged(localPrefs.*)',
-    ],
-
-    getFocusStops() {
-      return {
-        start: this.$.contextSelect,
-        end: this.$.saveButton,
-      };
-    },
-
-    resetFocus() {
-      this.$.contextSelect.focus();
-    },
-
-    _prefsChanged(changeRecord) {
-      const prefs = changeRecord.base;
-      // NOTE: Object.assign is NOT automatically a deep copy. If prefs adds
-      // an object as a value, it must be marked enumerable.
-      this._newPrefs = Object.assign({}, prefs);
-      this.$.contextSelect.value = prefs.context;
-      this.$.showTabsInput.checked = prefs.show_tabs;
-      this.$.showTrailingWhitespaceInput.checked = prefs.show_whitespace_errors;
-      this.$.lineWrappingInput.checked = prefs.line_wrapping;
-      this.$.syntaxHighlightInput.checked = prefs.syntax_highlighting;
-      this.$.automaticReviewInput.checked = !prefs.manual_review;
-      this.$.ignoreWhitespace.value = prefs.ignore_whitespace;
-    },
-
-    _localPrefsChanged(changeRecord) {
-      const localPrefs = changeRecord.base || {};
-      this._newLocalPrefs = Object.assign({}, localPrefs);
-    },
-
-    _handleContextSelectChange(e) {
-      const selectEl = Polymer.dom(e).rootTarget;
-      this.set('_newPrefs.context', parseInt(selectEl.value, 10));
-    },
-
-    _handleIgnoreWhitespaceChange(e) {
-      const selectEl = Polymer.dom(e).rootTarget;
-      this.set('_newPrefs.ignore_whitespace', selectEl.value);
-    },
-
-    _handleShowTabsTap(e) {
-      this.set('_newPrefs.show_tabs', Polymer.dom(e).rootTarget.checked);
-    },
-
-    _handleShowTrailingWhitespaceTap(e) {
-      this.set('_newPrefs.show_whitespace_errors',
-          Polymer.dom(e).rootTarget.checked);
-    },
-
-    _handleSyntaxHighlightTap(e) {
-      this.set('_newPrefs.syntax_highlighting',
-          Polymer.dom(e).rootTarget.checked);
-    },
-
-    _handlelineWrappingTap(e) {
-      this.set('_newPrefs.line_wrapping', Polymer.dom(e).rootTarget.checked);
-    },
-
-    _handleAutomaticReviewTap(e) {
-      this.set('_newPrefs.manual_review', !Polymer.dom(e).rootTarget.checked);
-    },
-
-    _handleSave(e) {
-      e.stopPropagation();
-      this.prefs = this._newPrefs;
-      this.localPrefs = this._newLocalPrefs;
-      const el = Polymer.dom(e).rootTarget;
-      el.disabled = true;
-      this.$.storage.savePreferences(this._localPrefs);
-      this._saveDiffPreferences().then(response => {
-        el.disabled = false;
-        if (!response.ok) { return response; }
-
-        this.$.prefsOverlay.close();
-      }).catch(err => {
-        el.disabled = false;
-      });
-    },
-
-    _handleCancel(e) {
-      e.stopPropagation();
-      this.$.prefsOverlay.close();
-    },
-
-    open() {
-      this.$.prefsOverlay.open().then(() => {
-        const focusStops = this.getFocusStops();
-        this.$.prefsOverlay.setFocusStops(focusStops);
-        this.resetFocus();
-      });
-    },
-
-    _saveDiffPreferences() {
-      return this.$.restAPI.saveDiffPreferences(this.prefs);
-    },
-  });
-})();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences_test.html b/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences_test.html
deleted file mode 100644
index d9e14c0..0000000
--- a/polygerrit-ui/app/elements/diff/gr-diff-preferences/gr-diff-preferences_test.html
+++ /dev/null
@@ -1,110 +0,0 @@
-<!DOCTYPE html>
-<!--
-@license
-Copyright (C) 2016 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.
--->
-
-<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
-<title>gr-diff-preferences</title>
-
-<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
-<script src="../../../bower_components/web-component-tester/browser.js"></script>
-<link rel="import" href="../../../test/common-test-setup.html"/>
-<link rel="import" href="gr-diff-preferences.html">
-
-<script>void(0);</script>
-
-<test-fixture id="basic">
-  <template>
-    <gr-diff-preferences></gr-diff-preferences>
-  </template>
-</test-fixture>
-
-<script>
-  suite('gr-diff-preferences tests', () => {
-    let element;
-    let sandbox;
-
-    setup(() => {
-      sandbox = sinon.sandbox.create();
-      element = fixture('basic');
-    });
-
-    teardown(() => {
-      sandbox.restore();
-    });
-
-    test('model changes', () => {
-      element.prefs = {
-        context: 10,
-        font_size: 12,
-        line_length: 100,
-        show_tabs: true,
-        tab_size: 8,
-        show_whitespace_errors: true,
-        syntax_highlighting: true,
-      };
-      assert.deepEqual(element.prefs, element._newPrefs);
-
-      element.$.contextSelect.value = '50';
-      element.fire('change', {}, {node: element.$.contextSelect});
-      element.$.columnsInput.bindValue = 80;
-      element.$.fontSizeInput.bindValue = 10;
-      element.$.tabSizeInput.bindValue = 4;
-      MockInteractions.tap(element.$.showTabsInput);
-      MockInteractions.tap(element.$.showTrailingWhitespaceInput);
-      MockInteractions.tap(element.$.syntaxHighlightInput);
-      MockInteractions.tap(element.$.lineWrappingInput);
-
-      assert.equal(element._newPrefs.context, 50);
-      assert.equal(element._newPrefs.font_size, 10);
-      assert.equal(element._newPrefs.line_length, 80);
-      assert.equal(element._newPrefs.tab_size, 4);
-      assert.isFalse(element._newPrefs.show_tabs);
-      assert.isFalse(element._newPrefs.show_whitespace_errors);
-      assert.isTrue(element._newPrefs.line_wrapping);
-      assert.isFalse(element._newPrefs.syntax_highlighting);
-    });
-
-    test('clicking save button calls _handleSave function', () => {
-      const savePrefs = sinon.stub(element, '_handleSave');
-      MockInteractions.tap(element.$.saveButton);
-      flushAsynchronousOperations();
-      assert(savePrefs.calledOnce);
-      savePrefs.restore();
-    });
-
-    test('save button', () => {
-      element.prefs = {
-        font_size: '11',
-      };
-      element._newPrefs = {
-        font_size: '12',
-      };
-      const saveStub = sandbox.stub(element.$.restAPI, 'saveDiffPreferences',
-          () => { return Promise.resolve(); });
-
-      MockInteractions.tap(element.$$('gr-button[primary]'));
-      assert.deepEqual(element.prefs, element._newPrefs);
-      assert.deepEqual(saveStub.lastCall.args[0], element._newPrefs);
-    });
-
-    test('cancel button', () => {
-      const closeStub = sandbox.stub(element.$.prefsOverlay, 'close');
-      MockInteractions.tap(element.$$('gr-button:not([primary])'));
-      assert.isTrue(closeStub.called);
-    });
-  });
-</script>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html
index b3210cc..57525e1 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.html
@@ -34,9 +34,9 @@
 <link rel="import" href="../../shared/revision-info/revision-info.html">
 <link rel="import" href="../gr-comment-api/gr-comment-api.html">
 <link rel="import" href="../gr-diff-cursor/gr-diff-cursor.html">
-<link rel="import" href="../gr-diff-mode-selector/gr-diff-mode-selector.html">
-<link rel="import" href="../gr-diff-preferences/gr-diff-preferences.html">
 <link rel="import" href="../gr-diff-host/gr-diff-host.html">
+<link rel="import" href="../gr-diff-mode-selector/gr-diff-mode-selector.html">
+<link rel="import" href="../gr-diff-preferences-dialog/gr-diff-preferences-dialog.html">
 <link rel="import" href="../gr-patch-range-select/gr-patch-range-select.html">
 
 <dom-module id="gr-diff-view">
@@ -338,10 +338,11 @@
         on-comment-anchor-tap="_onLineSelected"
         on-line-selected="_onLineSelected">
     </gr-diff-host>
-    <gr-diff-preferences
-        id="diffPreferences"
-        prefs="{{_prefs}}"
-        local-prefs="{{_localPrefs}}"></gr-diff-preferences>
+    <gr-diff-preferences-dialog
+        id="diffPreferencesDialog"
+        diff-prefs="{{_prefs}}"
+        on-reload-diff-preference="_handleReloadingDiffPreference">
+    </gr-diff-preferences-dialog>
     <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
     <gr-storage id="storage"></gr-storage>
     <gr-diff-cursor id="cursor"></gr-diff-cursor>
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
index dadf8a7..c8b3c4b 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view.js
@@ -266,7 +266,9 @@
     },
 
     _getDiffPreferences() {
-      return this.$.restAPI.getDiffPreferences();
+      return this.$.restAPI.getDiffPreferences().then(prefs => {
+        this._prefs = prefs;
+      });
     },
 
     _getPreferences() {
@@ -466,7 +468,7 @@
       if (this._diffPrefsDisabled) { return; }
 
       e.preventDefault();
-      this.$.diffPreferences.open();
+      this.$.diffPreferencesDialog.open();
     },
 
     _handleToggleDiffMode(e) {
@@ -617,10 +619,7 @@
 
       const promises = [];
 
-      this._localPrefs = this.$.storage.getPreferences();
-      promises.push(this._getDiffPreferences().then(prefs => {
-        this._prefs = prefs;
-      }));
+      promises.push(this._getDiffPreferences());
 
       promises.push(this._getPreferences().then(prefs => {
         this._userPrefs = prefs;
@@ -846,22 +845,7 @@
 
     _handlePrefsTap(e) {
       e.preventDefault();
-      this.$.diffPreferences.open();
-    },
-
-    _handlePrefsSave(e) {
-      e.stopPropagation();
-      const el = Polymer.dom(e).rootTarget;
-      el.disabled = true;
-      this.$.storage.savePreferences(this._localPrefs);
-      this._saveDiffPreferences().then(response => {
-        el.disabled = false;
-        if (!response.ok) { return response; }
-
-        this.$.prefsOverlay.close();
-      }).catch(err => {
-        el.disabled = false;
-      });
+      this.$.diffPreferencesDialog.open();
     },
 
     /**
@@ -1049,5 +1033,9 @@
           (file === this._path || !this._reviewedFiles.has(file)));
       this._navToFile(this._path, unreviewedFiles, 1);
     },
+
+    _handleReloadingDiffPreference() {
+      this._getDiffPreferences();
+    },
   });
 })();
diff --git a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html
index 0274330..c4d6c95 100644
--- a/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html
+++ b/polygerrit-ui/app/elements/diff/gr-diff-view/gr-diff-view_test.html
@@ -173,7 +173,7 @@
       assert.isTrue(element._loading);
 
       const showPrefsStub =
-          sandbox.stub(element.$.diffPreferences.$.prefsOverlay, 'open',
+          sandbox.stub(element.$.diffPreferencesDialog, 'open',
               () => Promise.resolve());
 
       MockInteractions.pressAndReleaseKeyOn(element, 188, null, ',');
@@ -386,7 +386,7 @@
 
     test('prefsButton opens gr-diff-preferences', () => {
       const handlePrefsTapSpy = sandbox.spy(element, '_handlePrefsTap');
-      const overlayOpenStub = sandbox.stub(element.$.diffPreferences,
+      const overlayOpenStub = sandbox.stub(element.$.diffPreferencesDialog,
           'open');
       const prefsButton =
           Polymer.dom(element.root).querySelector('.prefsButton');
diff --git a/polygerrit-ui/app/elements/gr-app.html b/polygerrit-ui/app/elements/gr-app.html
index 533136c..0301a13 100644
--- a/polygerrit-ui/app/elements/gr-app.html
+++ b/polygerrit-ui/app/elements/gr-app.html
@@ -42,6 +42,7 @@
     safeTypesBridge: Gerrit.SafeTypes.safeTypesBridge,
   });
 </script>
+<script src="../bower_components/moment/moment.js"></script>
 
 <link rel="import" href="../behaviors/base-url-behavior/base-url-behavior.html">
 <link rel="import" href="../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html
index 799cf59..3c4270f 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.html
@@ -27,6 +27,7 @@
 <link rel="import" href="../../settings/gr-change-table-editor/gr-change-table-editor.html">
 <link rel="import" href="../../shared/gr-button/gr-button.html">
 <link rel="import" href="../../shared/gr-date-formatter/gr-date-formatter.html">
+<link rel="import" href="../../shared/gr-diff-preferences/gr-diff-preferences.html">
 <link rel="import" href="../../shared/gr-page-nav/gr-page-nav.html">
 <link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
 <link rel="import" href="../../shared/gr-select/gr-select.html">
@@ -194,6 +195,18 @@
               </gr-select>
             </span>
           </section>
+          <section hidden$="[[!_localPrefs.default_base_for_merges]]">
+            <span class="title">Default Base For Merges</span>
+            <span class="value">
+              <gr-select
+                  bind-value="{{_localPrefs.default_base_for_merges}}">
+                <select>
+                  <option value="AUTO_MERGE">Auto Merge</option>
+                  <option value="FIRST_PARENT">First Parent</option>
+                </select>
+              </gr-select>
+            </span>
+          </section>
           <section>
             <span class="title">Diff view</span>
             <span class="value">
@@ -259,110 +272,9 @@
           Diff Preferences
         </h2>
         <fieldset id="diffPreferences">
-          <section>
-            <span class="title">Context</span>
-            <span class="value">
-              <gr-select bind-value="{{_diffPrefs.context}}">
-                <select>
-                  <option value="3">3 lines</option>
-                  <option value="10">10 lines</option>
-                  <option value="25">25 lines</option>
-                  <option value="50">50 lines</option>
-                  <option value="75">75 lines</option>
-                  <option value="100">100 lines</option>
-                  <option value="-1">Whole file</option>
-                </select>
-              </gr-select>
-            </span>
-          </section>
-          <section>
-            <span class="title">Fit to screen</span>
-            <span class="value">
-              <input
-                  id="diffLineWrapping"
-                  type="checkbox"
-                  checked$="[[_diffPrefs.line_wrapping]]"
-                  on-change="_handleDiffLineWrappingChanged">
-            </span>
-          </section>
-          <section id="columnsPref" hidden$="[[_diffPrefs.line_wrapping]]">
-            <span class="title">Diff width</span>
-            <span class="value">
-              <input
-                  is="iron-input"
-                  type="number"
-                  prevent-invalid-input
-                  allowed-pattern="[0-9]"
-                  bind-value="{{_diffPrefs.line_length}}">
-            </span>
-          </section>
-          <section>
-            <span class="title">Tab width</span>
-            <span class="value">
-              <input
-                  is="iron-input"
-                  type="number"
-                  prevent-invalid-input
-                  allowed-pattern="[0-9]"
-                  bind-value="{{_diffPrefs.tab_size}}">
-            </span>
-          </section>
-          <section hidden$="[[!_diffPrefs.font_size]]">
-            <span class="title">Font size</span>
-            <span class="value">
-              <input
-                  is="iron-input"
-                  type="number"
-                  prevent-invalid-input
-                  allowed-pattern="[0-9]"
-                  bind-value="{{_diffPrefs.font_size}}">
-            </span>
-          </section>
-          <section>
-            <span class="title">Show tabs</span>
-            <span class="value">
-              <input
-                  id="diffShowTabs"
-                  type="checkbox"
-                  checked$="[[_diffPrefs.show_tabs]]"
-                  on-change="_handleDiffShowTabsChanged">
-            </span>
-          </section>
-          <section>
-            <span class="title">Show trailing whitespace</span>
-            <span class="value">
-              <input
-                  id="showTrailingWhitespace"
-                  type="checkbox"
-                  checked$="[[_diffPrefs.show_whitespace_errors]]"
-                  on-change="_handleShowTrailingWhitespaceChanged">
-            </span>
-          </section>
-          <section>
-            <span class="title">Syntax highlighting</span>
-            <span class="value">
-              <input
-                  id="diffSyntaxHighlighting"
-                  type="checkbox"
-                  checked$="[[_diffPrefs.syntax_highlighting]]"
-                  on-change="_handleDiffSyntaxHighlightingChanged">
-            </span>
-          </section>
-          <section>
-            <div class="pref">
-              <span class="title">Ignore Whitespace</span>
-              <span class="value">
-                <gr-select bind-value="{{_diffPrefs.ignore_whitespace}}">
-                  <select>
-                    <option value="IGNORE_NONE">None</option>
-                    <option value="IGNORE_TRAILING">Trailing</option>
-                    <option value="IGNORE_LEADING_AND_TRAILING">Leading & trailing</option>
-                    <option value="IGNORE_ALL">All</option>
-                  </select>
-                </gr-select>
-              </span>
-            </div>
-          </section>
+          <gr-diff-preferences
+              id="diffPrefs"
+              has-unsaved-changes="{{_diffPrefsChanged}}"></gr-diff-preferences>
           <gr-button
               id="saveDiffPrefs"
               on-tap="_handleSaveDiffPreferences"
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js
index 0ce8ce0..916f97f 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view.js
@@ -25,6 +25,7 @@
     'diff_view',
     'publish_comments_on_push',
     'work_in_progress_by_default',
+    'default_base_for_merges',
     'signed_off_by',
     'email_format',
     'size_bar_in_change_table',
@@ -64,8 +65,6 @@
       },
       _accountNameMutable: Boolean,
       _accountInfoChanged: Boolean,
-      /** @type {?} */
-      _diffPrefs: Object,
       _changeTableColumnsNotDisplayed: Array,
       /** @type {?} */
       _localPrefs: {
@@ -92,10 +91,8 @@
         type: Boolean,
         value: false,
       },
-      _diffPrefsChanged: {
-        type: Boolean,
-        value: false,
-      },
+      /** @type {?} */
+      _diffPrefsChanged: Boolean,
       /** @type {?} */
       _editPrefsChanged: Boolean,
       _menuChanged: {
@@ -149,7 +146,6 @@
 
     observers: [
       '_handlePrefsChanged(_localPrefs.*)',
-      '_handleDiffPrefsChanged(_diffPrefs.*)',
       '_handleMenuChanged(_localMenu.splices)',
       '_handleChangeTableChanged(_localChangeTableColumns, _showNumber)',
     ],
@@ -166,6 +162,7 @@
         this.$.httpPass.loadData(),
         this.$.identities.loadData(),
         this.$.editPrefs.loadData(),
+        this.$.diffPrefs.loadData(),
       ];
 
       promises.push(this.$.restAPI.getPreferences().then(prefs => {
@@ -176,10 +173,6 @@
         this._cloneChangeTableColumns();
       }));
 
-      promises.push(this.$.restAPI.getDiffPreferences().then(prefs => {
-        this._diffPrefs = prefs;
-      }));
-
       promises.push(this.$.restAPI.getConfig().then(config => {
         this._serverConfig = config;
         const configPromises = [];
@@ -277,11 +270,6 @@
       this._prefsChanged = true;
     },
 
-    _handleDiffPrefsChanged() {
-      if (this._isLoading()) { return; }
-      this._diffPrefsChanged = true;
-    },
-
     _handleShowSizeBarsInFileListChanged() {
       this.set('_localPrefs.size_bar_in_change_table',
           this.$.showSizeBarsInFileList.checked);
@@ -318,24 +306,6 @@
       });
     },
 
-    _handleDiffLineWrappingChanged() {
-      this.set('_diffPrefs.line_wrapping', this.$.diffLineWrapping.checked);
-    },
-
-    _handleDiffShowTabsChanged() {
-      this.set('_diffPrefs.show_tabs', this.$.diffShowTabs.checked);
-    },
-
-    _handleShowTrailingWhitespaceChanged() {
-      this.set('_diffPrefs.show_whitespace_errors',
-          this.$.showTrailingWhitespace.checked);
-    },
-
-    _handleDiffSyntaxHighlightingChanged() {
-      this.set('_diffPrefs.syntax_highlighting',
-          this.$.diffSyntaxHighlighting.checked);
-    },
-
     _handleSaveChangeTable() {
       this.set('prefs.change_table', this._localChangeTableColumns);
       this.set('prefs.legacycid_in_change_table', this._showNumber);
@@ -346,10 +316,7 @@
     },
 
     _handleSaveDiffPreferences() {
-      return this.$.restAPI.saveDiffPreferences(this._diffPrefs)
-          .then(() => {
-            this._diffPrefsChanged = false;
-          });
+      this.$.diffPrefs.save();
     },
 
     _handleSaveEditPreferences() {
diff --git a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.html b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.html
index f47816f..b0ef704 100644
--- a/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.html
+++ b/polygerrit-ui/app/elements/settings/gr-settings-view/gr-settings-view_test.html
@@ -43,7 +43,6 @@
     let element;
     let account;
     let preferences;
-    let diffPreferences;
     let config;
     let sandbox;
 
@@ -88,6 +87,7 @@
         diff_view: 'UNIFIED_DIFF',
         email_strategy: 'ENABLED',
         email_format: 'HTML_PLAINTEXT',
+        default_base_for_merges: 'FIRST_PARENT',
         size_bar_in_change_table: true,
 
         my: [
@@ -96,31 +96,12 @@
         ],
         change_table: [],
       };
-      diffPreferences = {
-        context: 10,
-        tab_size: 8,
-        font_size: 12,
-        line_length: 100,
-        cursor_blink_rate: 0,
-        line_wrapping: false,
-        intraline_difference: true,
-        show_line_endings: true,
-        show_tabs: true,
-        show_whitespace_errors: true,
-        syntax_highlighting: true,
-        auto_hide_diff_table_header: true,
-        theme: 'DEFAULT',
-        ignore_whitespace: 'IGNORE_NONE',
-      };
       config = {auth: {editable_account_fields: []}};
 
       stub('gr-rest-api-interface', {
         getLoggedIn() { return Promise.resolve(true); },
         getAccount() { return Promise.resolve(account); },
         getPreferences() { return Promise.resolve(preferences); },
-        getDiffPreferences() {
-          return Promise.resolve(diffPreferences);
-        },
         getWatchedProjects() {
           return Promise.resolve([]);
         },
@@ -168,6 +149,8 @@
           .firstElementChild.bindValue, preferences.email_strategy);
       assert.equal(valueOf('Email format', 'preferences')
           .firstElementChild.bindValue, preferences.email_format);
+      assert.equal(valueOf('Default Base For Merges', 'preferences')
+          .firstElementChild.bindValue, preferences.default_base_for_merges);
       assert.equal(valueOf('Diff view', 'preferences')
           .firstElementChild.bindValue, preferences.diff_view);
       assert.equal(valueOf('Show size bars in file list', 'preferences')
@@ -261,56 +244,6 @@
       });
     });
 
-    test('diff preferences', done => {
-      // Rendered with the expected preferences selected.
-      assert.equal(valueOf('Context', 'diffPreferences')
-          .firstElementChild.bindValue, diffPreferences.context);
-      assert.equal(valueOf('Diff width', 'diffPreferences')
-          .firstElementChild.bindValue, diffPreferences.line_length);
-      assert.equal(valueOf('Tab width', 'diffPreferences')
-          .firstElementChild.bindValue, diffPreferences.tab_size);
-      assert.equal(valueOf('Font size', 'diffPreferences')
-          .firstElementChild.bindValue, diffPreferences.font_size);
-      assert.equal(valueOf('Show tabs', 'diffPreferences')
-          .firstElementChild.checked, diffPreferences.show_tabs);
-      assert.equal(valueOf('Show trailing whitespace', 'diffPreferences')
-          .firstElementChild.checked, diffPreferences.show_whitespace_errors);
-      assert.equal(valueOf('Fit to screen', 'diffPreferences')
-          .firstElementChild.checked, diffPreferences.line_wrapping);
-
-      assert.isFalse(element._diffPrefsChanged);
-
-      const showTabsCheckbox = valueOf('Show tabs', 'diffPreferences')
-          .firstElementChild;
-      showTabsCheckbox.checked = false;
-      element._handleDiffShowTabsChanged();
-
-      assert.isTrue(element._diffPrefsChanged);
-
-      stub('gr-rest-api-interface', {
-        saveDiffPreferences(prefs) {
-          assert.equal(prefs.show_tabs, false);
-          return Promise.resolve();
-        },
-      });
-
-      // Save the change.
-      element._handleSaveDiffPreferences().then(() => {
-        assert.isFalse(element._diffPrefsChanged);
-        done();
-      });
-    });
-
-    test('columns input is hidden with fit to scsreen is selected', () => {
-      assert.isFalse(element.$.columnsPref.hidden);
-
-      MockInteractions.tap(element.$.diffLineWrapping);
-      assert.isTrue(element.$.columnsPref.hidden);
-
-      MockInteractions.tap(element.$.diffLineWrapping);
-      assert.isFalse(element.$.columnsPref.hidden);
-    });
-
     test('menu', done => {
       assert.isFalse(element._menuChanged);
       assert.isFalse(element._prefsChanged);
diff --git a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js
index f32e940b..bf56382 100644
--- a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js
+++ b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar.js
@@ -41,25 +41,30 @@
 
     attached() {
       Promise.all([
-        this.$.restAPI.getConfig(),
+        this._getConfig(),
         Gerrit.awaitPluginsLoaded(),
       ]).then(([cfg]) => {
         this._hasAvatars = !!(cfg && cfg.plugin && cfg.plugin.has_avatars);
-        if (this._hasAvatars && this.account) {
-          // src needs to be set if avatar becomes visible
-          this._updateAvatarURL();
-        } else {
-          this.hidden = true;
-        }
+
+        this._updateAvatarURL();
       });
     },
 
+    _getConfig() {
+      return this.$.restAPI.getConfig();
+    },
+
     _accountChanged(account) {
       this._updateAvatarURL();
     },
 
     _updateAvatarURL() {
-      if (this.hidden || !this._hasAvatars) { return; }
+      if (!this._hasAvatars || !this.account) {
+        this.hidden = true;
+        return;
+      }
+      this.hidden = false;
+
       const url = this._buildAvatarURL(this.account);
       if (url) {
         this.style.backgroundImage = 'url("' + url + '")';
diff --git a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html
index f137c7f..5ce17c0 100644
--- a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html
+++ b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html
@@ -35,14 +35,17 @@
 <script>
   suite('gr-avatar tests', () => {
     let element;
+    let sandbox;
 
     setup(() => {
-      stub('gr-rest-api-interface', {
-        getConfig() { return Promise.resolve({plugin: {has_avatars: true}}); },
-      });
+      sandbox = sinon.sandbox.create();
       element = fixture('basic');
     });
 
+    teardown(() => {
+      sandbox.restore();
+    });
+
     test('methods', () => {
       assert.equal(element._buildAvatarURL(
           {
@@ -94,22 +97,32 @@
             ],
           }),
           '/accounts/123/avatar?s=16');
+      assert.equal(element._buildAvatarURL(undefined), '');
     });
 
     test('dom for existing account', () => {
       assert.isFalse(element.hasAttribute('hidden'));
+
+      sandbox.stub(element, '_getConfig', () => {
+        return Promise.resolve({plugin: {has_avatars: true}});
+      });
+
       element.imageSize = 64;
       element.account = {
         _account_id: 123,
       };
+
       assert.strictEqual(element.style.backgroundImage, '');
+
       // Emulate plugins loaded.
       Gerrit._setPluginsPending([]);
-      return Promise.all([
+
+      Promise.all([
         element.$.restAPI.getConfig(),
         Gerrit.awaitPluginsLoaded(),
       ]).then(() => {
         assert.isFalse(element.hasAttribute('hidden'));
+
         assert.isTrue(
             element.style.backgroundImage.includes('/accounts/123/avatar?s=64'));
       });
@@ -117,10 +130,57 @@
 
     test('dom for non available account', () => {
       assert.isFalse(element.hasAttribute('hidden'));
-      element.account = null;
-      assert.isFalse(element.hasAttribute('hidden'));
+
+      sandbox.stub(element, '_getConfig', () => {
+        return Promise.resolve({plugin: {has_avatars: true}});
+      });
+
       // Emulate plugins loaded.
       Gerrit._setPluginsPending([]);
+
+      return Promise.all([
+        element.$.restAPI.getConfig(),
+        Gerrit.awaitPluginsLoaded(),
+      ]).then(() => {
+        assert.isTrue(element.hasAttribute('hidden'));
+
+        assert.strictEqual(element.style.backgroundImage, '');
+      });
+    });
+
+    test('avatar config not set and account not set', () => {
+      assert.isFalse(element.hasAttribute('hidden'));
+
+      sandbox.stub(element, '_getConfig', () => {
+        return Promise.resolve({});
+      });
+
+      // Emulate plugins loaded.
+      Gerrit._setPluginsPending([]);
+
+      return Promise.all([
+        element.$.restAPI.getConfig(),
+        Gerrit.awaitPluginsLoaded(),
+      ]).then(() => {
+        assert.isTrue(element.hasAttribute('hidden'));
+      });
+    });
+
+    test('avatar config not set and account set', () => {
+      assert.isFalse(element.hasAttribute('hidden'));
+
+      sandbox.stub(element, '_getConfig', () => {
+        return Promise.resolve({});
+      });
+
+      element.imageSize = 64;
+      element.account = {
+        _account_id: 123,
+      };
+
+      // Emulate plugins loaded.
+      Gerrit._setPluginsPending([]);
+
       return Promise.all([
         element.$.restAPI.getConfig(),
         Gerrit.awaitPluginsLoaded(),
diff --git a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.html b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.html
index 1090fea..481dd2f 100644
--- a/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.html
+++ b/polygerrit-ui/app/elements/shared/gr-date-formatter/gr-date-formatter.html
@@ -20,7 +20,6 @@
 <link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
 <link rel="import" href="../../../styles/shared-styles.html">
 
-<script src="../../../bower_components/moment/moment.js"></script>
 <script src="../../../scripts/util.js"></script>
 
 <dom-module id="gr-date-formatter">
diff --git a/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.html b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.html
new file mode 100644
index 0000000..d7bc704
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.html
@@ -0,0 +1,163 @@
+<!--
+@license
+Copyright (C) 2016 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.
+-->
+
+<link rel="import" href="../../../bower_components/polymer/polymer.html">
+<link rel="import" href="../../../bower_components/iron-input/iron-input.html">
+<link rel="import" href="../../../styles/shared-styles.html">
+<link rel="import" href="../gr-button/gr-button.html">
+<link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
+<link rel="import" href="../gr-select/gr-select.html">
+
+<dom-module id="gr-diff-preferences">
+  <template>
+    <style include="shared-styles"></style>
+    <style include="gr-form-styles"></style>
+    <div id="diffPreferences" class="gr-form-styles">
+      <section>
+        <span class="title">Context</span>
+        <span class="value">
+          <gr-select
+              id="contextSelect"
+              bind-value="{{diffPrefs.context}}">
+            <select
+                on-keypress="_handleDiffPrefsChanged"
+                on-change="_handleDiffPrefsChanged">
+              <option value="3">3 lines</option>
+              <option value="10">10 lines</option>
+              <option value="25">25 lines</option>
+              <option value="50">50 lines</option>
+              <option value="75">75 lines</option>
+              <option value="100">100 lines</option>
+              <option value="-1">Whole file</option>
+            </select>
+          </gr-select>
+        </span>
+      </section>
+      <section>
+        <span class="title">Fit to screen</span>
+        <span class="value">
+          <input
+              id="lineWrappingInput"
+              type="checkbox"
+              checked$="[[diffPrefs.line_wrapping]]"
+              on-change="_handleLineWrappingTap">
+        </span>
+      </section>
+      <section>
+        <span class="title">Diff width</span>
+        <span class="value">
+          <input
+              is="iron-input"
+              type="number"
+              id="columnsInput"
+              prevent-invalid-input
+              allowed-pattern="[0-9]"
+              bind-value="{{diffPrefs.line_length}}"
+              on-keypress="_handleDiffPrefsChanged"
+              on-change="_handleDiffPrefsChanged">
+        </span>
+      </section>
+      <section>
+        <span class="title">Tab width</span>
+        <span class="value">
+          <input
+              is="iron-input"
+              type="number"
+              id="tabSizeInput"
+              prevent-invalid-input
+              allowed-pattern="[0-9]"
+              bind-value="{{diffPrefs.tab_size}}"
+              on-keypress="_handleDiffPrefsChanged"
+              on-change="_handleDiffPrefsChanged">
+        </span>
+      </section>
+      <section hidden$="[[!diffPrefs.font_size]]">
+        <span class="title">Font size</span>
+        <span class="value">
+          <input
+              is="iron-input"
+              type="number"
+              id="fontSizeInput"
+              prevent-invalid-input
+              allowed-pattern="[0-9]"
+              bind-value="{{diffPrefs.font_size}}"
+              on-keypress="_handleDiffPrefsChanged"
+              on-change="_handleDiffPrefsChanged">
+        </span>
+      </section>
+      <section>
+        <span class="title">Show tabs</span>
+        <span class="value">
+          <input
+              id="showTabsInput"
+              type="checkbox"
+              checked$="[[diffPrefs.show_tabs]]"
+              on-change="_handleShowTabsTap">
+        </span>
+      </section>
+      <section>
+        <span class="title">Show trailing whitespace</span>
+        <span class="value">
+          <input
+              id="showTrailingWhitespaceInput"
+              type="checkbox"
+              checked$="[[diffPrefs.show_whitespace_errors]]"
+              on-change="_handleShowTrailingWhitespaceTap">
+        </span>
+      </section>
+      <section>
+        <span class="title">Syntax highlighting</span>
+        <span class="value">
+          <input
+              id="syntaxHighlightInput"
+              type="checkbox"
+              checked$="[[diffPrefs.syntax_highlighting]]"
+              on-change="_handleSyntaxHighlightTap">
+        </span>
+      </section>
+      <section>
+        <span class="title">Automatically mark viewed files reviewed</span>
+        <span class="value">
+          <input
+              id="automaticReviewInput"
+              type="checkbox"
+              checked$="[[diffPrefs.manual_review]]"
+              on-change="_handleAutomaticReviewTap">
+        </span>
+      </section>
+      <section>
+        <div class="pref">
+          <span class="title">Ignore Whitespace</span>
+          <span class="value">
+            <gr-select bind-value="{{diffPrefs.ignore_whitespace}}">
+              <select
+                  on-keypress="_handleDiffPrefsChanged"
+                  on-change="_handleDiffPrefsChanged">
+                <option value="IGNORE_NONE">None</option>
+                <option value="IGNORE_TRAILING">Trailing</option>
+                <option value="IGNORE_LEADING_AND_TRAILING">Leading & trailing</option>
+                <option value="IGNORE_ALL">All</option>
+              </select>
+            </gr-select>
+          </span>
+        </div>
+      </section>
+    </div>
+    <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+  </template>
+  <script src="gr-diff-preferences.js"></script>
+</dom-module>
diff --git a/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.js b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.js
new file mode 100644
index 0000000..e2278c2
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences.js
@@ -0,0 +1,78 @@
+/**
+ * @license
+ * Copyright (C) 2016 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.
+ */
+(function() {
+  'use strict';
+
+  Polymer({
+    is: 'gr-diff-preferences',
+
+    properties: {
+      hasUnsavedChanges: {
+        type: Boolean,
+        notify: true,
+        value: false,
+      },
+
+      /** @type {?} */
+      diffPrefs: Object,
+    },
+
+    loadData() {
+      return this.$.restAPI.getDiffPreferences().then(prefs => {
+        this.diffPrefs = prefs;
+      });
+    },
+
+    _handleDiffPrefsChanged() {
+      this.hasUnsavedChanges = true;
+    },
+
+    _handleLineWrappingTap() {
+      this.set('diffPrefs.line_wrapping', this.$.lineWrappingInput.checked);
+      this._handleDiffPrefsChanged();
+    },
+
+    _handleShowTabsTap() {
+      this.set('diffPrefs.show_tabs', this.$.showTabsInput.checked);
+      this._handleDiffPrefsChanged();
+    },
+
+    _handleShowTrailingWhitespaceTap() {
+      this.set('diffPrefs.show_whitespace_errors',
+          this.$.showTrailingWhitespaceInput.checked);
+      this._handleDiffPrefsChanged();
+    },
+
+    _handleSyntaxHighlightTap() {
+      this.set('diffPrefs.syntax_highlighting',
+          this.$.syntaxHighlightInput.checked);
+      this._handleDiffPrefsChanged();
+    },
+
+    _handleAutomaticReviewTap() {
+      this.set('diffPrefs.manual_review',
+          this.$.automaticReviewInput.checked);
+      this._handleDiffPrefsChanged();
+    },
+
+    save() {
+      return this.$.restAPI.saveDiffPreferences(this.diffPrefs).then(res => {
+        this.hasUnsavedChanges = false;
+      });
+    },
+  });
+})();
diff --git a/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_test.html b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_test.html
new file mode 100644
index 0000000..511737f
--- /dev/null
+++ b/polygerrit-ui/app/elements/shared/gr-diff-preferences/gr-diff-preferences_test.html
@@ -0,0 +1,123 @@
+<!DOCTYPE html>
+<!--
+@license
+Copyright (C) 2016 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.
+-->
+
+<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
+<title>gr-diff-preferences</title>
+
+<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
+<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+<link rel="import" href="gr-diff-preferences.html">
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+  <template>
+    <gr-diff-preferences></gr-diff-preferences>
+  </template>
+</test-fixture>
+
+<script>
+  suite('gr-diff-preferences tests', () => {
+    let element;
+    let sandbox;
+    let diffPreferences;
+
+    function valueOf(title, fieldsetid) {
+      const sections = element.$[fieldsetid].querySelectorAll('section');
+      let titleEl;
+      for (let i = 0; i < sections.length; i++) {
+        titleEl = sections[i].querySelector('.title');
+        if (titleEl.textContent.trim() === title) {
+          return sections[i].querySelector('.value');
+        }
+      }
+    }
+
+    setup(() => {
+      diffPreferences = {
+        context: 10,
+        line_wrapping: false,
+        line_length: 100,
+        tab_size: 8,
+        font_size: 12,
+        show_tabs: true,
+        show_whitespace_errors: true,
+        syntax_highlighting: true,
+        manual_review: false,
+        ignore_whitespace: 'IGNORE_NONE',
+      };
+
+      stub('gr-rest-api-interface', {
+        getDiffPreferences() {
+          return Promise.resolve(diffPreferences);
+        },
+      });
+
+      element = fixture('basic');
+      sandbox = sinon.sandbox.create();
+      return element.loadData();
+    });
+
+    teardown(() => { sandbox.restore(); });
+
+    test('renders', () => {
+      // Rendered with the expected preferences selected.
+      assert.equal(valueOf('Context', 'diffPreferences')
+          .firstElementChild.bindValue, diffPreferences.context);
+      assert.equal(valueOf('Fit to screen', 'diffPreferences')
+          .firstElementChild.checked, diffPreferences.line_wrapping);
+      assert.equal(valueOf('Diff width', 'diffPreferences')
+          .firstElementChild.bindValue, diffPreferences.line_length);
+      assert.equal(valueOf('Tab width', 'diffPreferences')
+          .firstElementChild.bindValue, diffPreferences.tab_size);
+      assert.equal(valueOf('Font size', 'diffPreferences')
+          .firstElementChild.bindValue, diffPreferences.font_size);
+      assert.equal(valueOf('Show tabs', 'diffPreferences')
+          .firstElementChild.checked, diffPreferences.show_tabs);
+      assert.equal(valueOf('Show trailing whitespace', 'diffPreferences')
+          .firstElementChild.checked, diffPreferences.show_whitespace_errors);
+      assert.equal(valueOf('Syntax highlighting', 'diffPreferences')
+          .firstElementChild.checked, diffPreferences.syntax_highlighting);
+      assert.equal(
+          valueOf('Automatically mark viewed files reviewed', 'diffPreferences')
+              .firstElementChild.checked, diffPreferences.manual_review);
+      assert.equal(valueOf('Ignore Whitespace', 'diffPreferences')
+          .firstElementChild.bindValue, diffPreferences.ignore_whitespace);
+
+      assert.isFalse(element.hasUnsavedChanges);
+    });
+
+    test('save changes', () => {
+      sandbox.stub(element.$.restAPI, 'saveDiffPreferences')
+          .returns(Promise.resolve());
+      const showTrailingWhitespaceCheckbox =
+          valueOf('Show trailing whitespace', 'diffPreferences')
+          .firstElementChild;
+      showTrailingWhitespaceCheckbox.checked = false;
+      element._handleShowTrailingWhitespaceTap();
+
+      assert.isTrue(element.hasUnsavedChanges);
+
+      // Save the change.
+      return element.save().then(() => {
+        assert.isFalse(element.hasUnsavedChanges);
+      });
+    });
+  });
+</script>
diff --git a/polygerrit-ui/app/test/common-test-setup.html b/polygerrit-ui/app/test/common-test-setup.html
index 92b99e3..c5979fa 100644
--- a/polygerrit-ui/app/test/common-test-setup.html
+++ b/polygerrit-ui/app/test/common-test-setup.html
@@ -60,3 +60,4 @@
 <link rel="import"
     href="../bower_components/iron-test-helpers/iron-test-helpers.html" />
 <link rel="import" href="test-router.html" />
+<script src="../bower_components/moment/moment.js"></script>
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index 32bd396..d9e1238 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -108,7 +108,6 @@
     'diff/gr-diff-highlight/gr-annotation_test.html',
     'diff/gr-diff-highlight/gr-diff-highlight_test.html',
     'diff/gr-diff-mode-selector/gr-diff-mode-selector_test.html',
-    'diff/gr-diff-preferences/gr-diff-preferences_test.html',
     'diff/gr-diff-processor/gr-diff-processor_test.html',
     'diff/gr-diff-selection/gr-diff-selection_test.html',
     'diff/gr-diff-view/gr-diff-view_test.html',
@@ -162,6 +161,7 @@
     'shared/gr-cursor-manager/gr-cursor-manager_test.html',
     'shared/gr-date-formatter/gr-date-formatter_test.html',
     'shared/gr-dialog/gr-dialog_test.html',
+    'shared/gr-diff-preferences/gr-diff-preferences_test.html',
     'shared/gr-download-commands/gr-download-commands_test.html',
     'shared/gr-dropdown-list/gr-dropdown-list_test.html',
     'shared/gr-editable-content/gr-editable-content_test.html',