diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f0c266485b675d53926fcb16ea2a5c3138f53d3e..76a95ad6e0ac92fc600a24b39c5f24b5a208afe4 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -461,6 +461,7 @@ karma:
     - coverage-javascript/
 
 codeclimate:
+  <<: *except-docs
   before_script: []
   image: docker:latest
   stage: test
diff --git a/.gitlab/issue_templates/Bug.md b/.gitlab/issue_templates/Bug.md
index 9d53a48409a8f7e19ded72c49bbb87359760eda4..aec734870d65c850d3f33f364d706f4ec6b6ba61 100644
--- a/.gitlab/issue_templates/Bug.md
+++ b/.gitlab/issue_templates/Bug.md
@@ -1,11 +1,18 @@
 Please read this!
 
 Before opening a new issue, make sure to search for keywords in the issues
-filtered by the "regression" or "bug" label:
+filtered by the "regression" or "bug" label.
+
+For the Community Edition issue tracker:
 
 - https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=regression
 - https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=bug
 
+For the Enterprise Edition issue tracker:
+
+- https://gitlab.com/gitlab-org/gitlab-ee/issues?label_name%5B%5D=regression
+- https://gitlab.com/gitlab-org/gitlab-ee/issues?label_name%5B%5D=bug
+
 and verify the issue you're about to submit isn't a duplicate.
 
 Please remove this notice if you're confident your issue isn't a duplicate.
diff --git a/.gitlab/issue_templates/Feature Proposal.md b/.gitlab/issue_templates/Feature Proposal.md
index d96c9ad59e0b5e0c2d148be157f79c8790243dc7..1278061a410130f90c4425f38f95a7eae07f68ce 100644
--- a/.gitlab/issue_templates/Feature Proposal.md	
+++ b/.gitlab/issue_templates/Feature Proposal.md	
@@ -3,8 +3,14 @@ Please read this!
 Before opening a new issue, make sure to search for keywords in the issues
 filtered by the "feature proposal" label:
 
+For the Community Edition issue tracker:
+
 - https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name%5B%5D=feature+proposal
 
+For the Enterprise Edition issue tracker:
+
+- https://gitlab.com/gitlab-org/gitlab-ee/issues?label_name%5B%5D=feature+proposal
+
 and verify the issue you're about to submit isn't a duplicate.
 
 Please remove this notice if you're confident your issue isn't a duplicate.
@@ -21,12 +27,24 @@ Please remove this notice if you're confident your issue isn't a duplicate.
 
 ### Documentation blurb
 
-(Write the start of the documentation of this feature here, include:
+#### Overview
+
+What is it?
+Why should someone use this feature?
+What is the underlying (business) problem?
+How do you use this feature?
+
+#### Use cases
+
+Who is this for? Provide one or more use cases.
+
+### Feature checklist
 
-1. Why should someone use it; what's the underlying problem.
-2. What is the solution.
-3. How does someone use this
+Make sure these are completed before closing the issue,
+with a link to the relevant commit.
 
-During implementation, this can then be copied and used as a starter for the documentation.)
+- [ ] [Feature assurance](https://about.gitlab.com/handbook/product/#feature-assurance)
+- [ ] Documentation
+- [ ] Added to [features.yml](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/features.yml)
 
-/label ~"feature proposal"
+/label ~"feature proposal"
\ No newline at end of file
diff --git a/.rubocop.yml b/.rubocop.yml
index 4537e710dd488424ec18479370520b92736fe131..32ec60f540b16c6bc02dc178747dbbc7609b463f 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -164,6 +164,11 @@ Style/DefWithParentheses:
 Style/Documentation:
   Enabled: false
 
+# Multi-line method chaining should be done with leading dots.
+Style/DotPosition:
+  Enabled: true
+  EnforcedStyle: leading
+
 # This cop checks for uses of double negation (!!) to convert something
 # to a boolean value. As this is both cryptic and usually redundant, it
 # should be avoided.
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index e2d9c37479d339a67ef0b4936500d83d2da8654e..5ab4692dd60c8182bae4ec54af5bfbad84d872f6 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -88,13 +88,6 @@ Security/YAMLLoad:
 Style/BarePercentLiterals:
   Enabled: false
 
-# Offense count: 1403
-# Cop supports --auto-correct.
-# Configuration parameters: EnforcedStyle, SupportedStyles.
-# SupportedStyles: leading, trailing
-Style/DotPosition:
-  Enabled: false
-
 # Offense count: 5
 # Cop supports --auto-correct.
 Style/EachWithObject:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f43858a00a5104f47f34a2617beb20aaa1f87405..af5f5809c416758fa7fdde7ec638ff2940c50f58 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
 documentation](doc/development/changelog.md) for instructions on adding your own
 entry.
 
+## 9.2.7 (2017-06-21)
+
+- Reinstate is_admin flag in users api when authenticated user is an admin. !12211 (rickettm)
+
 ## 9.2.6 (2017-06-16)
 
 - Fix the last coverage in trace log should be extracted. !11128 (dosuken123)
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index bc859cbd6d998ecfd13ee4c49cd5da054afb36ad..54d1a4f2a4a7f6afc19897c88a7b73c17ccc54fb 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-0.11.2
+0.13.0
diff --git a/Gemfile b/Gemfile
index 2c200f2fa7adbc0413a0c4b0bbdf6c6dd8caf569..b1790b23dcd57db26888d20f93fd535c3c48c0ed 100644
--- a/Gemfile
+++ b/Gemfile
@@ -86,7 +86,7 @@ gem 'kaminari', '~> 0.17.0'
 gem 'hamlit', '~> 2.6.1'
 
 # Files attachments
-gem 'carrierwave', '~> 1.0'
+gem 'carrierwave', '~> 1.1'
 
 # Drag and Drop UI
 gem 'dropzonejs-rails', '~> 0.7.1'
@@ -158,7 +158,7 @@ gem 'rufus-scheduler', '~> 3.4'
 gem 'httparty', '~> 0.13.3'
 
 # Colored output to console
-gem 'rainbow', '~> 2.1.0'
+gem 'rainbow', '~> 2.2'
 
 # GitLab settings
 gem 'settingslogic', '~> 2.0.9'
@@ -260,6 +260,7 @@ gem 'premailer-rails', '~> 1.9.0'
 
 # I18n
 gem 'ruby_parser', '~> 3.8', require: false
+gem 'rails-i18n', '~> 4.0.9'
 gem 'gettext_i18n_rails', '~> 1.8.0'
 gem 'gettext_i18n_rails_js', '~> 1.2.0'
 gem 'gettext', '~> 3.2.2', require: false, group: :development
@@ -373,7 +374,7 @@ gem 'ruby-prof', '~> 0.16.2'
 gem 'oauth2', '~> 1.4'
 
 # Soft deletion
-gem 'paranoia', '~> 2.2'
+gem 'paranoia', '~> 2.3.1'
 
 # Health check
 gem 'health_check', '~> 2.6.0'
@@ -383,7 +384,7 @@ gem 'vmstat', '~> 2.3.0'
 gem 'sys-filesystem', '~> 1.1.6'
 
 # Gitaly GRPC client
-gem 'gitaly', '~> 0.8.0'
+gem 'gitaly', '~> 0.9.0'
 
 gem 'toml-rb', '~> 0.3.15', require: false
 
diff --git a/Gemfile.lock b/Gemfile.lock
index 6755c75e33167b47b197691ca9b97da563c5bd8a..bfd0498db35ff7f20f20f9c1c027cffaf9b88abc 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -108,7 +108,7 @@ GEM
     capybara-screenshot (1.0.14)
       capybara (>= 1.0, < 3)
       launchy
-    carrierwave (1.0.0)
+    carrierwave (1.1.0)
       activemodel (>= 4.0.0)
       activesupport (>= 4.0.0)
       mime-types (>= 1.16)
@@ -277,7 +277,7 @@ GEM
       po_to_json (>= 1.0.0)
       rails (>= 3.2.0)
     gherkin-ruby (0.3.2)
-    gitaly (0.8.0)
+    gitaly (0.9.0)
       google-protobuf (~> 3.1)
       grpc (~> 1.0)
     github-linguist (4.7.6)
@@ -546,8 +546,8 @@ GEM
       rubypants (~> 0.2)
     orm_adapter (0.5.0)
     os (0.9.6)
-    paranoia (2.2.0)
-      activerecord (>= 4.0, < 5.1)
+    paranoia (2.3.1)
+      activerecord (>= 4.0, < 5.2)
     parser (2.4.0.0)
       ast (~> 2.2)
     path_expander (1.0.1)
@@ -646,12 +646,16 @@ GEM
       rails-deprecated_sanitizer (>= 1.0.1)
     rails-html-sanitizer (1.0.3)
       loofah (~> 2.0)
+    rails-i18n (4.0.9)
+      i18n (~> 0.7)
+      railties (~> 4.0)
     railties (4.2.8)
       actionpack (= 4.2.8)
       activesupport (= 4.2.8)
       rake (>= 0.8.7)
       thor (>= 0.18.1, < 2.0)
-    rainbow (2.1.0)
+    rainbow (2.2.2)
+      rake
     raindrops (0.17.0)
     rake (10.5.0)
     rblineprof (0.3.6)
@@ -932,7 +936,7 @@ DEPENDENCIES
   bundler-audit (~> 0.5.0)
   capybara (~> 2.6.2)
   capybara-screenshot (~> 1.0.0)
-  carrierwave (~> 1.0)
+  carrierwave (~> 1.1)
   charlock_holmes (~> 0.7.3)
   chronic (~> 0.10.2)
   chronic_duration (~> 0.10.6)
@@ -973,7 +977,7 @@ DEPENDENCIES
   gettext (~> 3.2.2)
   gettext_i18n_rails (~> 1.8.0)
   gettext_i18n_rails_js (~> 1.2.0)
-  gitaly (~> 0.8.0)
+  gitaly (~> 0.9.0)
   github-linguist (~> 4.7.0)
   gitlab-flowdock-git-hook (~> 1.0.1)
   gitlab-markup (~> 1.5.1)
@@ -1031,7 +1035,7 @@ DEPENDENCIES
   omniauth-twitter (~> 1.2.0)
   omniauth_crowd (~> 2.2.0)
   org-ruby (~> 0.9.12)
-  paranoia (~> 2.2)
+  paranoia (~> 2.3.1)
   peek (~> 1.0.1)
   peek-gc (~> 0.0.2)
   peek-host (~> 1.0.0)
@@ -1053,7 +1057,8 @@ DEPENDENCIES
   rack-proxy (~> 0.6.0)
   rails (= 4.2.8)
   rails-deprecated_sanitizer (~> 1.0.3)
-  rainbow (~> 2.1.0)
+  rails-i18n (~> 4.0.9)
+  rainbow (~> 2.2)
   rblineprof (~> 0.3.6)
   rdoc (~> 4.2)
   recaptcha (~> 3.0)
@@ -1119,4 +1124,4 @@ DEPENDENCIES
   wikicloth (= 0.8.1)
 
 BUNDLED WITH
-   1.15.0
+   1.15.1
diff --git a/app/assets/javascripts/boards/filtered_search_boards.js b/app/assets/javascripts/boards/filtered_search_boards.js
index b37698fe9caa399bcbc3d402988424eb394f3720..3f083655f950819300ac2b93633d8587ad644f17 100644
--- a/app/assets/javascripts/boards/filtered_search_boards.js
+++ b/app/assets/javascripts/boards/filtered_search_boards.js
@@ -11,7 +11,6 @@ export default class FilteredSearchBoards extends gl.FilteredSearchManager {
     // Issue boards is slightly different, we handle all the requests async
     // instead or reloading the page, we just re-fire the list ajax requests
     this.isHandledAsync = true;
-
     this.cantEdit = cantEdit;
   }
 
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_bundle.js b/app/assets/javascripts/commit/pipelines/pipelines_bundle.js
index 86d99dd87da9eaba47297dfb753e09c0bcb0572c..2c38440a2afcbd93e2e7af035bc11336b32e13d6 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_bundle.js
+++ b/app/assets/javascripts/commit/pipelines/pipelines_bundle.js
@@ -1,29 +1,30 @@
-/* eslint-disable no-param-reassign */
-
 import Vue from 'vue';
-import VueResource from 'vue-resource';
-import CommitPipelinesTable from './pipelines_table';
-
-Vue.use(VueResource);
+import commitPipelinesTable from './pipelines_table.vue';
 
 /**
- * Commits View > Pipelines Tab > Pipelines Table.
- *
- * Renders Pipelines table in pipelines tab in the commits show view.
+ * Used in:
+ *  - Commit details View > Pipelines Tab > Pipelines Table.
+ *  - Merge Request details View > Pipelines Tab > Pipelines Table.
+ *  - New Merge Request View > Pipelines Tab > Pipelines Table.
  */
 
-// export for use in merge_request_tabs.js (TODO: remove this hack)
+const CommitPipelinesTable = Vue.extend(commitPipelinesTable);
+
+// export for use in merge_request_tabs.js (TODO: remove this hack when we understand how to load
+// vue.js in merge_request_tabs.js)
 window.gl = window.gl || {};
 window.gl.CommitPipelinesTable = CommitPipelinesTable;
 
-$(() => {
-  gl.commits = gl.commits || {};
-  gl.commits.pipelines = gl.commits.pipelines || {};
-
+document.addEventListener('DOMContentLoaded', () => {
   const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view');
 
   if (pipelineTableViewEl && pipelineTableViewEl.dataset.disableInitialization === undefined) {
-    gl.commits.pipelines.PipelinesTableBundle = new CommitPipelinesTable().$mount();
-    pipelineTableViewEl.appendChild(gl.commits.pipelines.PipelinesTableBundle.$el);
+    const table = new CommitPipelinesTable({
+      propsData: {
+        endpoint: pipelineTableViewEl.dataset.endpoint,
+        helpPagePath: pipelineTableViewEl.dataset.helpPagePath,
+      },
+    }).$mount();
+    pipelineTableViewEl.appendChild(table.$el);
   }
 });
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.js b/app/assets/javascripts/commit/pipelines/pipelines_table.js
deleted file mode 100644
index 70ba83ce5b9fb201e2131c04bbc3c6308462b9aa..0000000000000000000000000000000000000000
--- a/app/assets/javascripts/commit/pipelines/pipelines_table.js
+++ /dev/null
@@ -1,191 +0,0 @@
-import Vue from 'vue';
-import Visibility from 'visibilityjs';
-import pipelinesTableComponent from '../../vue_shared/components/pipelines_table.vue';
-import PipelinesService from '../../pipelines/services/pipelines_service';
-import PipelineStore from '../../pipelines/stores/pipelines_store';
-import eventHub from '../../pipelines/event_hub';
-import emptyState from '../../pipelines/components/empty_state.vue';
-import errorState from '../../pipelines/components/error_state.vue';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
-import '../../lib/utils/common_utils';
-import '../../vue_shared/vue_resource_interceptor';
-import Poll from '../../lib/utils/poll';
-
-/**
- *
- * Uses `pipelines-table-component` to render Pipelines table with an API call.
- * Endpoint is provided in HTML and passed as `endpoint`.
- * We need a store to store the received environemnts.
- * We need a service to communicate with the server.
- *
- */
-
-export default Vue.component('pipelines-table', {
-
-  components: {
-    pipelinesTableComponent,
-    errorState,
-    emptyState,
-    loadingIcon,
-  },
-
-  /**
-   * Accesses the DOM to provide the needed data.
-   * Returns the necessary props to render `pipelines-table-component` component.
-   *
-   * @return {Object}
-   */
-  data() {
-    const store = new PipelineStore();
-
-    return {
-      endpoint: null,
-      helpPagePath: null,
-      store,
-      state: store.state,
-      isLoading: false,
-      hasError: false,
-      isMakingRequest: false,
-      updateGraphDropdown: false,
-      hasMadeRequest: false,
-    };
-  },
-
-  computed: {
-    shouldRenderErrorState() {
-      return this.hasError && !this.isLoading;
-    },
-
-    /**
-     * Empty state is only rendered if after the first request we receive no pipelines.
-     *
-     * @return {Boolean}
-     */
-    shouldRenderEmptyState() {
-      return !this.state.pipelines.length &&
-        !this.isLoading &&
-        this.hasMadeRequest &&
-        !this.hasError;
-    },
-
-    shouldRenderTable() {
-      return !this.isLoading &&
-        this.state.pipelines.length > 0 &&
-        !this.hasError;
-    },
-  },
-
-  /**
-   * When the component is about to be mounted, tell the service to fetch the data
-   *
-   * A request to fetch the pipelines will be made.
-   * In case of a successfull response we will store the data in the provided
-   * store, in case of a failed response we need to warn the user.
-   *
-   */
-  beforeMount() {
-    const element = document.querySelector('#commit-pipeline-table-view');
-
-    this.endpoint = element.dataset.endpoint;
-    this.helpPagePath = element.dataset.helpPagePath;
-    this.service = new PipelinesService(this.endpoint);
-
-    this.poll = new Poll({
-      resource: this.service,
-      method: 'getPipelines',
-      successCallback: this.successCallback,
-      errorCallback: this.errorCallback,
-      notificationCallback: this.setIsMakingRequest,
-    });
-
-    if (!Visibility.hidden()) {
-      this.isLoading = true;
-      this.poll.makeRequest();
-    } else {
-      // If tab is not visible we need to make the first request so we don't show the empty
-      // state without knowing if there are any pipelines
-      this.fetchPipelines();
-    }
-
-    Visibility.change(() => {
-      if (!Visibility.hidden()) {
-        this.poll.restart();
-      } else {
-        this.poll.stop();
-      }
-    });
-
-    eventHub.$on('refreshPipelines', this.fetchPipelines);
-  },
-
-  beforeDestroy() {
-    eventHub.$off('refreshPipelines');
-  },
-
-  destroyed() {
-    this.poll.stop();
-  },
-
-  methods: {
-    fetchPipelines() {
-      this.isLoading = true;
-
-      return this.service.getPipelines()
-        .then(response => this.successCallback(response))
-        .catch(() => this.errorCallback());
-    },
-
-    successCallback(resp) {
-      const response = resp.json();
-
-      this.hasMadeRequest = true;
-
-      // depending of the endpoint the response can either bring a `pipelines` key or not.
-      const pipelines = response.pipelines || response;
-      this.store.storePipelines(pipelines);
-      this.isLoading = false;
-      this.updateGraphDropdown = true;
-    },
-
-    errorCallback() {
-      this.hasError = true;
-      this.isLoading = false;
-      this.updateGraphDropdown = false;
-    },
-
-    setIsMakingRequest(isMakingRequest) {
-      this.isMakingRequest = isMakingRequest;
-
-      if (isMakingRequest) {
-        this.updateGraphDropdown = false;
-      }
-    },
-  },
-
-  template: `
-    <div class="content-list pipelines">
-
-      <loading-icon
-        label="Loading pipelines"
-        size="3"
-        v-if="isLoading"
-        />
-
-      <empty-state
-        v-if="shouldRenderEmptyState"
-        :help-page-path="helpPagePath" />
-
-      <error-state v-if="shouldRenderErrorState" />
-
-      <div
-        class="table-holder"
-        v-if="shouldRenderTable">
-        <pipelines-table-component
-          :pipelines="state.pipelines"
-          :service="service"
-          :update-graph-dropdown="updateGraphDropdown"
-          />
-      </div>
-    </div>
-  `,
-});
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.vue b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
new file mode 100644
index 0000000000000000000000000000000000000000..3c77f14d533c2eb2ef8915aabbfa2d713fd73744
--- /dev/null
+++ b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
@@ -0,0 +1,90 @@
+<script>
+  import PipelinesService from '../../pipelines/services/pipelines_service';
+  import PipelineStore from '../../pipelines/stores/pipelines_store';
+  import pipelinesMixin from '../../pipelines/mixins/pipelines';
+
+  export default {
+    props: {
+      endpoint: {
+        type: String,
+        required: true,
+      },
+      helpPagePath: {
+        type: String,
+        required: true,
+      },
+    },
+    mixins: [
+      pipelinesMixin,
+    ],
+
+    data() {
+      const store = new PipelineStore();
+
+      return {
+        store,
+        state: store.state,
+      };
+    },
+
+    computed: {
+      /**
+       * Empty state is only rendered if after the first request we receive no pipelines.
+       *
+       * @return {Boolean}
+       */
+      shouldRenderEmptyState() {
+        return !this.state.pipelines.length &&
+          !this.isLoading &&
+          this.hasMadeRequest &&
+          !this.hasError;
+      },
+
+      shouldRenderTable() {
+        return !this.isLoading &&
+          this.state.pipelines.length > 0 &&
+          !this.hasError;
+      },
+    },
+    created() {
+      this.service = new PipelinesService(this.endpoint);
+    },
+    methods: {
+      successCallback(resp) {
+        const response = resp.json();
+
+        // depending of the endpoint the response can either bring a `pipelines` key or not.
+        const pipelines = response.pipelines || response;
+        this.setCommonData(pipelines);
+      },
+    },
+  };
+</script>
+<template>
+  <div class="content-list pipelines">
+
+    <loading-icon
+      label="Loading pipelines"
+      size="3"
+      v-if="isLoading"
+      />
+
+    <empty-state
+      v-if="shouldRenderEmptyState"
+      :help-page-path="helpPagePath"
+      />
+
+    <error-state
+      v-if="shouldRenderErrorState"
+      />
+
+    <div
+      class="table-holder"
+      v-if="shouldRenderTable">
+      <pipelines-table-component
+        :pipelines="state.pipelines"
+        :update-graph-dropdown="updateGraphDropdown"
+        />
+    </div>
+  </div>
+</template>
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index 5f87a05067bf11756addd428aba2040cff5cd227..88b4b567fa9e76d9ca4b45573fb0364938fea49e 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -79,7 +79,18 @@ import initSettingsPanels from './settings_panels';
       path = page.split(':');
       shortcut_handler = null;
 
-      new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources).setup();
+      $('.js-gfm-input').each((i, el) => {
+        const gfm = new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources);
+        const enableGFM = gl.utils.convertPermissionToBoolean(el.dataset.supportsAutocomplete);
+        gfm.setup($(el), {
+          emojis: true,
+          members: enableGFM,
+          issues: enableGFM,
+          milestones: enableGFM,
+          mergeRequests: enableGFM,
+          labels: enableGFM,
+        });
+      });
 
       function initBlob() {
         new LineHighlighter();
@@ -176,7 +187,7 @@ import initSettingsPanels from './settings_panels';
         case 'groups:milestones:update':
           new ZenMode();
           new gl.DueDateSelectors();
-          new gl.GLForm($('.milestone-form'));
+          new gl.GLForm($('.milestone-form'), true);
           break;
         case 'projects:compare:show':
           new gl.Diff();
@@ -188,7 +199,7 @@ import initSettingsPanels from './settings_panels';
         case 'projects:issues:new':
         case 'projects:issues:edit':
           shortcut_handler = new ShortcutsNavigation();
-          new gl.GLForm($('.issue-form'));
+          new gl.GLForm($('.issue-form'), true);
           new IssuableForm($('.issue-form'));
           new LabelsSelect();
           new MilestoneSelect();
@@ -199,7 +210,7 @@ import initSettingsPanels from './settings_panels';
         case 'projects:merge_requests:edit':
           new gl.Diff();
           shortcut_handler = new ShortcutsNavigation();
-          new gl.GLForm($('.merge-request-form'));
+          new gl.GLForm($('.merge-request-form'), true);
           new IssuableForm($('.merge-request-form'));
           new LabelsSelect();
           new MilestoneSelect();
@@ -208,22 +219,24 @@ import initSettingsPanels from './settings_panels';
           break;
         case 'projects:tags:new':
           new ZenMode();
-          new gl.GLForm($('.tag-form'));
+          new gl.GLForm($('.tag-form'), true);
           new RefSelectDropdown($('.js-branch-select'), window.gl.availableRefs);
           break;
         case 'projects:snippets:new':
         case 'projects:snippets:edit':
         case 'projects:snippets:create':
         case 'projects:snippets:update':
+          new gl.GLForm($('.snippet-form'), true);
+          break;
         case 'snippets:new':
         case 'snippets:edit':
         case 'snippets:create':
         case 'snippets:update':
-          new gl.GLForm($('.snippet-form'));
+          new gl.GLForm($('.snippet-form'), false);
           break;
         case 'projects:releases:edit':
           new ZenMode();
-          new gl.GLForm($('.release-form'));
+          new gl.GLForm($('.release-form'), true);
           break;
         case 'projects:merge_requests:show':
           new gl.Diff();
@@ -471,7 +484,7 @@ import initSettingsPanels from './settings_panels';
               new gl.Wikis();
               shortcut_handler = new ShortcutsWiki();
               new ZenMode();
-              new gl.GLForm($('.wiki-form'));
+              new gl.GLForm($('.wiki-form'), true);
               break;
             case 'snippets':
               shortcut_handler = new ShortcutsNavigation();
diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js
index 98ddcc20036c6086fc1713f8d40c0dcf6df73f8d..73675d300be97776af7cf43084aba1cfdd20f4d0 100644
--- a/app/assets/javascripts/dropzone_input.js
+++ b/app/assets/javascripts/dropzone_input.js
@@ -287,6 +287,10 @@ window.DropzoneInput = (function() {
       $uploadingErrorMessage.html(message);
     };
 
+    closeAlertMessage = function() {
+      return form.find('.div-dropzone-alert').alert('close');
+    };
+
     form.find('.markdown-selector').click(function(e) {
       e.preventDefault();
       $(this).closest('.gfm-form').find('.div-dropzone').click();
diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue
index 809c147bf25be7c0b9190b4c53c68f4176a7b4e2..b25113e0fc672dbbf98b85e4219239155fb21cc6 100644
--- a/app/assets/javascripts/environments/components/environment_item.vue
+++ b/app/assets/javascripts/environments/components/environment_item.vue
@@ -403,6 +403,14 @@ export default {
       return '';
     },
 
+    displayEnvironmentActions() {
+      return this.hasManualActions ||
+             this.externalURL ||
+             this.monitoringUrl ||
+             this.hasStopAction ||
+             this.canRetry;
+    },
+
     /**
      * Constructs folder URL based on the current location and the folder id.
      *
@@ -535,9 +543,12 @@ export default {
       </span>
     </div>
 
-    <div class="table-section section-30 table-button-footer" role="gridcell">
+    <div
+      v-if="!model.isFolder && displayEnvironmentActions"
+      class="table-section section-30 table-button-footer"
+      role="gridcell">
+
       <div
-        v-if="!model.isFolder"
         class="btn-group table-action-buttons"
         role="group">
 
diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js b/app/assets/javascripts/filtered_search/dropdown_hint.js
index 2af242a69df703a0c99c2bf77801be66a75c9a32..5838b1bdbb7bcdc428c0f727a835dd1b6f07a834 100644
--- a/app/assets/javascripts/filtered_search/dropdown_hint.js
+++ b/app/assets/javascripts/filtered_search/dropdown_hint.js
@@ -56,7 +56,7 @@ class DropdownHint extends gl.FilteredSearchDropdown {
   }
 
   renderContent() {
-    const dropdownData = gl.FilteredSearchTokenKeys.get()
+    const dropdownData = this.tokenKeys.get()
       .map(tokenKey => ({
         icon: `fa-${tokenKey.icon}`,
         hint: tokenKey.key,
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js
index 8f547bd8f1f894c69faa9bb4aa5ade391e1c4d68..c7c8d42e6770df90af31d7abab7004ac90dc7c8a 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js
@@ -487,6 +487,7 @@ class FilteredSearchManager {
   }
 
   searchState(e) {
+    e.preventDefault();
     const target = e.currentTarget;
     // remove focus outline after click
     target.blur();
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index 401dec1a37065eb5c3d56501f4793bcd7a3dc4ca..105762cb1ba8e1005e55340384fcffead7e99519 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -34,7 +34,7 @@ class GfmAutoComplete {
       const $input = $(input);
       $input.off('focus.setupAtWho').on('focus.setupAtWho', this.setupAtWho.bind(this, $input));
       // This triggers at.js again
-      // Needed for slash commands with suffixes (ex: /label ~)
+      // Needed for quick actions with suffixes (ex: /label ~)
       $input.on('inserted-commands.atwho', $input.trigger.bind($input, 'keyup'));
       $input.on('clear-commands-cache.atwho', () => this.clearCache());
     });
@@ -48,8 +48,8 @@ class GfmAutoComplete {
     if (this.enableMap.mergeRequests) this.setupMergeRequests($input);
     if (this.enableMap.labels) this.setupLabels($input);
 
-    // We don't instantiate the slash commands autocomplete for note and issue/MR edit forms
-    $input.filter('[data-supports-slash-commands="true"]').atwho({
+    // We don't instantiate the quick actions autocomplete for note and issue/MR edit forms
+    $input.filter('[data-supports-quick-actions="true"]').atwho({
       at: '/',
       alias: 'commands',
       searchKey: 'search',
diff --git a/app/assets/javascripts/issuable_bulk_update_sidebar.js b/app/assets/javascripts/issuable_bulk_update_sidebar.js
index 84bd2e092e6d4177ad0190da680c28bbf9391550..a8856120c5e6c25f4eba5d250e5dba183f960422 100644
--- a/app/assets/javascripts/issuable_bulk_update_sidebar.js
+++ b/app/assets/javascripts/issuable_bulk_update_sidebar.js
@@ -22,6 +22,7 @@ export default class IssuableBulkUpdateSidebar {
   initDomElements() {
     this.$page = $('.page-with-sidebar');
     this.$sidebar = $('.right-sidebar');
+    this.$sidebarInnerContainer = this.$sidebar.find('.issuable-sidebar');
     this.$bulkEditCancelBtn = $('.js-bulk-update-menu-hide');
     this.$bulkEditSubmitBtn = $('.update-selected-issues');
     this.$bulkUpdateEnableBtn = $('.js-bulk-update-toggle');
@@ -113,6 +114,7 @@ export default class IssuableBulkUpdateSidebar {
   toggleSidebarDisplay(show) {
     this.$page.toggleClass(SIDEBAR_EXPANDED_CLASS, show);
     this.$page.toggleClass(SIDEBAR_COLLAPSED_CLASS, !show);
+    this.$sidebarInnerContainer.toggleClass(HIDDEN_CLASS, !show);
     this.$sidebar.toggleClass(SIDEBAR_EXPANDED_CLASS, show);
     this.$sidebar.toggleClass(SIDEBAR_COLLAPSED_CLASS, !show);
   }
diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue
index e14414d3f68c7e1e46911ea5f8a80cdefab203fe..3d5fb7f441ca37dadfa4b0e559d2b295d778f613 100644
--- a/app/assets/javascripts/issue_show/components/app.vue
+++ b/app/assets/javascripts/issue_show/components/app.vue
@@ -51,6 +51,11 @@ export default {
       required: false,
       default: '',
     },
+    initialTaskStatus: {
+      type: String,
+      required: false,
+      default: '',
+    },
     updatedAt: {
       type: String,
       required: false,
@@ -105,6 +110,7 @@ export default {
       updatedAt: this.updatedAt,
       updatedByName: this.updatedByName,
       updatedByPath: this.updatedByPath,
+      taskStatus: this.initialTaskStatus,
     });
 
     return {
@@ -198,13 +204,7 @@ export default {
       method: 'getData',
       successCallback: (res) => {
         const data = res.json();
-        const shouldUpdate = this.store.stateShouldUpdate(data);
-
         this.store.updateState(data);
-
-        if (this.showForm && (shouldUpdate.title || shouldUpdate.description)) {
-          this.store.formState.lockedWarningVisible = true;
-        }
       },
       errorCallback(err) {
         throw new Error(err);
diff --git a/app/assets/javascripts/issue_show/components/description.vue b/app/assets/javascripts/issue_show/components/description.vue
index 5ae617356e020a07c3dfaa1f7a1c826928782aa5..43db66c8e08bb3ea3618c4731124c286ced100d5 100644
--- a/app/assets/javascripts/issue_show/components/description.vue
+++ b/app/assets/javascripts/issue_show/components/description.vue
@@ -37,23 +37,12 @@
         });
       },
       taskStatus() {
-        const taskRegexMatches = this.taskStatus.match(/(\d+) of (\d+)/);
-        const $issuableHeader = $('.issuable-meta');
-        const $tasks = $('#task_status', $issuableHeader);
-        const $tasksShort = $('#task_status_short', $issuableHeader);
-
-        if (taskRegexMatches) {
-          $tasks.text(this.taskStatus);
-          $tasksShort.text(`${taskRegexMatches[1]}/${taskRegexMatches[2]} task${taskRegexMatches[2] > 1 ? 's' : ''}`);
-        } else {
-          $tasks.text('');
-          $tasksShort.text('');
-        }
+        this.updateTaskStatusText();
       },
     },
     methods: {
       renderGFM() {
-        $(this.$refs['gfm-entry-content']).renderGFM();
+        $(this.$refs['gfm-content']).renderGFM();
 
         if (this.canUpdate) {
           // eslint-disable-next-line no-new
@@ -64,9 +53,24 @@
           });
         }
       },
+      updateTaskStatusText() {
+        const taskRegexMatches = this.taskStatus.match(/(\d+) of ((?!0)\d+)/);
+        const $issuableHeader = $('.issuable-meta');
+        const $tasks = $('#task_status', $issuableHeader);
+        const $tasksShort = $('#task_status_short', $issuableHeader);
+
+        if (taskRegexMatches) {
+          $tasks.text(this.taskStatus);
+          $tasksShort.text(`${taskRegexMatches[1]}/${taskRegexMatches[2]} task${taskRegexMatches[2] > 1 ? 's' : ''}`);
+        } else {
+          $tasks.text('');
+          $tasksShort.text('');
+        }
+      },
     },
     mounted() {
       this.renderGFM();
+      this.updateTaskStatusText();
     },
   };
 </script>
diff --git a/app/assets/javascripts/issue_show/components/fields/description.vue b/app/assets/javascripts/issue_show/components/fields/description.vue
index 30a1be5cb50ef16ea43b8d7cd47253192e65a2cf..54650d2f184c3c4dff3bdeefa16ea0bea4b9cfe2 100644
--- a/app/assets/javascripts/issue_show/components/fields/description.vue
+++ b/app/assets/javascripts/issue_show/components/fields/description.vue
@@ -41,7 +41,7 @@
       <textarea
         id="issue-description"
         class="note-textarea js-gfm-input js-autosize markdown-area"
-        data-supports-slash-commands="false"
+        data-supports-quick-actionss="false"
         aria-label="Description"
         v-model="formState.description"
         ref="textarea"
diff --git a/app/assets/javascripts/issue_show/index.js b/app/assets/javascripts/issue_show/index.js
index 14b2a1e18e910179b1beb5250a34ea40d4d70c07..ad8cb6465e281d8306385fec909a9b70dbe9eade 100644
--- a/app/assets/javascripts/issue_show/index.js
+++ b/app/assets/javascripts/issue_show/index.js
@@ -45,6 +45,7 @@ document.addEventListener('DOMContentLoaded', () => {
           updatedAt: this.updatedAt,
           updatedByName: this.updatedByName,
           updatedByPath: this.updatedByPath,
+          initialTaskStatus: this.initialTaskStatus,
         },
       });
     },
diff --git a/app/assets/javascripts/issue_show/stores/index.js b/app/assets/javascripts/issue_show/stores/index.js
index 27c2d349f52b2c69dbb99e2a8652c3e889ae0dc1..0c8bd6f1cc34604540354c223727c205000b2e9f 100644
--- a/app/assets/javascripts/issue_show/stores/index.js
+++ b/app/assets/javascripts/issue_show/stores/index.js
@@ -1,23 +1,6 @@
 export default class Store {
-  constructor({
-    titleHtml,
-    titleText,
-    descriptionHtml,
-    descriptionText,
-    updatedAt,
-    updatedByName,
-    updatedByPath,
-  }) {
-    this.state = {
-      titleHtml,
-      titleText,
-      descriptionHtml,
-      descriptionText,
-      taskStatus: '',
-      updatedAt,
-      updatedByName,
-      updatedByPath,
-    };
+  constructor(initialState) {
+    this.state = initialState;
     this.formState = {
       title: '',
       confidential: false,
@@ -29,6 +12,10 @@ export default class Store {
   }
 
   updateState(data) {
+    if (this.stateShouldUpdate(data)) {
+      this.formState.lockedWarningVisible = true;
+    }
+
     this.state.titleHtml = data.title;
     this.state.titleText = data.title_text;
     this.state.descriptionHtml = data.description;
@@ -40,10 +27,8 @@ export default class Store {
   }
 
   stateShouldUpdate(data) {
-    return {
-      title: this.state.titleText !== data.title_text,
-      description: this.state.descriptionText !== data.description_text,
-    };
+    return this.state.titleText !== data.title_text ||
+      this.state.descriptionText !== data.description_text;
   }
 
   setFormState(state) {
diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js
index 54c0da3fc9c9415a6233517871900d5dc60566fe..bfcc50996ccef57a249aa05d56fd8ed6066d93fb 100644
--- a/app/assets/javascripts/lib/utils/datetime_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime_utility.js
@@ -34,7 +34,7 @@ window.dateFormat = dateFormat;
 
     w.gl.utils.localTimeAgo = function($timeagoEls, setTimeago = true) {
       $timeagoEls.each((i, el) => {
-        el.setAttribute('title', gl.utils.formatDate(el.getAttribute('datetime')));
+        el.setAttribute('title', el.getAttribute('title'));
 
         if (setTimeago) {
           // Recreate with custom template
diff --git a/app/assets/javascripts/locale/en/app.js b/app/assets/javascripts/locale/en/app.js
index 0bb76c80b7a73de248ff67bed56b564146de8b8e..d634af959e5fd9165e6d6829c542d89739d58089 100644
--- a/app/assets/javascripts/locale/en/app.js
+++ b/app/assets/javascripts/locale/en/app.js
@@ -1 +1 @@
-var locales = locales || {}; locales['en'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-04-12 22:36-0500","Last-Translator":"FULL NAME <EMAIL@ADDRESS>","Language-Team":"English","Language":"en","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Plural-Forms":"nplurals=2; plural=n != 1;","lang":"en","domain":"app","plural_forms":"nplurals=2; plural=n != 1;"},"Are you sure you want to delete this pipeline schedule?":[""],"ByAuthor|by":[""],"Cancel":[""],"Commit":["",""],"Cron Timezone":[""],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":[""],"CycleAnalyticsStage|Code":[""],"CycleAnalyticsStage|Issue":[""],"CycleAnalyticsStage|Plan":[""],"CycleAnalyticsStage|Production":[""],"CycleAnalyticsStage|Review":[""],"CycleAnalyticsStage|Staging":[""],"CycleAnalyticsStage|Test":[""],"Delete":[""],"Deploy":["",""],"Description":[""],"Edit":[""],"Edit Pipeline Schedule %{id}":[""],"Failed to change the owner":[""],"Failed to remove the pipeline schedule":[""],"Filter":[""],"FirstPushedBy|First":[""],"FirstPushedBy|pushed by":[""],"From issue creation until deploy to production":[""],"From merge request merge until deploy to production":[""],"Interval Pattern":[""],"Introducing Cycle Analytics":[""],"Last %d day":["",""],"Last Pipeline":[""],"Limited to showing %d event at most":["",""],"Median":[""],"New Issue":["",""],"New Pipeline Schedule":[""],"No schedules":[""],"Not available":[""],"Not enough data":[""],"OpenedNDaysAgo|Opened":[""],"Owner":[""],"Pipeline Health":[""],"Pipeline Schedule":[""],"Pipeline Schedules":[""],"PipelineSchedules|Activated":[""],"PipelineSchedules|Active":[""],"PipelineSchedules|All":[""],"PipelineSchedules|Inactive":[""],"PipelineSchedules|Next Run":[""],"PipelineSchedules|None":[""],"PipelineSchedules|Provide a short description for this pipeline":[""],"PipelineSchedules|Take ownership":[""],"PipelineSchedules|Target":[""],"ProjectLifecycle|Stage":[""],"Read more":[""],"Related Commits":[""],"Related Deployed Jobs":[""],"Related Issues":[""],"Related Jobs":[""],"Related Merge Requests":[""],"Related Merged Requests":[""],"Save pipeline schedule":[""],"Schedule a new pipeline":[""],"Select a timezone":[""],"Select target branch":[""],"Showing %d event":["",""],"Target Branch":[""],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":[""],"The collection of events added to the data gathered for that stage.":[""],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":[""],"The phase of the development lifecycle.":[""],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":[""],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":[""],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":[""],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":[""],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":[""],"The time taken by each data entry gathered by that stage.":[""],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":[""],"Time before an issue gets scheduled":[""],"Time before an issue starts implementation":[""],"Time between merge request creation and merge/close":[""],"Time until first merge request":[""],"Time|hr":["",""],"Time|min":["",""],"Time|s":[""],"Total Time":[""],"Total test time for all commits/merges":[""],"Want to see the data? Please ask an administrator for access.":[""],"We don't have enough data to show this stage.":[""],"You need permission.":[""],"day":["",""]}}};
\ No newline at end of file
+var locales = locales || {}; locales['en'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-04-12 22:36-0500","Last-Translator":"FULL NAME <EMAIL@ADDRESS>","Language-Team":"English","Language":"en","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Plural-Forms":"nplurals=2; plural=n != 1;","lang":"en","domain":"app","plural_forms":"nplurals=2; plural=n != 1;"},"%{commit_author_link} committed %{commit_timeago}":[""],"About auto deploy":[""],"Active":[""],"Activity":[""],"Add Changelog":[""],"Add Contribution guide":[""],"Add License":[""],"Add an SSH key to your profile to pull or push via SSH.":[""],"Add new directory":[""],"Archived project! Repository is read-only":[""],"Are you sure you want to delete this pipeline schedule?":[""],"Attach a file by drag &amp; drop or %{upload_link}":[""],"Branch":["",""],"Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}":[""],"Branches":[""],"Browse files":[""],"ByAuthor|by":[""],"CI configuration":[""],"Cancel":[""],"ChangeTypeActionLabel|Pick into branch":[""],"ChangeTypeActionLabel|Revert in branch":[""],"ChangeTypeAction|Cherry-pick":[""],"Changelog":[""],"Charts":[""],"Cherry-pick this commit":[""],"Cherry-pick this merge request":[""],"CiStatusLabel|canceled":[""],"CiStatusLabel|created":[""],"CiStatusLabel|failed":[""],"CiStatusLabel|manual action":[""],"CiStatusLabel|passed":[""],"CiStatusLabel|passed with warnings":[""],"CiStatusLabel|pending":[""],"CiStatusLabel|skipped":[""],"CiStatusLabel|waiting for manual action":[""],"CiStatusText|blocked":[""],"CiStatusText|canceled":[""],"CiStatusText|created":[""],"CiStatusText|failed":[""],"CiStatusText|manual":[""],"CiStatusText|passed":[""],"CiStatusText|pending":[""],"CiStatusText|skipped":[""],"CiStatus|running":[""],"Commit":["",""],"Commit message":[""],"CommitBoxTitle|Commit":[""],"CommitMessage|Add %{file_name}":[""],"Commits":[""],"Commits|History":[""],"Committed by":[""],"Compare":[""],"Contribution guide":[""],"Contributors":[""],"Copy URL to clipboard":[""],"Copy commit SHA to clipboard":[""],"Create New Directory":[""],"Create directory":[""],"Create empty bare repository":[""],"Create merge request":[""],"Create new...":[""],"CreateNewFork|Fork":[""],"CreateTag|Tag":[""],"Cron Timezone":[""],"Cron syntax":[""],"Custom notification events":[""],"Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.":[""],"Cycle Analytics":[""],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":[""],"CycleAnalyticsStage|Code":[""],"CycleAnalyticsStage|Issue":[""],"CycleAnalyticsStage|Plan":[""],"CycleAnalyticsStage|Production":[""],"CycleAnalyticsStage|Review":[""],"CycleAnalyticsStage|Staging":[""],"CycleAnalyticsStage|Test":[""],"Define a custom pattern with cron syntax":[""],"Delete":[""],"Deploy":["",""],"Description":[""],"Directory name":[""],"Don't show again":[""],"Download":[""],"Download tar":[""],"Download tar.bz2":[""],"Download tar.gz":[""],"Download zip":[""],"DownloadArtifacts|Download":[""],"DownloadCommit|Email Patches":[""],"DownloadCommit|Plain Diff":[""],"DownloadSource|Download":[""],"Edit":[""],"Edit Pipeline Schedule %{id}":[""],"Every day (at 4:00am)":[""],"Every month (on the 1st at 4:00am)":[""],"Every week (Sundays at 4:00am)":[""],"Failed to change the owner":[""],"Failed to remove the pipeline schedule":[""],"Files":[""],"Find by path":[""],"Find file":[""],"FirstPushedBy|First":[""],"FirstPushedBy|pushed by":[""],"Fork":["",""],"ForkedFromProjectPath|Forked from":[""],"From issue creation until deploy to production":[""],"From merge request merge until deploy to production":[""],"Go to your fork":[""],"GoToYourFork|Fork":[""],"Home":[""],"Housekeeping successfully started":[""],"Import repository":[""],"Interval Pattern":[""],"Introducing Cycle Analytics":[""],"LFSStatus|Disabled":[""],"LFSStatus|Enabled":[""],"Last %d day":["",""],"Last Pipeline":[""],"Last Update":[""],"Last commit":[""],"Learn more in the":[""],"Learn more in the|pipeline schedules documentation":[""],"Leave group":[""],"Leave project":[""],"Limited to showing %d event at most":["",""],"Median":[""],"MissingSSHKeyWarningLink|add an SSH key":[""],"New Issue":["",""],"New Pipeline Schedule":[""],"New branch":[""],"New directory":[""],"New file":[""],"New issue":[""],"New merge request":[""],"New schedule":[""],"New snippet":[""],"New tag":[""],"No repository":[""],"No schedules":[""],"Not available":[""],"Not enough data":[""],"Notification events":[""],"NotificationEvent|Close issue":[""],"NotificationEvent|Close merge request":[""],"NotificationEvent|Failed pipeline":[""],"NotificationEvent|Merge merge request":[""],"NotificationEvent|New issue":[""],"NotificationEvent|New merge request":[""],"NotificationEvent|New note":[""],"NotificationEvent|Reassign issue":[""],"NotificationEvent|Reassign merge request":[""],"NotificationEvent|Reopen issue":[""],"NotificationEvent|Successful pipeline":[""],"NotificationLevel|Custom":[""],"NotificationLevel|Disabled":[""],"NotificationLevel|Global":[""],"NotificationLevel|On mention":[""],"NotificationLevel|Participate":[""],"NotificationLevel|Watch":[""],"OfSearchInADropdown|Filter":[""],"OpenedNDaysAgo|Opened":[""],"Options":[""],"Owner":[""],"Pipeline":[""],"Pipeline Health":[""],"Pipeline Schedule":[""],"Pipeline Schedules":[""],"PipelineSchedules|Activated":[""],"PipelineSchedules|Active":[""],"PipelineSchedules|All":[""],"PipelineSchedules|Inactive":[""],"PipelineSchedules|Next Run":[""],"PipelineSchedules|None":[""],"PipelineSchedules|Provide a short description for this pipeline":[""],"PipelineSchedules|Take ownership":[""],"PipelineSchedules|Target":[""],"PipelineSheduleIntervalPattern|Custom":[""],"Pipeline|with stage":[""],"Pipeline|with stages":[""],"Project '%{project_name}' queued for deletion.":[""],"Project '%{project_name}' was successfully created.":[""],"Project '%{project_name}' was successfully updated.":[""],"Project '%{project_name}' will be deleted.":[""],"Project access must be granted explicitly to each user.":[""],"Project export could not be deleted.":[""],"Project export has been deleted.":[""],"Project export link has expired. Please generate a new export from your project settings.":[""],"Project export started. A download link will be sent by email.":[""],"Project home":[""],"ProjectFeature|Disabled":[""],"ProjectFeature|Everyone with access":[""],"ProjectFeature|Only team members":[""],"ProjectFileTree|Name":[""],"ProjectLastActivity|Never":[""],"ProjectLifecycle|Stage":[""],"ProjectNetworkGraph|Graph":[""],"Read more":[""],"Readme":[""],"RefSwitcher|Branches":[""],"RefSwitcher|Tags":[""],"Related Commits":[""],"Related Deployed Jobs":[""],"Related Issues":[""],"Related Jobs":[""],"Related Merge Requests":[""],"Related Merged Requests":[""],"Remind later":[""],"Remove project":[""],"Request Access":[""],"Revert this commit":[""],"Revert this merge request":[""],"Save pipeline schedule":[""],"Schedule a new pipeline":[""],"Scheduling Pipelines":[""],"Search branches and tags":[""],"Select Archive Format":[""],"Select a timezone":[""],"Select target branch":[""],"Set a password on your account to pull or push via %{protocol}":[""],"Set up CI":[""],"Set up Koding":[""],"Set up auto deploy":[""],"SetPasswordToCloneLink|set a password":[""],"Showing %d event":["",""],"Source code":[""],"StarProject|Star":[""],"Start a  %{new_merge_request} with these changes":[""],"Start a <strong>new merge request</strong> with these changes":[""],"Switch branch/tag":[""],"Tag":["",""],"Tags":[""],"Target Branch":[""],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":[""],"The collection of events added to the data gathered for that stage.":[""],"The fork relationship has been removed.":[""],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":[""],"The phase of the development lifecycle.":[""],"The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user.":[""],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":[""],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":[""],"The project can be accessed by any logged in user.":[""],"The project can be accessed without any authentication.":[""],"The repository for this project does not exist.":[""],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":[""],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":[""],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":[""],"The time taken by each data entry gathered by that stage.":[""],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":[""],"This means you can not push code until you create an empty repository or import existing one.":[""],"Time before an issue gets scheduled":[""],"Time before an issue starts implementation":[""],"Time between merge request creation and merge/close":[""],"Time until first merge request":[""],"Timeago|%s days ago":[""],"Timeago|%s days remaining":[""],"Timeago|%s hours remaining":[""],"Timeago|%s minutes ago":[""],"Timeago|%s minutes remaining":[""],"Timeago|%s months ago":[""],"Timeago|%s months remaining":[""],"Timeago|%s seconds remaining":[""],"Timeago|%s weeks ago":[""],"Timeago|%s weeks remaining":[""],"Timeago|%s years ago":[""],"Timeago|%s years remaining":[""],"Timeago|1 day remaining":[""],"Timeago|1 hour remaining":[""],"Timeago|1 minute remaining":[""],"Timeago|1 month remaining":[""],"Timeago|1 week remaining":[""],"Timeago|1 year remaining":[""],"Timeago|Past due":[""],"Timeago|a day ago":[""],"Timeago|a month ago":[""],"Timeago|a week ago":[""],"Timeago|a while":[""],"Timeago|a year ago":[""],"Timeago|about %s hours ago":[""],"Timeago|about a minute ago":[""],"Timeago|about an hour ago":[""],"Timeago|in %s days":[""],"Timeago|in %s hours":[""],"Timeago|in %s minutes":[""],"Timeago|in %s months":[""],"Timeago|in %s seconds":[""],"Timeago|in %s weeks":[""],"Timeago|in %s years":[""],"Timeago|in 1 day":[""],"Timeago|in 1 hour":[""],"Timeago|in 1 minute":[""],"Timeago|in 1 month":[""],"Timeago|in 1 week":[""],"Timeago|in 1 year":[""],"Timeago|less than a minute ago":[""],"Time|hr":["",""],"Time|min":["",""],"Time|s":[""],"Total Time":[""],"Total test time for all commits/merges":[""],"Unstar":[""],"Upload New File":[""],"Upload file":[""],"Use your global notification setting":[""],"VisibilityLevel|Internal":[""],"VisibilityLevel|Private":[""],"VisibilityLevel|Public":[""],"Want to see the data? Please ask an administrator for access.":[""],"We don't have enough data to show this stage.":[""],"Withdraw Access Request":[""],"You are going to remove %{project_name_with_namespace}.\\nRemoved project CANNOT be restored!\\nAre you ABSOLUTELY sure?":[""],"You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?":[""],"You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?":[""],"You can only add files when you are on a branch":[""],"You must sign in to star a project":[""],"You need permission.":[""],"You will not get any notifications via email":[""],"You will only receive notifications for the events you choose":[""],"You will only receive notifications for threads you have participated in":[""],"You will receive notifications for any activity":[""],"You will receive notifications only for comments in which you were @mentioned":[""],"You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account":[""],"You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile":[""],"Your name":[""],"day":["",""],"new merge request":[""],"notification emails":[""],"parent":["",""]}}};
\ No newline at end of file
diff --git a/app/assets/javascripts/locale/es/app.js b/app/assets/javascripts/locale/es/app.js
index 6977625f4d8971eb30a7cde8ca88628ac85c786e..eafcd15acf9858e29ae2751b1b1badedb85c7e06 100644
--- a/app/assets/javascripts/locale/es/app.js
+++ b/app/assets/javascripts/locale/es/app.js
@@ -1 +1 @@
-var locales = locales || {}; locales['es'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-06-07 12:29-0500","Language-Team":"Spanish","Language":"es","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Plural-Forms":"nplurals=2; plural=n != 1;","Last-Translator":"Bob Van Landuyt <bob@gitlab.com>","X-Generator":"Poedit 2.0.2","lang":"es","domain":"app","plural_forms":"nplurals=2; plural=n != 1;"},"About auto deploy":["Acerca del auto despliegue"],"Activity":["Actividad"],"Add Changelog":["Agregar Changelog"],"Add Contribution guide":["Agregar guía de contribución"],"Add License":["Agregar Licencia"],"Add an SSH key to your profile to pull or push via SSH.":["Agregar una clave SSH a tu perfil para actualizar o enviar a través de SSH."],"Add new directory":["Agregar nuevo directorio"],"Archived project! Repository is read-only":["¡Proyecto archivado! El repositorio es de sólo lectura"],"Branch":["Rama","Ramas"],"Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}":["La rama <strong>%{branch_name}</strong> fue creada. Para configurar el auto despliegue, escoge una plantilla Yaml para GitLab CI y envía tus cambios. %{link_to_autodeploy_doc}"],"Branches":["Ramas"],"ByAuthor|by":["por"],"CI configuration":["Configuración de CI"],"Changelog":["Changelog"],"Charts":["Gráficos"],"CiStatusLabel|canceled":["cancelado"],"CiStatusLabel|created":["creado"],"CiStatusLabel|failed":["fallado"],"CiStatusLabel|manual action":["acción manual"],"CiStatusLabel|passed":["pasó"],"CiStatusLabel|passed with warnings":["pasó con advertencias"],"CiStatusLabel|pending":["pendiente"],"CiStatusLabel|skipped":["omitido"],"CiStatusLabel|waiting for manual action":["esperando acción manual"],"CiStatusText|blocked":["bloqueado"],"CiStatusText|canceled":["cancelado"],"CiStatusText|created":["creado"],"CiStatusText|failed":["fallado"],"CiStatusText|manual":["manual"],"CiStatusText|passed":["pasó"],"CiStatusText|pending":["pendiente"],"CiStatusText|skipped":["omitido"],"CiStatus|running":["en ejecución"],"Commit":["Cambio","Cambios"],"CommitMessage|Add %{file_name}":["Agregar %{file_name}"],"Commits":["Cambios"],"Commits|History":["Historial"],"Compare":["Comparar"],"Contribution guide":["Guía de contribución"],"Contributors":["Contribuidores"],"Copy URL to clipboard":["Copiar URL al portapapeles"],"Copy commit SHA to clipboard":["Copiar SHA del cambio al portapapeles"],"Create New Directory":["Crear Nuevo Directorio"],"Create directory":["Crear directorio"],"Create empty bare repository":["Crear repositorio vacío"],"Create merge request":["Crear solicitud de fusión"],"CreateNewFork|Fork":["Bifurcar"],"Custom notification events":["Eventos de notificaciones personalizadas"],"Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.":["Los niveles de notificación personalizados son los mismos que los niveles participantes. Con los niveles de notificación personalizados, también recibirá notificaciones para eventos seleccionados. Para obtener más información, consulte %{notification_link}."],"Cycle Analytics":["Cycle Analytics"],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["Cycle Analytics ofrece una visión general de cuánto tiempo tarda en pasar de idea a producción en su proyecto."],"CycleAnalyticsStage|Code":["Código"],"CycleAnalyticsStage|Issue":["Incidencia"],"CycleAnalyticsStage|Plan":["Planificación"],"CycleAnalyticsStage|Production":["Producción"],"CycleAnalyticsStage|Review":["Revisión"],"CycleAnalyticsStage|Staging":["Puesta en escena"],"CycleAnalyticsStage|Test":["Pruebas"],"Deploy":["Despliegue","Despliegues"],"Directory name":["Nombre del directorio"],"Don't show again":["No mostrar de nuevo"],"Download tar":["Descargar tar"],"Download tar.bz2":["Descargar tar.bz2"],"Download tar.gz":["Descargar tar.gz"],"Download zip":["Descargar zip"],"DownloadArtifacts|Download":["Descargar"],"DownloadSource|Download":["Descargar"],"Files":["Archivos"],"Find by path":["Buscar por ruta"],"Find file":["Buscar archivo"],"FirstPushedBy|First":["Primer"],"FirstPushedBy|pushed by":["enviado por"],"ForkedFromProjectPath|Forked from":["Bifurcado de"],"Forks":["Bifurcaciones"],"From issue creation until deploy to production":["Desde la creación de la incidencia hasta el despliegue a producción"],"From merge request merge until deploy to production":["Desde la integración de la solicitud de fusión hasta el despliegue a producción"],"Go to your fork":["Ir a tu bifurcación"],"GoToYourFork|Fork":["Bifurcación"],"Home":["Inicio"],"Housekeeping successfully started":["Servicio de limpieza iniciado con éxito"],"Import repository":["Importar repositorio"],"Introducing Cycle Analytics":["Introducción a Cycle Analytics"],"LFSStatus|Disabled":["Deshabilitado"],"LFSStatus|Enabled":["Habilitado"],"Last %d day":["Último %d día","Últimos %d días"],"Last Update":["Última actualización"],"Last commit":["Último cambio"],"Leave group":["Abandonar grupo"],"Leave project":["Abandonar proyecto"],"Limited to showing %d event at most":["Limitado a mostrar máximo %d evento","Limitado a mostrar máximo %d eventos"],"Median":["Mediana"],"MissingSSHKeyWarningLink|add an SSH key":["agregar una clave SSH"],"New Issue":["Nueva incidencia","Nuevas incidencias"],"New branch":["Nueva rama"],"New directory":["Nuevo directorio"],"New file":["Nuevo archivo"],"New issue":["Nueva incidencia"],"New merge request":["Nueva solicitud de fusión"],"New snippet":["Nuevo fragmento de código"],"New tag":["Nueva etiqueta"],"No repository":["No hay repositorio"],"Not available":["No disponible"],"Not enough data":["No hay suficientes datos"],"Notification events":["Eventos de notificación"],"NotificationEvent|Close issue":["Cerrar incidencia"],"NotificationEvent|Close merge request":["Cerrar solicitud de fusión"],"NotificationEvent|Failed pipeline":["Pipeline fallido"],"NotificationEvent|Merge merge request":["Integrar solicitud de fusión"],"NotificationEvent|New issue":["Nueva incidencia"],"NotificationEvent|New merge request":["Nueva solicitud de fusión"],"NotificationEvent|New note":["Nueva nota"],"NotificationEvent|Reassign issue":["Reasignar incidencia"],"NotificationEvent|Reassign merge request":["Reasignar solicitud de fusión"],"NotificationEvent|Reopen issue":["Reabrir incidencia"],"NotificationEvent|Successful pipeline":["Pipeline exitoso"],"NotificationLevel|Custom":["Personalizado"],"NotificationLevel|Disabled":["Deshabilitado"],"NotificationLevel|Global":["Global"],"NotificationLevel|On mention":["Cuando me mencionan"],"NotificationLevel|Participate":["Participación"],"NotificationLevel|Watch":["Vigilancia"],"OpenedNDaysAgo|Opened":["Abierto"],"Pipeline Health":["Estado del Pipeline"],"Project '%{project_name}' queued for deletion.":["Proyecto ‘%{project_name}’ en cola para eliminación."],"Project '%{project_name}' was successfully created.":["Proyecto ‘%{project_name}’ fue creado satisfactoriamente."],"Project '%{project_name}' was successfully updated.":["Proyecto ‘%{project_name}’ fue actualizado satisfactoriamente."],"Project '%{project_name}' will be deleted.":["Proyecto ‘%{project_name}’ será eliminado."],"Project access must be granted explicitly to each user.":["El acceso al proyecto debe concederse explícitamente a cada usuario."],"Project export could not be deleted.":["No se pudo eliminar la exportación del proyecto."],"Project export has been deleted.":["La exportación del proyecto ha sido eliminada."],"Project export link has expired. Please generate a new export from your project settings.":["El enlace de exportación del proyecto ha caducado. Por favor, genera una nueva exportación desde la configuración del proyecto."],"Project export started. A download link will be sent by email.":["Se inició la exportación del proyecto. Se enviará un enlace de descarga por correo electrónico."],"Project home":["Inicio del proyecto"],"ProjectFeature|Disabled":["Deshabilitada"],"ProjectFeature|Everyone with access":["Todos con acceso"],"ProjectFeature|Only team members":["Solo miembros del equipo"],"ProjectFileTree|Name":["Nombre"],"ProjectLastActivity|Never":["Nunca"],"ProjectLifecycle|Stage":["Etapa"],"ProjectNetworkGraph|Graph":["Historial gráfico"],"Read more":["Leer más"],"Readme":["Readme"],"RefSwitcher|Branches":["Ramas"],"RefSwitcher|Tags":["Etiquetas"],"Related Commits":["Cambios Relacionados"],"Related Deployed Jobs":["Trabajos Desplegados Relacionados"],"Related Issues":["Incidencias Relacionadas"],"Related Jobs":["Trabajos Relacionados"],"Related Merge Requests":["Solicitudes de fusión Relacionadas"],"Related Merged Requests":["Solicitudes de fusión Relacionadas"],"Remind later":["Recordar después"],"Remove project":["Eliminar proyecto"],"Request Access":["Solicitar acceso"],"Search branches and tags":["Buscar ramas y etiquetas"],"Select Archive Format":["Seleccionar formato de archivo"],"Set a password on your account to pull or push via %{protocol}":["Establezca una contraseña en su cuenta para actualizar o enviar a través de% {protocol}"],"Set up CI":["Configurar CI"],"Set up Koding":["Configurar Koding"],"Set up auto deploy":["Configurar auto despliegue"],"SetPasswordToCloneLink|set a password":["establecer una contraseña"],"Showing %d event":["Mostrando %d evento","Mostrando %d eventos"],"Source code":["Código fuente"],"StarProject|Star":["Destacar"],"Switch branch/tag":["Cambiar rama/etiqueta"],"Tag":["Etiqueta","Etiquetas"],"Tags":["Etiquetas"],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":["La etapa de desarrollo muestra el tiempo desde el primer cambio hasta la creación de la solicitud de fusión. Los datos serán automáticamente incorporados aquí una vez creada tu primera solicitud de fusión."],"The collection of events added to the data gathered for that stage.":["La colección de eventos agregados a los datos recopilados para esa etapa."],"The fork relationship has been removed.":["La relación con la bifurcación se ha eliminado."],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["La etapa de incidencia muestra el tiempo que toma desde la creación de un tema hasta asignar el tema a un hito, o añadir el tema a una lista en el panel de temas. Empieza a crear temas para ver los datos de esta etapa."],"The phase of the development lifecycle.":["La etapa del ciclo de vida de desarrollo."],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["La etapa de planificación muestra el tiempo desde el paso anterior hasta el envío de tu primera confirmación. Este tiempo se añadirá automáticamente una vez que usted envíe el primer cambio."],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["La etapa de producción muestra el tiempo total que tarda entre la creación de una incidencia y el despliegue del código a producción. Los datos se añadirán automáticamente una vez haya finalizado por completo el ciclo de idea a producción."],"The project can be accessed by any logged in user.":["El proyecto puede ser accedido por cualquier usuario conectado."],"The project can be accessed without any authentication.":["El proyecto puede accederse sin ninguna autenticación."],"The repository for this project does not exist.":["El repositorio para este proyecto no existe."],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["La etapa de revisión muestra el tiempo desde la creación de la solicitud de fusión hasta que los cambios se fusionaron. Los datos se añadirán automáticamente después de fusionar su primera solicitud de fusión."],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["La etapa de puesta en escena muestra el tiempo entre la fusión y el despliegue de código en el entorno de producción. Los datos se añadirán automáticamente una vez que se despliega a producción por primera vez."],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["La etapa de pruebas muestra el tiempo que GitLab CI toma para ejecutar cada pipeline para la solicitud de fusión relacionada. Los datos se añadirán automáticamente luego de que el primer pipeline termine de ejecutarse."],"The time taken by each data entry gathered by that stage.":["El tiempo utilizado por cada entrada de datos obtenido por esa etapa."],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["El valor en el punto medio de una serie de valores observados. Por ejemplo, entre 3, 5, 9, la mediana es 5. Entre 3, 5, 7, 8, la mediana es (5 + 7) / 2 = 6."],"This means you can not push code until you create an empty repository or import existing one.":["Esto significa que no puede enviar código hasta que cree un repositorio vacío o importe uno existente."],"Time before an issue gets scheduled":["Tiempo antes de que una incidencia sea programada"],"Time before an issue starts implementation":["Tiempo antes de que empieze la implementación de una incidencia"],"Time between merge request creation and merge/close":["Tiempo entre la creación de la solicitud de fusión y la integración o cierre de ésta"],"Time until first merge request":["Tiempo hasta la primera solicitud de fusión"],"Timeago|%s days ago":["hace %s días"],"Timeago|%s days remaining":["%s días restantes"],"Timeago|%s hours remaining":["%s horas restantes"],"Timeago|%s minutes ago":["hace %s minutos"],"Timeago|%s minutes remaining":["%s minutos restantes"],"Timeago|%s months ago":["hace %s meses"],"Timeago|%s months remaining":["%s meses restantes"],"Timeago|%s seconds remaining":["%s segundos restantes"],"Timeago|%s weeks ago":["hace %s semanas"],"Timeago|%s weeks remaining":["%s semanas restantes"],"Timeago|%s years ago":["hace %s años"],"Timeago|%s years remaining":["%s años restantes"],"Timeago|1 day remaining":["1 día restante"],"Timeago|1 hour remaining":["1 hora restante"],"Timeago|1 minute remaining":["1 minuto restante"],"Timeago|1 month remaining":["1 mes restante"],"Timeago|1 week remaining":["1 semana restante"],"Timeago|1 year remaining":["1 año restante"],"Timeago|Past due":["Atrasado"],"Timeago|a day ago":["hace un día"],"Timeago|a month ago":["hace 1 mes"],"Timeago|a week ago":["hace 1 semana"],"Timeago|a while":["hace un momento"],"Timeago|a year ago":["hace 1 año"],"Timeago|about %s hours ago":["hace alrededor de %s horas"],"Timeago|about a minute ago":["hace alrededor de 1 minuto"],"Timeago|about an hour ago":["hace alrededor de 1 hora"],"Timeago|in %s days":["en %s días"],"Timeago|in %s hours":["en %s horas"],"Timeago|in %s minutes":["en %s minutos"],"Timeago|in %s months":["en %s meses"],"Timeago|in %s seconds":["en %s segundos"],"Timeago|in %s weeks":["en %s semanas"],"Timeago|in %s years":["en %s años"],"Timeago|in 1 day":["en 1 día"],"Timeago|in 1 hour":["en 1 hora"],"Timeago|in 1 minute":["en 1 minuto"],"Timeago|in 1 month":["en 1 mes"],"Timeago|in 1 week":["en 1 semana"],"Timeago|in 1 year":["en 1 año"],"Timeago|less than a minute ago":["hace menos de 1 minuto"],"Time|hr":["hr","hrs"],"Time|min":["min","mins"],"Time|s":["s"],"Total Time":["Tiempo Total"],"Total test time for all commits/merges":["Tiempo total de pruebas para todos los cambios o integraciones"],"Unstar":["No Destacar"],"Upload New File":["Subir nuevo archivo"],"Upload file":["Subir archivo"],"Use your global notification setting":["Utiliza tu configuración de notificación global"],"VisibilityLevel|Internal":["Interno"],"VisibilityLevel|Private":["Privado"],"VisibilityLevel|Public":["Público"],"Want to see the data? Please ask an administrator for access.":["¿Quieres ver los datos? Por favor pide acceso al administrador."],"We don't have enough data to show this stage.":["No hay suficientes datos para mostrar en esta etapa."],"Withdraw Access Request":["Retirar Solicitud de Acceso"],"You are going to remove %{project_name_with_namespace}.\\nRemoved project CANNOT be restored!\\nAre you ABSOLUTELY sure?":["Va a eliminar %{project_name_with_namespace}.\\n¡El proyecto eliminado NO puede ser restaurado!\\n¿Estás TOTALMENTE seguro?"],"You are going to remove the fork relationship to source project %{forked_from_project}.  Are you ABSOLUTELY sure?":["Vas a eliminar el enlace de la bifurcación con el proyecto original %{forked_from_project}. ¿Estás TOTALMENTE seguro?"],"You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?":["Vas a transferir %{project_name_with_namespace} a otro propietario. ¿Estás TOTALMENTE seguro?"],"You can only add files when you are on a branch":["Sólo puede agregar archivos cuando estas en una rama"],"You must sign in to star a project":["Debes iniciar sesión para destacar un proyecto"],"You need permission.":["Necesitas permisos."],"You will not get any notifications via email":["No recibirás ninguna notificación por correo electrónico"],"You will only receive notifications for the events you choose":["Solo recibirás notificaciones de los eventos que elijas"],"You will only receive notifications for threads you have participated in":["Solo recibirás notificaciones de los temas en los que has participado"],"You will receive notifications for any activity":["Recibirás notificaciones para cualquier actividad"],"You will receive notifications only for comments in which you were @mentioned":["Recibirás notificaciones sólo para los comentarios en los que se te mencionó"],"You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account":["No podrás actualizar o enviar código al proyecto a través de %{protocol} hasta que %{set_password_link} en tu cuenta"],"You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile":["No podrás actualizar o enviar código al proyecto a través de SSH hasta que %{add_ssh_key_link} en su perfil"],"Your name":["Tu nombre"],"committed":["cambió"],"day":["día","días"],"notification emails":["correos electrónicos de notificación"]}}};
\ No newline at end of file
+var locales = locales || {}; locales['es'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-06-15 21:59-0500","Language-Team":"Spanish","Language":"es","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Plural-Forms":"nplurals=2; plural=n != 1;","Last-Translator":"Bob Van Landuyt <bob@gitlab.com>","X-Generator":"Poedit 2.0.2","POT-Creation-Date":"2017-06-15 21:59-0500","lang":"es","domain":"app","plural_forms":"nplurals=2; plural=n != 1;"},"%{commit_author_link} committed %{commit_timeago}":["%{commit_author_link} cambió %{commit_timeago}"],"About auto deploy":["Acerca del auto despliegue"],"Active":["Activo"],"Activity":["Actividad"],"Add Changelog":["Agregar Changelog"],"Add Contribution guide":["Agregar guía de contribución"],"Add License":["Agregar Licencia"],"Add an SSH key to your profile to pull or push via SSH.":["Agregar una clave SSH a tu perfil para actualizar o enviar a través de SSH."],"Add new directory":["Agregar nuevo directorio"],"Archived project! Repository is read-only":["¡Proyecto archivado! El repositorio es de solo lectura"],"Are you sure you want to delete this pipeline schedule?":["¿Estás seguro que deseas eliminar esta programación del pipeline?"],"Attach a file by drag &amp; drop or %{upload_link}":["Adjunte un archivo arrastrando &amp; soltando o %{upload_link}"],"Branch":["Rama","Ramas"],"Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}":["La rama <strong>%{branch_name}</strong> fue creada. Para configurar el auto despliegue, escoge una plantilla Yaml para GitLab CI y envía tus cambios. %{link_to_autodeploy_doc}"],"Branches":["Ramas"],"Browse files":["Examinar los archivos"],"ByAuthor|by":["por"],"CI configuration":["Configuración de CI"],"Cancel":["Cancelar"],"ChangeTypeActionLabel|Pick into branch":["Escoger en la rama"],"ChangeTypeActionLabel|Revert in branch":["Revertir en la rama"],"ChangeTypeAction|Cherry-pick":["Cherry-pick"],"ChangeTypeAction|Revert":["Revertir"],"Changelog":["Changelog"],"Charts":["Gráficos"],"Cherry-pick this commit":["Escoger este cambio"],"Cherry-pick this merge request":["Escoger esta solicitud de fusión"],"CiStatusLabel|canceled":["cancelado"],"CiStatusLabel|created":["creado"],"CiStatusLabel|failed":["fallido"],"CiStatusLabel|manual action":["acción manual"],"CiStatusLabel|passed":["pasó"],"CiStatusLabel|passed with warnings":["pasó con advertencias"],"CiStatusLabel|pending":["pendiente"],"CiStatusLabel|skipped":["omitido"],"CiStatusLabel|waiting for manual action":["esperando acción manual"],"CiStatusText|blocked":["bloqueado"],"CiStatusText|canceled":["cancelado"],"CiStatusText|created":["creado"],"CiStatusText|failed":["fallado"],"CiStatusText|manual":["manual"],"CiStatusText|passed":["pasó"],"CiStatusText|pending":["pendiente"],"CiStatusText|skipped":["omitido"],"CiStatus|running":["en ejecución"],"Commit":["Cambio","Cambios"],"Commit message":["Mensaje del cambio"],"CommitBoxTitle|Commit":["Cambio"],"CommitMessage|Add %{file_name}":["Agregar %{file_name}"],"Commits":["Cambios"],"Commits|History":["Historial"],"Committed by":["Enviado por"],"Compare":["Comparar"],"Contribution guide":["Guía de contribución"],"Contributors":["Contribuidores"],"Copy URL to clipboard":["Copiar URL al portapapeles"],"Copy commit SHA to clipboard":["Copiar SHA del cambio al portapapeles"],"Create New Directory":["Crear Nuevo Directorio"],"Create directory":["Crear directorio"],"Create empty bare repository":["Crear repositorio vacío"],"Create merge request":["Crear solicitud de fusión"],"Create new...":["Crear nuevo..."],"CreateNewFork|Fork":["Bifurcar"],"CreateTag|Tag":["Etiqueta"],"Cron Timezone":["Zona horaria del Cron"],"Cron syntax":["Sintaxis de Cron"],"Custom notification events":["Eventos de notificaciones personalizadas"],"Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.":["Los niveles de notificación personalizados son los mismos que los niveles participantes. Con los niveles de notificación personalizados, también recibirá notificaciones para eventos seleccionados. Para obtener más información, consulte %{notification_link}."],"Cycle Analytics":["Cycle Analytics"],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["Cycle Analytics ofrece una visión general de cuánto tiempo tarda en pasar de idea a producción en su proyecto."],"CycleAnalyticsStage|Code":["Código"],"CycleAnalyticsStage|Issue":["Incidencia"],"CycleAnalyticsStage|Plan":["Planificación"],"CycleAnalyticsStage|Production":["Producción"],"CycleAnalyticsStage|Review":["Revisión"],"CycleAnalyticsStage|Staging":["Puesta en escena"],"CycleAnalyticsStage|Test":["Pruebas"],"Define a custom pattern with cron syntax":["Definir un patrón personalizado con la sintaxis de cron"],"Delete":["Eliminar"],"Deploy":["Despliegue","Despliegues"],"Description":["Descripción"],"Directory name":["Nombre del directorio"],"Don't show again":["No mostrar de nuevo"],"Download":["Descargar"],"Download tar":["Descargar tar"],"Download tar.bz2":["Descargar tar.bz2"],"Download tar.gz":["Descargar tar.gz"],"Download zip":["Descargar zip"],"DownloadArtifacts|Download":["Descargar"],"DownloadCommit|Email Patches":["Parches por correo electrónico"],"DownloadCommit|Plain Diff":["Diferencias en texto plano"],"DownloadSource|Download":["Descargar"],"Edit":["Editar"],"Edit Pipeline Schedule %{id}":["Editar Programación del Pipeline %{id}"],"Every day (at 4:00am)":["Todos los días (a las 4:00 am)"],"Every month (on the 1st at 4:00am)":["Todos los meses (el día 1 a las 4:00 am)"],"Every week (Sundays at 4:00am)":["Todas las semanas (domingos a las 4:00 am)"],"Failed to change the owner":["Error al cambiar el propietario"],"Failed to remove the pipeline schedule":["Error al eliminar la programación del pipeline"],"Files":["Archivos"],"Find by path":["Buscar por ruta"],"Find file":["Buscar archivo"],"FirstPushedBy|First":["Primer"],"FirstPushedBy|pushed by":["enviado por"],"Fork":["Bifurcación","Bifurcaciones"],"ForkedFromProjectPath|Forked from":["Bifurcado de"],"From issue creation until deploy to production":["Desde la creación de la incidencia hasta el despliegue a producción"],"From merge request merge until deploy to production":["Desde la integración de la solicitud de fusión hasta el despliegue a producción"],"Go to your fork":["Ir a tu bifurcación"],"GoToYourFork|Fork":["Bifurcación"],"Home":["Inicio"],"Housekeeping successfully started":["Servicio de limpieza iniciado con éxito"],"Import repository":["Importar repositorio"],"Interval Pattern":["Patrón de intervalo"],"Introducing Cycle Analytics":["Introducción a Cycle Analytics"],"LFSStatus|Disabled":["Deshabilitado"],"LFSStatus|Enabled":["Habilitado"],"Last %d day":["Último %d día","Últimos %d días"],"Last Pipeline":["Último Pipeline"],"Last Update":["Última actualización"],"Last commit":["Último cambio"],"Learn more in the":["Más información en la"],"Learn more in the|pipeline schedules documentation":["documentación sobre la programación de pipelines"],"Leave group":["Abandonar grupo"],"Leave project":["Abandonar proyecto"],"Limited to showing %d event at most":["Limitado a mostrar máximo %d evento","Limitado a mostrar máximo %d eventos"],"Median":["Mediana"],"MissingSSHKeyWarningLink|add an SSH key":["agregar una clave SSH"],"New Issue":["Nueva incidencia","Nuevas incidencias"],"New Pipeline Schedule":["Nueva Programación del Pipeline"],"New branch":["Nueva rama"],"New directory":["Nuevo directorio"],"New file":["Nuevo archivo"],"New issue":["Nueva incidencia"],"New merge request":["Nueva solicitud de fusión"],"New schedule":["Nueva programación"],"New snippet":["Nuevo fragmento de código"],"New tag":["Nueva etiqueta"],"No repository":["No hay repositorio"],"No schedules":["No hay programaciones"],"Not available":["No disponible"],"Not enough data":["No hay suficientes datos"],"Notification events":["Eventos de notificación"],"NotificationEvent|Close issue":["Cerrar incidencia"],"NotificationEvent|Close merge request":["Cerrar solicitud de fusión"],"NotificationEvent|Failed pipeline":["Pipeline fallido"],"NotificationEvent|Merge merge request":["Integrar solicitud de fusión"],"NotificationEvent|New issue":["Nueva incidencia"],"NotificationEvent|New merge request":["Nueva solicitud de fusión"],"NotificationEvent|New note":["Nueva nota"],"NotificationEvent|Reassign issue":["Reasignar incidencia"],"NotificationEvent|Reassign merge request":["Reasignar solicitud de fusión"],"NotificationEvent|Reopen issue":["Reabrir incidencia"],"NotificationEvent|Successful pipeline":["Pipeline exitoso"],"NotificationLevel|Custom":["Personalizado"],"NotificationLevel|Disabled":["Deshabilitado"],"NotificationLevel|Global":["Global"],"NotificationLevel|On mention":["Cuando me mencionan"],"NotificationLevel|Participate":["Participación"],"NotificationLevel|Watch":["Vigilancia"],"OfSearchInADropdown|Filter":["Filtrar"],"OpenedNDaysAgo|Opened":["Abierto"],"Options":["Opciones"],"Owner":["Propietario"],"Pipeline":["Pipeline"],"Pipeline Health":["Estado del Pipeline"],"Pipeline Schedule":["Programación del Pipeline"],"Pipeline Schedules":["Programaciones de los Pipelines"],"PipelineSchedules|Activated":["Activado"],"PipelineSchedules|Active":["Activos"],"PipelineSchedules|All":["Todos"],"PipelineSchedules|Inactive":["Inactivos"],"PipelineSchedules|Next Run":["Próxima Ejecución"],"PipelineSchedules|None":["Ninguno"],"PipelineSchedules|Provide a short description for this pipeline":["Proporcione una breve descripción para este pipeline"],"PipelineSchedules|Take ownership":["Tomar posesión"],"PipelineSchedules|Target":["Destino"],"PipelineSheduleIntervalPattern|Custom":["Personalizado"],"Pipeline|with stage":["con etapa"],"Pipeline|with stages":["con etapas"],"Project '%{project_name}' queued for deletion.":["Proyecto ‘%{project_name}’ en cola para eliminación."],"Project '%{project_name}' was successfully created.":["Proyecto ‘%{project_name}’ fue creado satisfactoriamente."],"Project '%{project_name}' was successfully updated.":["Proyecto ‘%{project_name}’ fue actualizado satisfactoriamente."],"Project '%{project_name}' will be deleted.":["Proyecto ‘%{project_name}’ será eliminado."],"Project access must be granted explicitly to each user.":["El acceso al proyecto debe concederse explícitamente a cada usuario."],"Project export could not be deleted.":["No se pudo eliminar la exportación del proyecto."],"Project export has been deleted.":["La exportación del proyecto ha sido eliminada."],"Project export link has expired. Please generate a new export from your project settings.":["El enlace de exportación del proyecto ha caducado. Por favor, genera una nueva exportación desde la configuración del proyecto."],"Project export started. A download link will be sent by email.":["Se inició la exportación del proyecto. Se enviará un enlace de descarga por correo electrónico."],"Project home":["Inicio del proyecto"],"ProjectFeature|Disabled":["Deshabilitada"],"ProjectFeature|Everyone with access":["Todos con acceso"],"ProjectFeature|Only team members":["Solo miembros del equipo"],"ProjectFileTree|Name":["Nombre"],"ProjectLastActivity|Never":["Nunca"],"ProjectLifecycle|Stage":["Etapa"],"ProjectNetworkGraph|Graph":["Historial gráfico"],"Read more":["Leer más"],"Readme":["Léeme"],"RefSwitcher|Branches":["Ramas"],"RefSwitcher|Tags":["Etiquetas"],"Related Commits":["Cambios Relacionados"],"Related Deployed Jobs":["Trabajos Desplegados Relacionados"],"Related Issues":["Incidencias Relacionadas"],"Related Jobs":["Trabajos Relacionados"],"Related Merge Requests":["Solicitudes de fusión Relacionadas"],"Related Merged Requests":["Solicitudes de fusión Relacionadas"],"Remind later":["Recordar después"],"Remove project":["Eliminar proyecto"],"Request Access":["Solicitar acceso"],"Revert this commit":["Revertir este cambio"],"Revert this merge request":["Revertir esta solicitud de fusión"],"Save pipeline schedule":["Guardar programación del pipeline"],"Schedule a new pipeline":["Programar un nuevo pipeline"],"Scheduling Pipelines":["Programación de Pipelines"],"Search branches and tags":["Buscar ramas y etiquetas"],"Select Archive Format":["Seleccionar formato de archivo"],"Select a timezone":["Selecciona una zona horaria"],"Select target branch":["Selecciona una rama de destino"],"Set a password on your account to pull or push via %{protocol}":["Establezca una contraseña en su cuenta para actualizar o enviar a través de %{protocol}"],"Set up CI":["Configurar CI"],"Set up Koding":["Configurar Koding"],"Set up auto deploy":["Configurar auto despliegue"],"SetPasswordToCloneLink|set a password":["establecer una contraseña"],"Showing %d event":["Mostrando %d evento","Mostrando %d eventos"],"Source code":["Código fuente"],"StarProject|Star":["Destacar"],"Start a %{new_merge_request} with these changes":["Iniciar una %{new_merge_request} con estos cambios"],"Switch branch/tag":["Cambiar rama/etiqueta"],"Tag":["Etiqueta","Etiquetas"],"Tags":["Etiquetas"],"Target Branch":["Rama de destino"],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":["La etapa de desarrollo muestra el tiempo desde el primer cambio hasta la creación de la solicitud de fusión. Los datos serán automáticamente incorporados aquí una vez creada tu primera solicitud de fusión."],"The collection of events added to the data gathered for that stage.":["La colección de eventos agregados a los datos recopilados para esa etapa."],"The fork relationship has been removed.":["La relación con la bifurcación se ha eliminado."],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["La etapa de incidencia muestra el tiempo que toma desde la creación de un tema hasta asignar el tema a un hito, o añadir el tema a una lista en el panel de temas. Empieza a crear temas para ver los datos de esta etapa."],"The phase of the development lifecycle.":["La etapa del ciclo de vida de desarrollo."],"The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user.":["La programación de pipelines ejecuta pipelines en el futuro, repetidamente, para ramas o etiquetas específicas. Los pipelines programados heredarán acceso limitado al proyecto basado en su usuario asociado."],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["La etapa de planificación muestra el tiempo desde el paso anterior hasta el envío de tu primera confirmación. Este tiempo se añadirá automáticamente una vez que usted envíe el primer cambio."],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["La etapa de producción muestra el tiempo total que tarda entre la creación de una incidencia y el despliegue del código a producción. Los datos se añadirán automáticamente una vez haya finalizado por completo el ciclo de idea a producción."],"The project can be accessed by any logged in user.":["El proyecto puede ser accedido por cualquier usuario conectado."],"The project can be accessed without any authentication.":["El proyecto puede accederse sin ninguna autenticación."],"The repository for this project does not exist.":["El repositorio para este proyecto no existe."],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["La etapa de revisión muestra el tiempo desde la creación de la solicitud de fusión hasta que los cambios se fusionaron. Los datos se añadirán automáticamente después de fusionar su primera solicitud de fusión."],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["La etapa de puesta en escena muestra el tiempo entre la fusión y el despliegue de código en el entorno de producción. Los datos se añadirán automáticamente una vez que se despliega a producción por primera vez."],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["La etapa de pruebas muestra el tiempo que GitLab CI toma para ejecutar cada pipeline para la solicitud de fusión relacionada. Los datos se añadirán automáticamente luego de que el primer pipeline termine de ejecutarse."],"The time taken by each data entry gathered by that stage.":["El tiempo utilizado por cada entrada de datos obtenido por esa etapa."],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["El valor en el punto medio de una serie de valores observados. Por ejemplo, entre 3, 5, 9, la mediana es 5. Entre 3, 5, 7, 8, la mediana es (5 + 7) / 2 = 6."],"This means you can not push code until you create an empty repository or import existing one.":["Esto significa que no puede enviar código hasta que cree un repositorio vacío o importe uno existente."],"Time before an issue gets scheduled":["Tiempo antes de que una incidencia sea programada"],"Time before an issue starts implementation":["Tiempo antes de que empieze la implementación de una incidencia"],"Time between merge request creation and merge/close":["Tiempo entre la creación de la solicitud de fusión y la integración o cierre de ésta"],"Time until first merge request":["Tiempo hasta la primera solicitud de fusión"],"Timeago|%s days ago":["hace %s días"],"Timeago|%s days remaining":["%s días restantes"],"Timeago|%s hours remaining":["%s horas restantes"],"Timeago|%s minutes ago":["hace %s minutos"],"Timeago|%s minutes remaining":["%s minutos restantes"],"Timeago|%s months ago":["hace %s meses"],"Timeago|%s months remaining":["%s meses restantes"],"Timeago|%s seconds remaining":["%s segundos restantes"],"Timeago|%s weeks ago":["hace %s semanas"],"Timeago|%s weeks remaining":["%s semanas restantes"],"Timeago|%s years ago":["hace %s años"],"Timeago|%s years remaining":["%s años restantes"],"Timeago|1 day remaining":["1 día restante"],"Timeago|1 hour remaining":["1 hora restante"],"Timeago|1 minute remaining":["1 minuto restante"],"Timeago|1 month remaining":["1 mes restante"],"Timeago|1 week remaining":["1 semana restante"],"Timeago|1 year remaining":["1 año restante"],"Timeago|Past due":["Atrasado"],"Timeago|a day ago":["hace un día"],"Timeago|a month ago":["hace un mes"],"Timeago|a week ago":["hace una semana"],"Timeago|a while":["hace un momento"],"Timeago|a year ago":["hace un año"],"Timeago|about %s hours ago":["hace alrededor de %s horas"],"Timeago|about a minute ago":["hace alrededor de 1 minuto"],"Timeago|about an hour ago":["hace alrededor de 1 hora"],"Timeago|in %s days":["en %s días"],"Timeago|in %s hours":["en %s horas"],"Timeago|in %s minutes":["en %s minutos"],"Timeago|in %s months":["en %s meses"],"Timeago|in %s seconds":["en %s segundos"],"Timeago|in %s weeks":["en %s semanas"],"Timeago|in %s years":["en %s años"],"Timeago|in 1 day":["en 1 día"],"Timeago|in 1 hour":["en 1 hora"],"Timeago|in 1 minute":["en 1 minuto"],"Timeago|in 1 month":["en 1 mes"],"Timeago|in 1 week":["en 1 semana"],"Timeago|in 1 year":["en 1 año"],"Timeago|less than a minute ago":["hace menos de 1 minuto"],"Time|hr":["hr","hrs"],"Time|min":["min","mins"],"Time|s":["s"],"Total Time":["Tiempo Total"],"Total test time for all commits/merges":["Tiempo total de pruebas para todos los cambios o integraciones"],"Unstar":["No Destacar"],"Upload New File":["Subir nuevo archivo"],"Upload file":["Subir archivo"],"Use your global notification setting":["Utiliza tu configuración de notificación global"],"VisibilityLevel|Internal":["Interno"],"VisibilityLevel|Private":["Privado"],"VisibilityLevel|Public":["Público"],"Want to see the data? Please ask an administrator for access.":["¿Quieres ver los datos? Por favor pide acceso al administrador."],"We don't have enough data to show this stage.":["No hay suficientes datos para mostrar en esta etapa."],"Withdraw Access Request":["Retirar Solicitud de Acceso"],"You are going to remove %{project_name_with_namespace}.\\nRemoved project CANNOT be restored!\\nAre you ABSOLUTELY sure?":["Va a eliminar %{project_name_with_namespace}.\\n¡El proyecto eliminado NO puede ser restaurado!\\n¿Estás TOTALMENTE seguro?"],"You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?":["Vas a eliminar el enlace de la bifurcación con el proyecto original %{forked_from_project}. ¿Estás TOTALMENTE seguro?"],"You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?":["Vas a transferir %{project_name_with_namespace} a otro propietario. ¿Estás TOTALMENTE seguro?"],"You can only add files when you are on a branch":["Solo puedes agregar archivos cuando estás en una rama"],"You must sign in to star a project":["Debes iniciar sesión para destacar un proyecto"],"You need permission.":["Necesitas permisos."],"You will not get any notifications via email":["No recibirás ninguna notificación por correo electrónico"],"You will only receive notifications for the events you choose":["Solo recibirás notificaciones de los eventos que elijas"],"You will only receive notifications for threads you have participated in":["Solo recibirás notificaciones de los temas en los que has participado"],"You will receive notifications for any activity":["Recibirás notificaciones por cualquier actividad"],"You will receive notifications only for comments in which you were @mentioned":["Recibirás notificaciones solo para los comentarios en los que se te mencionó"],"You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account":["No podrás actualizar o enviar código al proyecto a través de %{protocol} hasta que %{set_password_link} en tu cuenta"],"You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile":["No podrás actualizar o enviar código al proyecto a través de SSH hasta que %{add_ssh_key_link} en su perfil"],"Your name":["Tu nombre"],"day":["día","días"],"new merge request":["nueva solicitud de fusión"],"notification emails":["correos electrónicos de notificación"],"parent":["padre","padres"]}}};
\ No newline at end of file
diff --git a/app/assets/javascripts/locale/zh_CN/app.js b/app/assets/javascripts/locale/zh_CN/app.js
index d1335cfbc0fd557440f7c925a16116853e0b1561..9c28e4e46273a90bdc448eda558b73b16effb9a9 100644
--- a/app/assets/javascripts/locale/zh_CN/app.js
+++ b/app/assets/javascripts/locale/zh_CN/app.js
@@ -1 +1 @@
-var locales = locales || {}; locales['zh_CN'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","PO-Revision-Date":"2017-05-04 19:24-0500","Last-Translator":"HuangTao <htve@outlook.com>, 2017","Language-Team":"Chinese (China) (https://www.transifex.com/gitlab-zh/teams/75177/zh_CN/)","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","Language":"zh_CN","Plural-Forms":"nplurals=1; plural=0;","lang":"zh_CN","domain":"app","plural_forms":"nplurals=1; plural=0;"},"Are you sure you want to delete this pipeline schedule?":[""],"ByAuthor|by":["作者:"],"Cancel":[""],"Commit":["提交"],"Cron Timezone":[""],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["周期分析概述了项目从想法到产品实现的各阶段所需的时间。"],"CycleAnalyticsStage|Code":["编码"],"CycleAnalyticsStage|Issue":["议题"],"CycleAnalyticsStage|Plan":["计划"],"CycleAnalyticsStage|Production":["生产"],"CycleAnalyticsStage|Review":["评审"],"CycleAnalyticsStage|Staging":["预发布"],"CycleAnalyticsStage|Test":["测试"],"Delete":[""],"Deploy":["部署"],"Description":[""],"Edit":[""],"Edit Pipeline Schedule %{id}":[""],"Failed to change the owner":[""],"Failed to remove the pipeline schedule":[""],"Filter":[""],"FirstPushedBy|First":["首次推送"],"FirstPushedBy|pushed by":["推送者:"],"From issue creation until deploy to production":["从创建议题到部署至生产环境"],"From merge request merge until deploy to production":["从合并请求被合并后到部署至生产环境"],"Interval Pattern":[""],"Introducing Cycle Analytics":["周期分析简介"],"Last %d day":["最后 %d 天"],"Last Pipeline":[""],"Limited to showing %d event at most":["最多显示 %d 个事件"],"Median":["中位数"],"New Issue":["新议题"],"New Pipeline Schedule":[""],"No schedules":[""],"Not available":["数据不足"],"Not enough data":["数据不足"],"OpenedNDaysAgo|Opened":["开始于"],"Owner":[""],"Pipeline Health":["流水线健康指标"],"Pipeline Schedule":[""],"Pipeline Schedules":[""],"PipelineSchedules|Activated":[""],"PipelineSchedules|Active":[""],"PipelineSchedules|All":[""],"PipelineSchedules|Inactive":[""],"PipelineSchedules|Next Run":[""],"PipelineSchedules|None":[""],"PipelineSchedules|Provide a short description for this pipeline":[""],"PipelineSchedules|Take ownership":[""],"PipelineSchedules|Target":[""],"ProjectLifecycle|Stage":["项目生命周期"],"Read more":["了解更多"],"Related Commits":["相关的提交"],"Related Deployed Jobs":["相关的部署作业"],"Related Issues":["相关的议题"],"Related Jobs":["相关的作业"],"Related Merge Requests":["相关的合并请求"],"Related Merged Requests":["相关已合并的合并请求"],"Save pipeline schedule":[""],"Schedule a new pipeline":[""],"Select a timezone":[""],"Select target branch":[""],"Showing %d event":["显示 %d 个事件"],"Target Branch":[""],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":["编码阶段概述了从第一次提交到创建合并请求的时间。创建第一个合并请求后,数据将自动添加到此处。"],"The collection of events added to the data gathered for that stage.":["与该阶段相关的事件。"],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["议题阶段概述了从创建议题到将议题设置里程碑或将议题添加到议题看板的时间。开始创建议题以查看此阶段的数据。"],"The phase of the development lifecycle.":["项目生命周期中的各个阶段。"],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["计划阶段概述了从议题添加到日程后到推送首次提交的时间。当首次推送提交后,数据将自动添加到此处。"],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["生产阶段概述了从创建一个议题到将代码部署到生产环境的总时间。当完成想法到部署生产的循环,数据将自动添加到此处。"],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["评审阶段概述了从创建合并请求到被合并的时间。当创建第一个合并请求后,数据将自动添加到此处。"],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["预发布阶段概述了从合并请求被合并到部署至生产环境的总时间。首次部署到生产环境后,数据将自动添加到此处。"],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["测试阶段概述了GitLab CI为相关合并请求运行每个流水线所需的时间。当第一个流水线运行完成后,数据将自动添加到此处。"],"The time taken by each data entry gathered by that stage.":["该阶段每条数据所花的时间"],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["中位数是一个数列中最中间的值。例如在 3、5、9 之间,中位数是 5。在 3、5、7、8 之间,中位数是 (5 + 7)/ 2 = 6。"],"Time before an issue gets scheduled":["议题被列入日程表的时间"],"Time before an issue starts implementation":["开始进行编码前的时间"],"Time between merge request creation and merge/close":["从创建合并请求到被合并或关闭的时间"],"Time until first merge request":["创建第一个合并请求之前的时间"],"Time|hr":["小时"],"Time|min":["分钟"],"Time|s":["秒"],"Total Time":["总时间"],"Total test time for all commits/merges":["所有提交和合并的总测试时间"],"Want to see the data? Please ask an administrator for access.":["权限不足。如需查看相关数据,请向管理员申请权限。"],"We don't have enough data to show this stage.":["该阶段的数据不足,无法显示。"],"You need permission.":["您需要相关的权限。"],"day":["天"]}}};
\ No newline at end of file
+var locales = locales || {}; locales['zh_CN'] = {"domain":"app","locale_data":{"app":{"":{"Project-Id-Version":"gitlab 1.0.0","Report-Msgid-Bugs-To":"","POT-Creation-Date":"2017-06-15 21:59-0500","MIME-Version":"1.0","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","PO-Revision-Date":"2017-06-19 09:57-0400","Last-Translator":"Huang Tao <htve@outlook.com>","Language-Team":"Chinese (China) (https://translate.zanata.org/project/view/GitLab)","Language":"zh-CN","X-Generator":"Zanata 3.9.6","Plural-Forms":"nplurals=1; plural=0","lang":"zh_CN","domain":"app","plural_forms":"nplurals=1; plural=0"},"%{commit_author_link} committed %{commit_timeago}":["由 %{commit_author_link} 提交于 %{commit_timeago}"],"About auto deploy":["关于自动部署"],"Active":["启用"],"Activity":["活动"],"Add Changelog":["添加更新日志"],"Add Contribution guide":["添加贡献指南"],"Add License":["添加许可证"],"Add an SSH key to your profile to pull or push via SSH.":["新建一个用于推送或拉取的 SSH 秘钥到账号中。"],"Add new directory":["添加目录"],"Archived project! Repository is read-only":["项目已归档!存储库为只读状态"],"Are you sure you want to delete this pipeline schedule?":["确定要删除此流水线计划吗?"],"Attach a file by drag &amp; drop or %{upload_link}":["拖放文件到此处或者 %{upload_link}"],"Branch":["分支"],"Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}":["已创建分支 <strong>%{branch_name}</strong> 。如需设置自动部署, 请选择合适的 GitLab CI Yaml 模板并提交更改。%{link_to_autodeploy_doc}"],"Branches":["分支"],"Browse files":["浏览文件"],"ByAuthor|by":["作者:"],"CI configuration":["CI 配置"],"Cancel":["取消"],"ChangeTypeActionLabel|Pick into branch":["选择分支"],"ChangeTypeActionLabel|Revert in branch":["还原分支"],"ChangeTypeAction|Cherry-pick":["优选"],"ChangeTypeAction|Revert":["还原"],"Changelog":["更新日志"],"Charts":["统计图"],"Cherry-pick this commit":["优选此提交"],"Cherry-pick this merge request":["优选此合并请求"],"CiStatusLabel|canceled":["已取消"],"CiStatusLabel|created":["已创建"],"CiStatusLabel|failed":["已失败"],"CiStatusLabel|manual action":["手动操作"],"CiStatusLabel|passed":["已通过"],"CiStatusLabel|passed with warnings":["已通过但有警告"],"CiStatusLabel|pending":["等待中"],"CiStatusLabel|skipped":["已跳过"],"CiStatusLabel|waiting for manual action":["等待手动操作"],"CiStatusText|blocked":["已阻塞"],"CiStatusText|canceled":["已取消"],"CiStatusText|created":["已创建"],"CiStatusText|failed":["已失败"],"CiStatusText|manual":["手动操作"],"CiStatusText|passed":["已通过"],"CiStatusText|pending":["等待中"],"CiStatusText|skipped":["已跳过"],"CiStatus|running":["运行中"],"Commit":["提交"],"Commit message":["提交信息"],"CommitBoxTitle|Commit":["提交"],"CommitMessage|Add %{file_name}":["添加 %{file_name}"],"Commits":["提交"],"Commits|History":["历史"],"Committed by":["提交者:"],"Compare":["比较"],"Contribution guide":["贡献指南"],"Contributors":["贡献者"],"Copy URL to clipboard":["复制 URL 到剪贴板"],"Copy commit SHA to clipboard":["复制提交 SHA 的值到剪贴板"],"Create New Directory":["创建新目录"],"Create directory":["创建目录"],"Create empty bare repository":["创建空的存储库"],"Create merge request":["创建合并请求"],"Create new...":["创建..."],"CreateNewFork|Fork":["派生"],"CreateTag|Tag":["标签"],"Cron Timezone":["Cron 时区"],"Cron syntax":["Cron 语法"],"Custom notification events":["自定义通知事件"],"Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}.":["自定义通知级别继承自参与级别。使用自定义通知级别,您会收到参与级别及选定事件的通知。想了解更多信息,请查看 %{notification_link}."],"Cycle Analytics":["周期分析"],"Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project.":["周期分析概述了项目从想法到产品实现的各阶段所需的时间。"],"CycleAnalyticsStage|Code":["编码"],"CycleAnalyticsStage|Issue":["议题"],"CycleAnalyticsStage|Plan":["计划"],"CycleAnalyticsStage|Production":["生产"],"CycleAnalyticsStage|Review":["评审"],"CycleAnalyticsStage|Staging":["预发布"],"CycleAnalyticsStage|Test":["测试"],"Define a custom pattern with cron syntax":["使用 Cron 语法定义自定义模式"],"Delete":["删除"],"Deploy":["部署"],"Description":["描述"],"Directory name":["目录名称"],"Don't show again":["不再显示"],"Download":["下载"],"Download tar":["下载 tar"],"Download tar.bz2":["下载 tar.bz2"],"Download tar.gz":["下载 tar.gz"],"Download zip":["下载 zip"],"DownloadArtifacts|Download":["下载"],"DownloadCommit|Email Patches":["电子邮件补丁"],"DownloadCommit|Plain Diff":["差异文件"],"DownloadSource|Download":["下载"],"Edit":["编辑"],"Edit Pipeline Schedule %{id}":["编辑 %{id} 流水线计划"],"Every day (at 4:00am)":["每日执行(凌晨 4 点)"],"Every month (on the 1st at 4:00am)":["每月执行(每月 1 日凌晨 4 点)"],"Every week (Sundays at 4:00am)":["每周执行(周日凌晨 4 点)"],"Failed to change the owner":["无法变更所有者"],"Failed to remove the pipeline schedule":["无法删除流水线计划"],"Files":["文件"],"Find by path":["按路径查找"],"Find file":["查找文件"],"FirstPushedBy|First":["首次推送"],"FirstPushedBy|pushed by":["推送者:"],"Fork":["派生"],"ForkedFromProjectPath|Forked from":["派生自"],"From issue creation until deploy to production":["从创建议题到部署至生产环境"],"From merge request merge until deploy to production":["从合并请求被合并后到部署至生产环境"],"Go to your fork":["跳转到派生项目"],"GoToYourFork|Fork":["跳转到派生项目"],"Home":["首页"],"Housekeeping successfully started":["已开始维护"],"Import repository":["导入存储库"],"Interval Pattern":["循环周期"],"Introducing Cycle Analytics":["周期分析简介"],"LFSStatus|Disabled":["停用"],"LFSStatus|Enabled":["启用"],"Last %d day":["最近 %d 天"],"Last Pipeline":["最新流水线"],"Last Update":["最后更新"],"Last commit":["最后提交"],"Learn more in the":["了解更多"],"Learn more in the|pipeline schedules documentation":["流水线计划文档"],"Leave group":["退出群组"],"Leave project":["退出项目"],"Limited to showing %d event at most":["最多显示 %d 个事件"],"Median":["中位数"],"MissingSSHKeyWarningLink|add an SSH key":["新建 SSH 公钥"],"New Issue":["新建议题"],"New Pipeline Schedule":["创建流水线计划"],"New branch":["新建分支"],"New directory":["新建目录"],"New file":["新建文件"],"New issue":["新建议题"],"New merge request":["新建合并请求"],"New schedule":["新建计划"],"New snippet":["新建代码片段"],"New tag":["新建标签"],"No repository":["没有存储库"],"No schedules":["没有计划"],"Not available":["数据不足"],"Not enough data":["数据不足"],"Notification events":["通知事件"],"NotificationEvent|Close issue":["关闭议题"],"NotificationEvent|Close merge request":["关闭合并请求"],"NotificationEvent|Failed pipeline":["流水线失败"],"NotificationEvent|Merge merge request":["合并请求被合并"],"NotificationEvent|New issue":["新建议题"],"NotificationEvent|New merge request":["新建合并请求"],"NotificationEvent|New note":["新建评论"],"NotificationEvent|Reassign issue":["重新指派议题"],"NotificationEvent|Reassign merge request":["重新指派合并请求"],"NotificationEvent|Reopen issue":["重启议题"],"NotificationEvent|Successful pipeline":["流水线成功完成"],"NotificationLevel|Custom":["自定义"],"NotificationLevel|Disabled":["停用"],"NotificationLevel|Global":["全局"],"NotificationLevel|On mention":["提及"],"NotificationLevel|Participate":["参与"],"NotificationLevel|Watch":["关注"],"OfSearchInADropdown|Filter":["筛选"],"OpenedNDaysAgo|Opened":["开始于"],"Options":["操作"],"Owner":["所有者"],"Pipeline":["流水线"],"Pipeline Health":["流水线健康指标"],"Pipeline Schedule":["流水线计划"],"Pipeline Schedules":["流水线计划"],"PipelineSchedules|Activated":["是否启用"],"PipelineSchedules|Active":["已启用"],"PipelineSchedules|All":["所有"],"PipelineSchedules|Inactive":["未启用"],"PipelineSchedules|Next Run":["下次运行时间"],"PipelineSchedules|None":["无"],"PipelineSchedules|Provide a short description for this pipeline":["为此流水线提供简短描述"],"PipelineSchedules|Take ownership":["取得所有者"],"PipelineSchedules|Target":["目标"],"PipelineSheduleIntervalPattern|Custom":["自定义"],"Pipeline|with stage":["于阶段"],"Pipeline|with stages":["于阶段"],"Project '%{project_name}' queued for deletion.":["项目 '%{project_name}' 已进入删除队列。"],"Project '%{project_name}' was successfully created.":["项目 '%{project_name}' 已创建成功。"],"Project '%{project_name}' was successfully updated.":["项目 '%{project_name}' 已更新完成。"],"Project '%{project_name}' will be deleted.":["项目 '%{project_name}' 将被删除。"],"Project access must be granted explicitly to each user.":["项目访问权限必须明确授权给每个用户。"],"Project export could not be deleted.":["无法删除项目导出。"],"Project export has been deleted.":["项目导出已被删除。"],"Project export link has expired. Please generate a new export from your project settings.":["项目导出链接已过期。请从项目设置中重新生成项目导出。"],"Project export started. A download link will be sent by email.":["项目导出已开始。下载链接将通过电子邮件发送。"],"Project home":["项目首页"],"ProjectFeature|Disabled":["停用"],"ProjectFeature|Everyone with access":["任何对项目有访问权的人"],"ProjectFeature|Only team members":["只限团队成员"],"ProjectFileTree|Name":["名称"],"ProjectLastActivity|Never":["从未"],"ProjectLifecycle|Stage":["阶段"],"ProjectNetworkGraph|Graph":["分支图"],"Read more":["了解更多"],"Readme":["自述文件"],"RefSwitcher|Branches":["分支"],"RefSwitcher|Tags":["标签"],"Related Commits":["相关的提交"],"Related Deployed Jobs":["相关的部署作业"],"Related Issues":["相关的议题"],"Related Jobs":["相关的作业"],"Related Merge Requests":["相关的合并请求"],"Related Merged Requests":["相关已合并的合并请求"],"Remind later":["稍后提醒"],"Remove project":["删除项目"],"Request Access":["申请权限"],"Revert this commit":["还原此提交"],"Revert this merge request":["还原此合并请求"],"Save pipeline schedule":["保存流水线计划"],"Schedule a new pipeline":["新建流水线计划"],"Scheduling Pipelines":["流水线计划"],"Search branches and tags":["搜索分支和标签"],"Select Archive Format":["选择下载格式"],"Select a timezone":["选择时区"],"Select target branch":["选择目标分支"],"Set a password on your account to pull or push via %{protocol}":["为账号创建一个用于推送或拉取的 %{protocol} 密码。"],"Set up CI":["设置 CI"],"Set up Koding":["设置 Koding"],"Set up auto deploy":["设置自动部署"],"SetPasswordToCloneLink|set a password":["设置密码"],"Showing %d event":["显示 %d 个事件"],"Source code":["源代码"],"StarProject|Star":["星标"],"Start a %{new_merge_request} with these changes":["由此更改 %{new_merge_request}"],"Switch branch/tag":["切换分支/标签"],"Tag":["标签"],"Tags":["标签"],"Target Branch":["目标分支"],"The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.":["编码阶段概述了从第一次提交到创建合并请求的时间。创建第一个合并请求后,数据将自动添加到此处。"],"The collection of events added to the data gathered for that stage.":["与该阶段相关的事件集合。"],"The fork relationship has been removed.":["派生关系已被删除。"],"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.":["议题阶段概述了从创建议题到将议题添加到里程碑或议题看板所花费的时间。创建第一个议题后,数据将自动添加到此处.。"],"The phase of the development lifecycle.":["项目生命周期中的各个阶段。"],"The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user.":["流水线计划会周期性重复运行指定分支或标签的流水线。这些流水线将根据其关联用户继承有限的项目访问权限。"],"The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.":["计划阶段概述了从议题添加到日程到推送首次提交的时间。当首次推送提交后,数据将自动添加到此处。"],"The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.":["生产阶段概述了从创建一个议题到将代码部署到生产环境的总时间。当完成想法到部署生产的循环,数据将自动添加到此处。"],"The project can be accessed by any logged in user.":["该项目允许已登录的用户访问。"],"The project can be accessed without any authentication.":["该项目允许任何人访问。"],"The repository for this project does not exist.":["此项目的存储库不存在。"],"The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.":["评审阶段概述了从创建合并请求到被合并的时间。当创建第一个合并请求后,数据将自动添加到此处。"],"The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.":["预发布阶段概述了从合并请求被合并到部署至生产环境的总时间。首次部署到生产环境后,数据将自动添加到此处。"],"The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.":["测试阶段概述了 GitLab CI 为相关合并请求运行每个流水线所需的时间。当第一个流水线运行完成后,数据将自动添加到此处。"],"The time taken by each data entry gathered by that stage.":["该阶段每条数据所花的时间"],"The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6.":["中位数是一个数列中最中间的值。例如在 3、5、9 之间,中位数是 5。在 3、5、7、8 之间,中位数是 (5 + 7)/ 2 = 6。"],"This means you can not push code until you create an empty repository or import existing one.":["在创建一个空的存储库或导入现有存储库之前,将无法推送代码。"],"Time before an issue gets scheduled":["议题被列入日程表的时间"],"Time before an issue starts implementation":["开始进行编码前的时间"],"Time between merge request creation and merge/close":["从创建合并请求到被合并或关闭的时间"],"Time until first merge request":["创建第一个合并请求之前的时间"],"Timeago|%s days ago":[" %s 天前"],"Timeago|%s days remaining":["剩余 %s 天"],"Timeago|%s hours remaining":["剩余 %s 小时"],"Timeago|%s minutes ago":[" %s 分钟前"],"Timeago|%s minutes remaining":["剩余 %s 分钟"],"Timeago|%s months ago":[" %s 个月前"],"Timeago|%s months remaining":["剩余 %s 月"],"Timeago|%s seconds remaining":["剩余 %s 秒"],"Timeago|%s weeks ago":[" %s 星期前"],"Timeago|%s weeks remaining":["剩余 %s 星期"],"Timeago|%s years ago":[" %s 年前"],"Timeago|%s years remaining":["剩余 %s 年"],"Timeago|1 day remaining":["剩余 1 天"],"Timeago|1 hour remaining":["剩余 1 小时"],"Timeago|1 minute remaining":["剩余 1 分钟"],"Timeago|1 month remaining":["剩余 1 个月"],"Timeago|1 week remaining":["剩余 1 星期"],"Timeago|1 year remaining":["剩余 1 年"],"Timeago|Past due":["逾期"],"Timeago|a day ago":[" 1 天前"],"Timeago|a month ago":[" 1 个月前"],"Timeago|a week ago":[" 1 星期前"],"Timeago|a while":["刚刚"],"Timeago|a year ago":[" 1 年前"],"Timeago|about %s hours ago":["约 %s 小时前"],"Timeago|about a minute ago":["约 1 分钟前"],"Timeago|about an hour ago":["约 1 小时前"],"Timeago|in %s days":[" %s 天后"],"Timeago|in %s hours":[" %s 小时后"],"Timeago|in %s minutes":[" %s 分钟后"],"Timeago|in %s months":[" %s 个月后"],"Timeago|in %s seconds":[" %s 秒后"],"Timeago|in %s weeks":[" %s 星期后"],"Timeago|in %s years":[" %s 年后"],"Timeago|in 1 day":[" 1 天后"],"Timeago|in 1 hour":[" 1 小时后"],"Timeago|in 1 minute":[" 1 分钟后"],"Timeago|in 1 month":[" 1 月后"],"Timeago|in 1 week":[" 1 星期后"],"Timeago|in 1 year":[" 1 年后"],"Timeago|less than a minute ago":["不到 1 分钟前"],"Time|hr":["小时"],"Time|min":["分钟"],"Time|s":["秒"],"Total Time":["总时间"],"Total test time for all commits/merges":["所有提交和合并的总测试时间"],"Unstar":["取消星标"],"Upload New File":["上传新文件"],"Upload file":["上传文件"],"Use your global notification setting":["使用全局通知设置"],"VisibilityLevel|Internal":["内部"],"VisibilityLevel|Private":["私有"],"VisibilityLevel|Public":["公开"],"Want to see the data? Please ask an administrator for access.":["权限不足。如需查看相关数据,请向管理员申请权限。"],"We don't have enough data to show this stage.":["该阶段的数据不足,无法显示。"],"Withdraw Access Request":["取消权限申请"],"You are going to remove %{project_name_with_namespace}.\\nRemoved project CANNOT be restored!\\nAre you ABSOLUTELY sure?":["即将要删除 %{project_name_with_namespace}。\\n已删除的项目无法恢复!\\n确定继续吗?"],"You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?":["即将删除与源项目 %{forked_from_project} 的派生关系。确定继续吗?"],"You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?":["即将 %{project_name_with_namespace} 转移给另一个所有者。确定继续吗?"],"You can only add files when you are on a branch":["只能在分支上添加文件"],"You have reached your project limit":["您已达到项目数量限制"],"You must sign in to star a project":["必须登录才能对项目加星标"],"You need permission.":["需要相关的权限。"],"You will not get any notifications via email":["不会收到任何通知邮件"],"You will only receive notifications for the events you choose":["只接收选择的事件通知"],"You will only receive notifications for threads you have participated in":["只接收参与的主题的通知"],"You will receive notifications for any activity":["接收所有活动的通知"],"You will receive notifications only for comments in which you were @mentioned":["只接收评论中提及(@)您的通知"],"You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account":["在账号中 %{set_password_link} 之前将无法通过 %{protocol} 拉取或推送代码。"],"You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile":["在账号中 %{add_ssh_key_link} 之前将无法通过 SSH 拉取或推送代码。"],"Your name":["您的名字"],"day":["天"],"new merge request":["新建合并请求"],"notification emails":["通知邮件"],"parent":["父级"]}}};
\ No newline at end of file
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index 894ed81b044c5a989222881a414579ee82ce61a7..7bb2236017e49bcbeb79f8175a6e8f8e5eade129 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -233,11 +233,18 @@ import BlobForkSuggestion from './blob/blob_fork_suggestion';
     }
 
     mountPipelinesView() {
-      this.commitPipelinesTable = new gl.CommitPipelinesTable().$mount();
+      const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view');
+      const CommitPipelinesTable = gl.CommitPipelinesTable;
+      this.commitPipelinesTable = new CommitPipelinesTable({
+        propsData: {
+          endpoint: pipelineTableViewEl.dataset.endpoint,
+          helpPagePath: pipelineTableViewEl.dataset.helpPagePath,
+        },
+      }).$mount();
+
       // $mount(el) replaces the el with the new rendered component. We need it in order to mount
       // it everytime this tab is clicked - https://vuejs.org/v2/api/#vm-mount
-      document.querySelector('#commit-pipeline-table-view')
-        .appendChild(this.commitPipelinesTable.$el);
+      pipelineTableViewEl.appendChild(this.commitPipelinesTable.$el);
     }
 
     loadDiff(source) {
diff --git a/app/assets/javascripts/milestone.js b/app/assets/javascripts/milestone.js
index 07ede5ee91358d716343b25f34f5892312ec5aef..3e07ec4d0aa01e7301c5b6611670efafda45d00c 100644
--- a/app/assets/javascripts/milestone.js
+++ b/app/assets/javascripts/milestone.js
@@ -4,87 +4,7 @@
 
 (function() {
   this.Milestone = (function() {
-    Milestone.updateIssue = function(li, issue_url, data) {
-      return $.ajax({
-        type: "PUT",
-        url: issue_url,
-        data: data,
-        success: function(_data) {
-          return Milestone.successCallback(_data, li);
-        },
-        error: function(data) {
-          return new Flash("Issue update failed", 'alert');
-        },
-        dataType: "json"
-      });
-    };
-
-    Milestone.sortIssues = function(url, data) {
-      return $.ajax({
-        type: "PUT",
-        url,
-        data: data,
-        success: function(_data) {
-          return Milestone.successCallback(_data);
-        },
-        error: function() {
-          return new Flash("Issues update failed", 'alert');
-        },
-        dataType: "json"
-      });
-    };
-
-    Milestone.sortMergeRequests = function(url, data) {
-      return $.ajax({
-        type: "PUT",
-        url,
-        data: data,
-        success: function(_data) {
-          return Milestone.successCallback(_data);
-        },
-        error: function(data) {
-          return new Flash("Issue update failed", 'alert');
-        },
-        dataType: "json"
-      });
-    };
-
-    Milestone.updateMergeRequest = function(li, merge_request_url, data) {
-      return $.ajax({
-        type: "PUT",
-        url: merge_request_url,
-        data: data,
-        success: function(_data) {
-          return Milestone.successCallback(_data, li);
-        },
-        error: function(data) {
-          return new Flash("Issue update failed", 'alert');
-        },
-        dataType: "json"
-      });
-    };
-
-    Milestone.successCallback = function(data, element) {
-      const $avatarContainer = $(element).find('.assignee-icon');
-      $avatarContainer.empty();
-
-      if (data.assignees && data.assignees.length > 0) {
-        const $avatars = data.assignees.map((assignee) => {
-          const img_tag = $('<img/>');
-          img_tag.attr('src', assignee.avatar_url);
-          img_tag.addClass('avatar s16');
-          return img_tag;
-        });
-
-        $avatarContainer.append($avatars);
-      }
-    };
-
     function Milestone() {
-      this.issuesSortEndpoint = $('#tab-issues').data('sort-endpoint');
-      this.mergeRequestsSortEndpoint = $('#tab-merge-requests').data('sort-endpoint');
-
-      this.bindIssuesSorting();
       this.bindTabsSwitching();
 
       // Load merge request tab if it is active
@@ -94,22 +14,6 @@
       this.loadInitialTab();
     }
 
-    Milestone.prototype.bindIssuesSorting = function() {
-      if (!this.issuesSortEndpoint) return;
-
-      $('#issues-list-unassigned, #issues-list-ongoing, #issues-list-closed').each(function (i, el) {
-        this.createSortable(el, {
-          group: 'issue-list',
-          listEls: $('.issues-sortable-list'),
-          fieldName: 'issue',
-          sortCallback: (data) => {
-            Milestone.sortIssues(this.issuesSortEndpoint, data);
-          },
-          updateCallback: Milestone.updateIssue,
-        });
-      }.bind(this));
-    };
-
     Milestone.prototype.bindTabsSwitching = function() {
       return $('a[data-toggle="tab"]').on('show.bs.tab', (e) => {
         const $target = $(e.target);
@@ -119,69 +23,6 @@
       });
     };
 
-    Milestone.prototype.bindMergeRequestSorting = function() {
-      if (!this.mergeRequestsSortEndpoint) return;
-
-      $("#merge_requests-list-unassigned, #merge_requests-list-ongoing, #merge_requests-list-closed").each(function (i, el) {
-        this.createSortable(el, {
-          group: 'merge-request-list',
-          listEls: $(".merge_requests-sortable-list:not(#merge_requests-list-merged)"),
-          fieldName: 'merge_request',
-          sortCallback: (data) => {
-            Milestone.sortMergeRequests(this.mergeRequestsSortEndpoint, data);
-          },
-          updateCallback: Milestone.updateMergeRequest,
-        });
-      }.bind(this));
-    };
-
-    Milestone.prototype.createSortable = function(el, opts) {
-      return Sortable.create(el, {
-        group: opts.group,
-        filter: '.is-disabled',
-        forceFallback: true,
-        onStart: function(e) {
-          opts.listEls.css('min-height', e.item.offsetHeight);
-        },
-        onEnd: function () {
-          opts.listEls.css("min-height", "0px");
-        },
-        onUpdate: function(e) {
-          var ids = this.toArray(),
-            data;
-
-          if (ids.length) {
-            data = ids.map(function(id) {
-              return 'sortable_' + opts.fieldName + '[]=' + id;
-            }).join('&');
-
-            opts.sortCallback(data);
-          }
-        },
-        onAdd: function (e) {
-          var data, issuableId, issuableUrl, newState;
-          newState = e.to.dataset.state;
-          issuableUrl = e.item.dataset.url;
-          data = (function() {
-            switch (newState) {
-              case 'ongoing':
-                return `${opts.fieldName}[assignee_ids][]=${gon.current_user_id}`;
-              case 'unassigned':
-                return `${opts.fieldName}[assignee_ids][]=0`;
-              case 'closed':
-                return opts.fieldName + '[state_event]=close';
-            }
-          })();
-          if (e.from.dataset.state === 'closed') {
-            data += '&' + opts.fieldName + '[state_event]=reopen';
-          }
-
-          opts.updateCallback(e.item, issuableUrl, data);
-          this.options.onUpdate.call(this, e);
-        }
-      });
-    };
-
     Milestone.prototype.loadInitialTab = function() {
       const $target = $(`.js-milestone-tabs a[href="${location.hash}"]`);
 
@@ -203,10 +44,6 @@
         .done((data) => {
           $(tabElId).html(data.html);
           $target.addClass('is-loaded');
-
-          if (tabElId === '#tab-merge-requests') {
-            this.bindMergeRequestSorting();
-          }
         });
       }
     };
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index d56cf959486b93c54191be31a9c4a6fb280b460e..624dd33678642a3b31892b131c4cf65fb36d6852 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -32,7 +32,7 @@ const normalizeNewlines = function(str) {
 (function() {
   this.Notes = (function() {
     const MAX_VISIBLE_COMMIT_LIST_COUNT = 3;
-    const REGEX_SLASH_COMMANDS = /^\/\w+.*$/gm;
+    const REGEX_QUICK_ACTIONS = /^\/\w+.*$/gm;
 
     Notes.interval = null;
 
@@ -187,7 +187,7 @@ const normalizeNewlines = function(str) {
           if ($textarea.val() !== '') {
             return;
           }
-          myLastNote = $(`li.note[data-author-id='${gon.current_user_id}'][data-editable]:last`, $textarea.closest('.note, #notes'));
+          myLastNote = $(`li.note[data-author-id='${gon.current_user_id}'][data-editable]:last`, $textarea.closest('.note, .notes_holder, #notes'));
           if (myLastNote.length) {
             myLastNoteEditBtn = myLastNote.find('.js-note-edit');
             return myLastNoteEditBtn.trigger('click', [true, myLastNote]);
@@ -284,7 +284,7 @@ const normalizeNewlines = function(str) {
       return this.initRefresh();
     };
 
-    Notes.prototype.handleSlashCommands = function(noteEntity) {
+    Notes.prototype.handleQuickActions = function(noteEntity) {
       var votesBlock;
       if (noteEntity.commands_changes) {
         if ('merge' in noteEntity.commands_changes) {
@@ -322,7 +322,9 @@ const normalizeNewlines = function(str) {
 
     Notes.updateNoteTargetSelector = function($note) {
       const hash = gl.utils.getLocationHash();
-      $note.toggleClass('target', hash && $note.filter(`#${hash}`).length > 0);
+      // Needs to be an explicit true/false for the jQuery `toggleClass(force)`
+      const addTargetClass = Boolean(hash && $note.filter(`#${hash}`).length > 0);
+      $note.toggleClass('target', addTargetClass);
     };
 
     /*
@@ -1220,27 +1222,27 @@ const normalizeNewlines = function(str) {
     };
 
     /**
-     * Identify if comment has any slash commands
+     * Identify if comment has any quick actions
      */
-    Notes.prototype.hasSlashCommands = function(formContent) {
-      return REGEX_SLASH_COMMANDS.test(formContent);
+    Notes.prototype.hasQuickActions = function(formContent) {
+      return REGEX_QUICK_ACTIONS.test(formContent);
     };
 
     /**
-     * Remove slash commands and leave comment with pure message
+     * Remove quick actions and leave comment with pure message
      */
-    Notes.prototype.stripSlashCommands = function(formContent) {
-      return formContent.replace(REGEX_SLASH_COMMANDS, '').trim();
+    Notes.prototype.stripQuickActions = function(formContent) {
+      return formContent.replace(REGEX_QUICK_ACTIONS, '').trim();
     };
 
     /**
-     * Gets appropriate description from slash commands found in provided `formContent`
+     * Gets appropriate description from quick actions found in provided `formContent`
      */
-    Notes.prototype.getSlashCommandDescription = function (formContent, availableSlashCommands = []) {
+    Notes.prototype.getQuickActionDescription = function (formContent, availableQuickActions = []) {
       let tempFormContent;
 
-      // Identify executed slash commands from `formContent`
-      const executedCommands = availableSlashCommands.filter((command, index) => {
+      // Identify executed quick actions from `formContent`
+      const executedCommands = availableQuickActions.filter((command, index) => {
         const commandRegex = new RegExp(`/${command.name}`);
         return commandRegex.test(formContent);
       });
@@ -1298,7 +1300,7 @@ const normalizeNewlines = function(str) {
     };
 
     /**
-     * Create Placeholder System Note DOM element populated with slash command description
+     * Create Placeholder System Note DOM element populated with quick action description
      */
     Notes.prototype.createPlaceholderSystemNote = function ({ formContent, uniqueId }) {
       const $tempNote = $(
@@ -1347,7 +1349,7 @@ const normalizeNewlines = function(str) {
       const { formData, formContent, formAction } = this.getFormData($form);
       let noteUniqueId;
       let systemNoteUniqueId;
-      let hasSlashCommands = false;
+      let hasQuickActions = false;
       let $notesContainer;
       let tempFormContent;
 
@@ -1366,9 +1368,9 @@ const normalizeNewlines = function(str) {
       }
 
       tempFormContent = formContent;
-      if (this.hasSlashCommands(formContent)) {
-        tempFormContent = this.stripSlashCommands(formContent);
-        hasSlashCommands = true;
+      if (this.hasQuickActions(formContent)) {
+        tempFormContent = this.stripQuickActions(formContent);
+        hasQuickActions = true;
       }
 
       // Show placeholder note
@@ -1385,10 +1387,10 @@ const normalizeNewlines = function(str) {
       }
 
       // Show placeholder system note
-      if (hasSlashCommands) {
+      if (hasQuickActions) {
         systemNoteUniqueId = _.uniqueId('tempSystemNote_');
         $notesContainer.append(this.createPlaceholderSystemNote({
-          formContent: this.getSlashCommandDescription(formContent, AjaxCache.get(gl.GfmAutoComplete.dataSources.commands)),
+          formContent: this.getQuickActionDescription(formContent, AjaxCache.get(gl.GfmAutoComplete.dataSources.commands)),
           uniqueId: systemNoteUniqueId,
         }));
       }
@@ -1410,7 +1412,7 @@ const normalizeNewlines = function(str) {
           $notesContainer.find(`#${noteUniqueId}`).remove();
 
           // Reset cached commands list when command is applied
-          if (hasSlashCommands) {
+          if (hasQuickActions) {
             $form.find('textarea.js-note-text').trigger('clear-commands-cache.atwho');
           }
 
@@ -1444,7 +1446,7 @@ const normalizeNewlines = function(str) {
           }
 
           if (note.commands_changes) {
-            this.handleSlashCommands(note);
+            this.handleQuickActions(note);
           }
 
           $form.trigger('ajax:success', [note]);
@@ -1452,7 +1454,7 @@ const normalizeNewlines = function(str) {
           // Submission failed, remove placeholder note and show Flash error message
           $notesContainer.find(`#${noteUniqueId}`).remove();
 
-          if (hasSlashCommands) {
+          if (hasQuickActions) {
             $notesContainer.find(`#${systemNoteUniqueId}`).remove();
           }
 
diff --git a/app/assets/javascripts/pipeline_schedules/components/interval_pattern_input.js b/app/assets/javascripts/pipeline_schedules/components/interval_pattern_input.js
index 4d623763ca7f8f247220ce694660f57be13139ea..901adbe9fce312f0d55ad147e4aa185a184bf935 100644
--- a/app/assets/javascripts/pipeline_schedules/components/interval_pattern_input.js
+++ b/app/assets/javascripts/pipeline_schedules/components/interval_pattern_input.js
@@ -1,4 +1,7 @@
 import Vue from 'vue';
+import Translate from '../../vue_shared/translate';
+
+Vue.use(Translate);
 
 const inputNameAttribute = 'schedule[cron]';
 
@@ -72,11 +75,11 @@ export default {
         />
 
         <label for="custom">
-          Custom
+          {{ s__('PipelineSheduleIntervalPattern|Custom') }}
         </label>
 
         <span class="cron-syntax-link-wrap">
-          (<a :href="cronSyntaxUrl" target="_blank">Cron syntax</a>)
+          (<a :href="cronSyntaxUrl" target="_blank">{{ __('Cron syntax') }}</a>)
         </span>
       </div>
 
@@ -92,7 +95,7 @@ export default {
         />
 
         <label class="label-light" for="every-day">
-          Every day (at 4:00am)
+          {{ __('Every day (at 4:00am)') }}
         </label>
       </div>
 
@@ -108,7 +111,7 @@ export default {
         />
 
         <label class="label-light" for="every-week">
-          Every week (Sundays at 4:00am)
+          {{ __('Every week (Sundays at 4:00am)') }}
         </label>
       </div>
 
@@ -124,7 +127,7 @@ export default {
         />
 
         <label class="label-light" for="every-month">
-          Every month (on the 1st at 4:00am)
+          {{ __('Every month (on the 1st at 4:00am)') }}
         </label>
       </div>
 
@@ -133,7 +136,7 @@ export default {
           id="schedule_cron"
           class="form-control inline cron-interval-input"
           type="text"
-          placeholder="Define a custom pattern with cron syntax"
+          :placeholder="__('Define a custom pattern with cron syntax')"
           required="true"
           v-model="cronInterval"
           :name="inputNameAttribute"
diff --git a/app/assets/javascripts/pipeline_schedules/components/pipeline_schedules_callout.js b/app/assets/javascripts/pipeline_schedules/components/pipeline_schedules_callout.js
index 5109b110b31fe8728f5bb6feac591207fbd2776a..c827b7402dcd694207e7580af3e8a958fd7c919a 100644
--- a/app/assets/javascripts/pipeline_schedules/components/pipeline_schedules_callout.js
+++ b/app/assets/javascripts/pipeline_schedules/components/pipeline_schedules_callout.js
@@ -1,6 +1,10 @@
+import Vue from 'vue';
 import Cookies from 'js-cookie';
+import Translate from '../../vue_shared/translate';
 import illustrationSvg from '../icons/intro_illustration.svg';
 
+Vue.use(Translate);
+
 const cookieKey = 'pipeline_schedules_callout_dismissed';
 
 export default {
@@ -29,20 +33,18 @@ export default {
         </button>
         <div class="svg-container" v-html="illustrationSvg"></div>
         <div class="user-callout-copy">
-          <h4>Scheduling Pipelines</h4>
+          <h4>{{ __('Scheduling Pipelines') }}</h4>
           <p>
-              The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags.
-              Those scheduled pipelines will inherit limited project access based on their associated user.
+              {{ __('The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user.') }}
           </p>
-          <p> Learn more in the
+          <p> {{ __('Learn more in the') }}
             <a
               :href="docsUrl"
               target="_blank"
-              rel="nofollow">pipeline schedules documentation</a>. <!-- oneline to prevent extra space before period -->
+              rel="nofollow">{{ s__('Learn more in the|pipeline schedules documentation') }}</a>. <!-- oneline to prevent extra space before period -->
           </p>
         </div>
       </div>
     </div>
   `,
 };
-
diff --git a/app/assets/javascripts/pipelines/components/async_button.vue b/app/assets/javascripts/pipelines/components/async_button.vue
index 37a6f02d8fda7811bd5a9cac7afa8a1cb6fd4ae5..abcd0c4ecea44328e6156a841a692abe218a7269 100644
--- a/app/assets/javascripts/pipelines/components/async_button.vue
+++ b/app/assets/javascripts/pipelines/components/async_button.vue
@@ -1,9 +1,9 @@
 <script>
 /* eslint-disable no-new, no-alert */
-/* global Flash */
-import '~/flash';
+
 import eventHub from '../event_hub';
 import loadingIcon from '../../vue_shared/components/loading_icon.vue';
+import tooltipMixin from '../../vue_shared/mixins/tooltip';
 
 export default {
   props: {
@@ -11,53 +11,42 @@ export default {
       type: String,
       required: true,
     },
-
-    service: {
-      type: Object,
-      required: true,
-    },
-
     title: {
       type: String,
       required: true,
     },
-
     icon: {
       type: String,
       required: true,
     },
-
     cssClass: {
       type: String,
       required: true,
     },
-
     confirmActionMessage: {
       type: String,
       required: false,
     },
   },
-
   components: {
     loadingIcon,
   },
-
+  mixins: [
+    tooltipMixin,
+  ],
   data() {
     return {
       isLoading: false,
     };
   },
-
   computed: {
     iconClass() {
       return `fa fa-${this.icon}`;
     },
-
     buttonClass() {
-      return `btn has-tooltip ${this.cssClass}`;
+      return `btn ${this.cssClass}`;
     },
   },
-
   methods: {
     onClick() {
       if (this.confirmActionMessage && confirm(this.confirmActionMessage)) {
@@ -66,21 +55,11 @@ export default {
         this.makeRequest();
       }
     },
-
     makeRequest() {
       this.isLoading = true;
 
-      $(this.$el).tooltip('destroy');
-
-      this.service.postAction(this.endpoint)
-        .then(() => {
-          this.isLoading = false;
-          eventHub.$emit('refreshPipelines');
-        })
-        .catch(() => {
-          this.isLoading = false;
-          new Flash('An error occured while making the request.');
-        });
+      $(this.$refs.tooltip).tooltip('destroy');
+      eventHub.$emit('postAction', this.endpoint);
     },
   },
 };
@@ -95,10 +74,12 @@ export default {
     :aria-label="title"
     data-container="body"
     data-placement="top"
+    ref="tooltip"
     :disabled="isLoading">
     <i
       :class="iconClass"
-      aria-hidden="true" />
+      aria-hidden="true">
+    </i>
     <loading-icon v-if="isLoading" />
   </button>
 </template>
diff --git a/app/assets/javascripts/pipelines/components/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines.vue
index fed42d23112622867ce9d4fffdb08db984ace252..01ae07aad65d4bbade118e0a9b5da620df4a7adf 100644
--- a/app/assets/javascripts/pipelines/components/pipelines.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines.vue
@@ -1,15 +1,9 @@
 <script>
-  import Visibility from 'visibilityjs';
   import PipelinesService from '../services/pipelines_service';
-  import eventHub from '../event_hub';
-  import pipelinesTableComponent from '../../vue_shared/components/pipelines_table.vue';
+  import pipelinesMixin from '../mixins/pipelines';
   import tablePagination from '../../vue_shared/components/table_pagination.vue';
-  import emptyState from './empty_state.vue';
-  import errorState from './error_state.vue';
   import navigationTabs from './navigation_tabs.vue';
   import navigationControls from './nav_controls.vue';
-  import loadingIcon from '../../vue_shared/components/loading_icon.vue';
-  import Poll from '../../lib/utils/poll';
 
   export default {
     props: {
@@ -20,13 +14,12 @@
     },
     components: {
       tablePagination,
-      pipelinesTableComponent,
-      emptyState,
-      errorState,
       navigationTabs,
       navigationControls,
-      loadingIcon,
     },
+    mixins: [
+      pipelinesMixin,
+    ],
     data() {
       const pipelinesData = document.querySelector('#pipelines-list-vue').dataset;
 
@@ -47,11 +40,6 @@
         state: this.store.state,
         apiScope: 'all',
         pagenum: 1,
-        isLoading: false,
-        hasError: false,
-        isMakingRequest: false,
-        updateGraphDropdown: false,
-        hasMadeRequest: false,
       };
     },
     computed: {
@@ -62,9 +50,6 @@
         const scope = gl.utils.getParameterByName('scope');
         return scope === null ? 'all' : scope;
       },
-      shouldRenderErrorState() {
-        return this.hasError && !this.isLoading;
-      },
 
       /**
       * The empty state should only be rendered when the request is made to fetch all pipelines
@@ -106,7 +91,6 @@
           this.state.pipelines.length &&
           this.state.pageInfo.total > this.state.pageInfo.perPage;
       },
-
       hasCiEnabled() {
         return this.hasCi !== undefined;
       },
@@ -129,37 +113,7 @@
     },
     created() {
       this.service = new PipelinesService(this.endpoint);
-
-      const poll = new Poll({
-        resource: this.service,
-        method: 'getPipelines',
-        data: { page: this.pageParameter, scope: this.scopeParameter },
-        successCallback: this.successCallback,
-        errorCallback: this.errorCallback,
-        notificationCallback: this.setIsMakingRequest,
-      });
-
-      if (!Visibility.hidden()) {
-        this.isLoading = true;
-        poll.makeRequest();
-      } else {
-        // If tab is not visible we need to make the first request so we don't show the empty
-        // state without knowing if there are any pipelines
-        this.fetchPipelines();
-      }
-
-      Visibility.change(() => {
-        if (!Visibility.hidden()) {
-          poll.restart();
-        } else {
-          poll.stop();
-        }
-      });
-
-      eventHub.$on('refreshPipelines', this.fetchPipelines);
-    },
-    beforeDestroy() {
-      eventHub.$off('refreshPipelines');
+      this.requestData = { page: this.pageParameter, scope: this.scopeParameter };
     },
     methods: {
       /**
@@ -174,15 +128,6 @@
         return param;
       },
 
-      fetchPipelines() {
-        if (!this.isMakingRequest) {
-          this.isLoading = true;
-
-          this.service.getPipelines({ scope: this.scopeParameter, page: this.pageParameter })
-            .then(response => this.successCallback(response))
-            .catch(() => this.errorCallback());
-        }
-      },
       successCallback(resp) {
         const response = {
           headers: resp.headers,
@@ -190,33 +135,14 @@
         };
 
         this.store.storeCount(response.body.count);
-        this.store.storePipelines(response.body.pipelines);
         this.store.storePagination(response.headers);
-
-        this.isLoading = false;
-        this.updateGraphDropdown = true;
-        this.hasMadeRequest = true;
-      },
-
-      errorCallback() {
-        this.hasError = true;
-        this.isLoading = false;
-        this.updateGraphDropdown = false;
-      },
-
-      setIsMakingRequest(isMakingRequest) {
-        this.isMakingRequest = isMakingRequest;
-
-        if (isMakingRequest) {
-          this.updateGraphDropdown = false;
-        }
+        this.setCommonData(response.body.pipelines);
       },
     },
   };
 </script>
 <template>
   <div :class="cssClass">
-
     <div
       class="top-area scrolling-tabs-container inner-page-scroll-tabs"
       v-if="!isLoading && !shouldRenderEmptyState">
@@ -274,7 +200,6 @@
 
         <pipelines-table-component
           :pipelines="state.pipelines"
-          :service="service"
           :update-graph-dropdown="updateGraphDropdown"
           />
       </div>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_actions.vue b/app/assets/javascripts/pipelines/components/pipelines_actions.vue
index 97b4de26214d57de05cb29643bcf0e84dbae28f1..a6fc4f042373ce58df43dd485aeee2c2902874d0 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_actions.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_actions.vue
@@ -11,10 +11,6 @@
         type: Array,
         required: true,
       },
-      service: {
-        type: Object,
-        required: true,
-      },
     },
     components: {
       loadingIcon,
@@ -31,17 +27,9 @@
 
         $(this.$refs.tooltip).tooltip('destroy');
 
-        this.service.postAction(endpoint)
-        .then(() => {
-          this.isLoading = false;
-          eventHub.$emit('refreshPipelines');
-        })
-        .catch(() => {
-          this.isLoading = false;
-          // eslint-disable-next-line no-new
-          new Flash('An error occured while making the request.');
-        });
+        eventHub.$emit('postAction', endpoint);
       },
+
       isActionDisabled(action) {
         if (action.playable === undefined) {
           return false;
diff --git a/app/assets/javascripts/vue_shared/components/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_table.vue
similarity index 93%
rename from app/assets/javascripts/vue_shared/components/pipelines_table.vue
rename to app/assets/javascripts/pipelines/components/pipelines_table.vue
index 884f1ce96890364a4dc3e3e8c92ec0abfd359a31..5088d92209fc7dc4565147c4dfa804f4a1f7918b 100644
--- a/app/assets/javascripts/vue_shared/components/pipelines_table.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_table.vue
@@ -12,10 +12,6 @@
         type: Array,
         required: true,
       },
-      service: {
-        type: Object,
-        required: true,
-      },
       updateGraphDropdown: {
         type: Boolean,
         required: false,
@@ -57,7 +53,6 @@
       v-for="model in pipelines"
       :key="model.id"
       :pipeline="model"
-      :service="service"
       :update-graph-dropdown="updateGraphDropdown"
     />
   </div>
diff --git a/app/assets/javascripts/vue_shared/components/pipelines_table_row.vue b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
similarity index 91%
rename from app/assets/javascripts/vue_shared/components/pipelines_table_row.vue
rename to app/assets/javascripts/pipelines/components/pipelines_table_row.vue
index 4d5ebe2e9ededa67c2dfc3300256972534b6b9a9..c3f1c426d8a2d2a67c64ea108f77a4b1179f05bb 100644
--- a/app/assets/javascripts/vue_shared/components/pipelines_table_row.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
@@ -1,13 +1,13 @@
 <script>
 /* eslint-disable no-param-reassign */
-import asyncButtonComponent from '../../pipelines/components/async_button.vue';
-import pipelinesActionsComponent from '../../pipelines/components/pipelines_actions.vue';
-import pipelinesArtifactsComponent from '../../pipelines/components/pipelines_artifacts.vue';
-import ciBadge from './ci_badge_link.vue';
-import pipelineStage from '../../pipelines/components/stage.vue';
-import pipelineUrl from '../../pipelines/components/pipeline_url.vue';
-import pipelinesTimeago from '../../pipelines/components/time_ago.vue';
-import commitComponent from './commit.vue';
+import asyncButtonComponent from './async_button.vue';
+import pipelinesActionsComponent from './pipelines_actions.vue';
+import pipelinesArtifactsComponent from './pipelines_artifacts.vue';
+import ciBadge from '../../vue_shared/components/ci_badge_link.vue';
+import pipelineStage from './stage.vue';
+import pipelineUrl from './pipeline_url.vue';
+import pipelinesTimeago from './time_ago.vue';
+import commitComponent from '../../vue_shared/components/commit.vue';
 
 /**
  * Pipeline table row.
@@ -20,10 +20,6 @@ export default {
       type: Object,
       required: true,
     },
-    service: {
-      type: Object,
-      required: true,
-    },
     updateGraphDropdown: {
       type: Boolean,
       required: false,
@@ -271,7 +267,6 @@ export default {
         <pipelines-actions-component
           v-if="pipeline.details.manual_actions.length"
           :actions="pipeline.details.manual_actions"
-          :service="service"
           />
 
         <pipelines-artifacts-component
@@ -282,7 +277,6 @@ export default {
 
         <async-button-component
           v-if="pipeline.flags.retryable"
-          :service="service"
           :endpoint="pipeline.retry_path"
           css-class="js-pipelines-retry-button btn-default btn-retry"
           title="Retry"
@@ -291,7 +285,6 @@ export default {
 
         <async-button-component
           v-if="pipeline.flags.cancelable"
-          :service="service"
           :endpoint="pipeline.cancel_path"
           css-class="js-pipelines-cancel-button btn-remove"
           title="Cancel"
diff --git a/app/assets/javascripts/pipelines/mixins/pipelines.js b/app/assets/javascripts/pipelines/mixins/pipelines.js
new file mode 100644
index 0000000000000000000000000000000000000000..9adc15e6266ac3f08f8165cdcd27a8a0dbf736cf
--- /dev/null
+++ b/app/assets/javascripts/pipelines/mixins/pipelines.js
@@ -0,0 +1,103 @@
+/* global Flash */
+import '~/flash';
+import Visibility from 'visibilityjs';
+import Poll from '../../lib/utils/poll';
+import emptyState from '../components/empty_state.vue';
+import errorState from '../components/error_state.vue';
+import loadingIcon from '../../vue_shared/components/loading_icon.vue';
+import pipelinesTableComponent from '../components/pipelines_table.vue';
+import eventHub from '../event_hub';
+
+export default {
+  components: {
+    pipelinesTableComponent,
+    errorState,
+    emptyState,
+    loadingIcon,
+  },
+  computed: {
+    shouldRenderErrorState() {
+      return this.hasError && !this.isLoading;
+    },
+  },
+  data() {
+    return {
+      isLoading: false,
+      hasError: false,
+      isMakingRequest: false,
+      updateGraphDropdown: false,
+      hasMadeRequest: false,
+    };
+  },
+  beforeMount() {
+    this.poll = new Poll({
+      resource: this.service,
+      method: 'getPipelines',
+      data: this.requestData ? this.requestData : undefined,
+      successCallback: this.successCallback,
+      errorCallback: this.errorCallback,
+      notificationCallback: this.setIsMakingRequest,
+    });
+
+    if (!Visibility.hidden()) {
+      this.isLoading = true;
+      this.poll.makeRequest();
+    } else {
+      // If tab is not visible we need to make the first request so we don't show the empty
+      // state without knowing if there are any pipelines
+      this.fetchPipelines();
+    }
+
+    Visibility.change(() => {
+      if (!Visibility.hidden()) {
+        this.poll.restart();
+      } else {
+        this.poll.stop();
+      }
+    });
+
+    eventHub.$on('refreshPipelines', this.fetchPipelines);
+    eventHub.$on('postAction', this.postAction);
+  },
+  beforeDestroy() {
+    eventHub.$off('refreshPipelines');
+    eventHub.$on('postAction', this.postAction);
+  },
+  destroyed() {
+    this.poll.stop();
+  },
+  methods: {
+    fetchPipelines() {
+      if (!this.isMakingRequest) {
+        this.isLoading = true;
+
+        this.service.getPipelines(this.requestData)
+          .then(response => this.successCallback(response))
+          .catch(() => this.errorCallback());
+      }
+    },
+    setCommonData(pipelines) {
+      this.store.storePipelines(pipelines);
+      this.isLoading = false;
+      this.updateGraphDropdown = true;
+      this.hasMadeRequest = true;
+    },
+    errorCallback() {
+      this.hasError = true;
+      this.isLoading = false;
+      this.updateGraphDropdown = false;
+    },
+    setIsMakingRequest(isMakingRequest) {
+      this.isMakingRequest = isMakingRequest;
+
+      if (isMakingRequest) {
+        this.updateGraphDropdown = false;
+      }
+    },
+    postAction(endpoint) {
+      this.service.postAction(endpoint)
+        .then(() => eventHub.$emit('refreshPipelines'))
+        .catch(() => new Flash('An error occured while making the request.'));
+    },
+  },
+};
diff --git a/app/assets/javascripts/preview_markdown.js b/app/assets/javascripts/preview_markdown.js
index 4a3df2fd465c8d73a3e368e5e0fcf22072eca760..141333b2b4d1af3514920569778d692b0a7a988f 100644
--- a/app/assets/javascripts/preview_markdown.js
+++ b/app/assets/javascripts/preview_markdown.js
@@ -3,7 +3,7 @@
 // MarkdownPreview
 //
 // Handles toggling the "Write" and "Preview" tab clicks, rendering the preview
-// (including the explanation of slash commands), and showing a warning when
+// (including the explanation of quick actions), and showing a warning when
 // more than `x` users are referenced.
 //
 (function () {
diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js
index b71c3097706e665b8dffc9d82c4fb2fb1f1e8c03..da7c0c5a36c07a6fff304ed42f7642c2c0996d97 100644
--- a/app/assets/javascripts/right_sidebar.js
+++ b/app/assets/javascripts/right_sidebar.js
@@ -7,6 +7,13 @@ import Cookies from 'js-cookie';
     function Sidebar(currentUser) {
       this.toggleTodo = this.toggleTodo.bind(this);
       this.sidebar = $('aside');
+
+      this.$sidebarInner = this.sidebar.find('.issuable-sidebar');
+      this.$navGitlab = $('.navbar-gitlab');
+      this.$layoutNav = $('.layout-nav');
+      this.$subScroll = $('.sub-nav-scroll');
+      this.$rightSidebar = $('.js-right-sidebar');
+
       this.removeListeners();
       this.addEventListeners();
     }
@@ -21,14 +28,15 @@ import Cookies from 'js-cookie';
 
     Sidebar.prototype.addEventListeners = function() {
       const $document = $(document);
-      const throttledSetSidebarHeight = _.throttle(this.setSidebarHeight, 10);
+      const throttledSetSidebarHeight = _.throttle(this.setSidebarHeight.bind(this), 20);
+      const debouncedSetSidebarHeight = _.debounce(this.setSidebarHeight.bind(this), 200);
 
       this.sidebar.on('click', '.sidebar-collapsed-icon', this, this.sidebarCollapseClicked);
       $('.dropdown').on('hidden.gl.dropdown', this, this.onSidebarDropdownHidden);
       $('.dropdown').on('loading.gl.dropdown', this.sidebarDropdownLoading);
       $('.dropdown').on('loaded.gl.dropdown', this.sidebarDropdownLoaded);
       $(window).on('resize', () => throttledSetSidebarHeight());
-      $document.on('scroll', () => throttledSetSidebarHeight());
+      $document.on('scroll', () => debouncedSetSidebarHeight());
       $document.on('click', '.js-sidebar-toggle', function(e, triggered) {
         var $allGutterToggleIcons, $this, $thisIcon;
         e.preventDefault();
@@ -207,13 +215,14 @@ import Cookies from 'js-cookie';
     };
 
     Sidebar.prototype.setSidebarHeight = function() {
-      const $navHeight = $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight() + $('.sub-nav-scroll').outerHeight();
-      const $rightSidebar = $('.js-right-sidebar');
+      const $navHeight = this.$navGitlab.outerHeight() + this.$layoutNav.outerHeight() + (this.$subScroll ? this.$subScroll.outerHeight() : 0);
       const diff = $navHeight - $(window).scrollTop();
       if (diff > 0) {
-        $rightSidebar.outerHeight($(window).height() - diff);
+        this.$rightSidebar.outerHeight($(window).height() - diff);
+        this.$sidebarInner.height('100%');
       } else {
-        $rightSidebar.outerHeight('100%');
+        this.$rightSidebar.outerHeight('100%');
+        this.$sidebarInner.height('');
       }
     };
 
diff --git a/app/assets/javascripts/settings_panels.js b/app/assets/javascripts/settings_panels.js
index e67f449e1a2a5501072f3215cce5acd74d3a5bdb..59ff2a86293c40038f5710c3cbc68afb97309577 100644
--- a/app/assets/javascripts/settings_panels.js
+++ b/app/assets/javascripts/settings_panels.js
@@ -1,11 +1,28 @@
+function expandSectionParent($section, $content) {
+  $section.addClass('expanded');
+  $content.off('animationend.expandSectionParent');
+}
+
 function expandSection($section) {
   $section.find('.js-settings-toggle').text('Close');
-  $section.find('.settings-content').addClass('expanded').off('scroll').scrollTop(0);
+
+  const $content = $section.find('.settings-content');
+  $content.addClass('expanded').off('scroll.expandSection').scrollTop(0);
+
+  if ($content.hasClass('no-animate')) {
+    expandSectionParent($section, $content);
+  } else {
+    $content.on('animationend.expandSectionParent', () => expandSectionParent($section, $content));
+  }
 }
 
 function closeSection($section) {
   $section.find('.js-settings-toggle').text('Expand');
-  $section.find('.settings-content').removeClass('expanded').on('scroll', () => expandSection($section));
+
+  const $content = $section.find('.settings-content');
+  $content.removeClass('expanded').on('scroll.expandSection', () => expandSection($section));
+
+  $section.removeClass('expanded');
 }
 
 function toggleSection($section) {
@@ -21,7 +38,7 @@ function toggleSection($section) {
 export default function initSettingsPanels() {
   $('.settings').each((i, elm) => {
     const $section = $(elm);
-    $section.on('click', '.js-settings-toggle', () => toggleSection($section));
-    $section.find('.settings-content:not(.expanded)').on('scroll', () => expandSection($section));
+    $section.on('click.toggleSection', '.js-settings-toggle', () => toggleSection($section));
+    $section.find('.settings-content:not(.expanded)').on('scroll.expandSection', () => expandSection($section));
   });
 }
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/help_state.js b/app/assets/javascripts/sidebar/components/time_tracking/help_state.js
index b2a77462fe0d45ad608dd77a18f4abe8b96db037..142ad4375092b6373dee591651097e761da016eb 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/help_state.js
+++ b/app/assets/javascripts/sidebar/components/time_tracking/help_state.js
@@ -15,10 +15,10 @@ export default {
     <div class="time-tracking-help-state">
       <div class="time-tracking-info">
         <h4>
-          Track time with slash commands
+          Track time with quick actions
         </h4>
         <p>
-          Slash commands can be used in the issues description and comment boxes.
+          Quick actions can be used in the issues description and comment boxes.
         </p>
         <p>
           <code>
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.js b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.js
index 244b67b3ad9266ce984d2f2a8421d0b2fe9a99d4..650e935b1165b3ae0e63bed9a8fc1c7be9770686 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.js
+++ b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.js
@@ -16,10 +16,10 @@ export default {
     'issuable-time-tracker': timeTracker,
   },
   methods: {
-    listenForSlashCommands() {
-      $(document).on('ajax:success', '.gfm-form', this.slashCommandListened);
+    listenForQuickActions() {
+      $(document).on('ajax:success', '.gfm-form', this.quickActionListened);
     },
-    slashCommandListened(e, data) {
+    quickActionListened(e, data) {
       const subscribedCommands = ['spend_time', 'time_estimate'];
       let changedCommands;
       if (data !== undefined) {
@@ -35,7 +35,7 @@ export default {
     },
   },
   mounted() {
-    this.listenForSlashCommands();
+    this.listenForQuickActions();
   },
   template: `
     <div class="block">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_memory_usage.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_memory_usage.js
index 8155218681c8e6366083f2d0003e0e3dc3c0d544..76cb71b6c12d9de538c56e81bb7266233a122d7d 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_memory_usage.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_memory_usage.js
@@ -1,5 +1,5 @@
-import statusCodes from '~/lib/utils/http_status';
-import { bytesToMiB } from '~/lib/utils/number_utils';
+import statusCodes from '../../lib/utils/http_status';
+import { bytesToMiB } from '../../lib/utils/number_utils';
 
 import MemoryGraph from '../../vue_shared/components/memory_graph';
 import MRWidgetService from '../services/mr_widget_service';
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index fefe5575d9bec1b77bb639006c8debfd67833875..95a08c960eaa9a47b0c97059f718ece481231c78 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -254,7 +254,7 @@
 }
 
 .landing {
-  margin-bottom: $gl-padding;
+  margin: $gl-padding auto;
   overflow: hidden;
   display: flex;
   position: relative;
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index b26d8fbd5fe3d172cd30668a15913052787b9e25..da03e4f5b5e4b6e835fa1f22b4d5673b969b447c 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -63,6 +63,7 @@
     background-color: $gray-light;
     text-align: right;
     padding: 8px $gl-padding;
+    border-bottom: 1px solid $border-color;
 
     @media (max-width: $screen-xs-max) {
       text-align: left;
diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss
index cfbaaaa04c74a0328650aa5e5f89d8c32bcc01bb..767cf5ffea58c2170809712589eb6b885f32dda0 100644
--- a/app/assets/stylesheets/framework/filters.scss
+++ b/app/assets/stylesheets/framework/filters.scss
@@ -152,7 +152,7 @@
   }
 
   .value-container {
-    background-color: $filter-value-selected-color;
+    box-shadow: inset 0 0 0 100px $filtered-search-term-shadow-color;
   }
 }
 
@@ -236,9 +236,6 @@
     width: 35px;
     background-color: $white-light;
     border: none;
-    position: static;
-    right: 0;
-    height: 100%;
     outline: none;
     z-index: 1;
 
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index a78179e727f2355bbc500ef1c6db8d5b1712d0db..61e3897f36966bd95668b55a708cf6895bd6437a 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -125,10 +125,11 @@ label {
 .select-wrapper {
   position: relative;
 
-  .fa-caret-down {
+  .fa-chevron-down {
     position: absolute;
+    font-size: 10px;
     right: 10px;
-    top: 10px;
+    top: 12px;
     color: $gray-darkest;
     pointer-events: none;
   }
@@ -138,6 +139,12 @@ label {
   padding-left: 10px;
   padding-right: 10px;
   -webkit-appearance: none;
+  -moz-appearance: none;
+  appearance: none;
+
+  &::-ms-expand {
+    display: none;
+  }
 }
 
 .form-control-inline {
diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss
index 80691a234f8cf1e10e5783eb94210ac4e39f846c..b21bcc22a8728e536b8188cf6f7a57de2d00e587 100644
--- a/app/assets/stylesheets/framework/markdown_area.scss
+++ b/app/assets/stylesheets/framework/markdown_area.scss
@@ -174,3 +174,14 @@
     white-space: nowrap;
   }
 }
+
+@media(max-width: $screen-xs-max) {
+  .atwho-view-ul {
+    width: 350px;
+  }
+
+  .atwho-view ul li {
+    overflow: hidden;
+    text-overflow: ellipsis;
+  }
+}
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index 3787ef370b2928820aaa005f981c3d7121270c32..28b2a7cfacd786b6c918e02ec308520ee3ff1620 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -45,8 +45,7 @@
   li {
     display: flex;
 
-    a,
-    .btn-link {
+    a {
       padding: $gl-btn-padding;
       padding-bottom: 11px;
       font-size: 14px;
@@ -68,29 +67,7 @@
       }
     }
 
-    .btn-link {
-      padding-top: 16px;
-      padding-left: 15px;
-      padding-right: 15px;
-      border-left: none;
-      border-right: none;
-      border-top: none;
-      border-radius: 0;
-
-      &:hover,
-      &:active,
-      &:focus {
-        background-color: transparent;
-      }
-
-      &:active {
-        outline: 0;
-        box-shadow: none;
-      }
-    }
-
-    &.active a,
-    &.active .btn-link {
+    &.active a {
       border-bottom: 2px solid $link-underline-blue;
       color: $black;
       font-weight: 600;
diff --git a/app/assets/stylesheets/framework/panels.scss b/app/assets/stylesheets/framework/panels.scss
index fa364e68d22541620b70f3e819d38a245438a1e1..e8d69e621940dcf41da584a6f85392b77355533d 100644
--- a/app/assets/stylesheets/framework/panels.scss
+++ b/app/assets/stylesheets/framework/panels.scss
@@ -1,54 +1,60 @@
 .panel {
   margin-bottom: $gl-padding;
+}
+
+.panel-slim {
+  @extend .panel;
+  margin-bottom: $gl-vert-padding;
+}
+
+
+.panel-heading {
+  padding: $gl-vert-padding $gl-padding;
+  line-height: 36px;
+
+  .controls {
+    margin-top: -2px;
+    float: right;
+  }
+
+  .dropdown-menu-toggle {
+    line-height: 20px;
+  }
 
-  .panel-heading {
-    padding: $gl-vert-padding $gl-padding;
-    line-height: 36px;
-
-    .controls {
-      margin-top: -2px;
-      float: right;
-    }
-
-    .dropdown-menu-toggle {
-      line-height: 20px;
-    }
-
-    .badge {
-      margin-top: -2px;
-      margin-left: 5px;
-    }
-
-    &.split {
-      display: flex;
-      align-items: center;
-    }
-
-    .left {
-      flex: 1 1 auto;
-    }
-
-    .right {
-      flex: 0 0 auto;
-      text-align: right;
-    }
+  .badge {
+    margin-top: -2px;
+    margin-left: 5px;
   }
 
-  .panel-empty-heading {
-    border-bottom: 0;
+  &.split {
+    display: flex;
+    align-items: center;
   }
 
-  .panel-body {
-    padding: $gl-padding;
+  .left {
+    flex: 1 1 auto;
+  }
 
-    .form-actions {
-      margin: -$gl-padding;
-      margin-top: $gl-padding;
-    }
+  .right {
+    flex: 0 0 auto;
+    text-align: right;
   }
+}
+
+.panel-empty-heading {
+  border-bottom: 0;
+}
+
+.panel-body {
+  padding: $gl-padding;
 
-  .panel-title {
-    font-size: inherit;
-    line-height: inherit;
+  .form-actions {
+    margin: -$gl-padding;
+    margin-top: $gl-padding;
   }
 }
+
+.panel-title {
+  font-size: inherit;
+  line-height: inherit;
+}
diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss
index 1b20c35ad98f97973eaad9dea2cad87c3bfd994a..40e654f4838e1635fb251c18c333a606ec34ecc6 100644
--- a/app/assets/stylesheets/framework/selects.scss
+++ b/app/assets/stylesheets/framework/selects.scss
@@ -18,19 +18,28 @@
       background-image: none;
       background-color: transparent;
       border: none;
-      padding-top: 6px;
-      padding-right: 10px;
+      padding-top: 12px;
+      padding-right: 20px;
+      font-size: 10px;
 
       b {
-        display: inline-block;
-        width: 0;
-        height: 0;
-        margin-left: 2px;
-        vertical-align: middle;
-        border-top: 5px dashed;
-        border-right: 5px solid transparent;
-        border-left: 5px solid transparent;
+        display: none;
+      }
+
+      &::after {
+        content: "\f078";
+        position: absolute;
+        z-index: 1;
+        text-align: center;
+        pointer-events: none;
+        box-sizing: border-box;
         color: $gray-darkest;
+        display: inline-block;
+        font: normal normal normal 14px/1 FontAwesome;
+        font-size: inherit;
+        text-rendering: auto;
+        -webkit-font-smoothing: antialiased;
+        -moz-osx-font-smoothing: grayscale;
       }
     }
 
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index d4421e3af7468851be63ef31a0601438a05858f1..5cf9330b8f81173fca1d12320a31d6dc9516f3c8 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -97,17 +97,19 @@
 
 .issues-bulk-update.right-sidebar {
   @include maintain-sidebar-dimensions;
-  transition: right $sidebar-transition-duration;
-  right: -$gutter-width;
+  width: 0;
+  padding: 0;
+  transition: width $sidebar-transition-duration;
 
   &.right-sidebar-expanded {
     @include maintain-sidebar-dimensions;
-    right: 0;
+    width: $gutter-width;
   }
 
   &.right-sidebar-collapsed {
     @include maintain-sidebar-dimensions;
-    right: -$gutter-width;
+    width: 0;
+    padding: 0;
 
     .block {
       padding: 16px 0;
@@ -118,5 +120,6 @@
 
   .issuable-sidebar {
     padding: 0 3px;
+    width: calc(100% + 35px);
   }
 }
diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss
index 108819870382d40e491db8a8368c5832ffa2f553..3d68a50f91f73b9e567b8b94362ec4664485482b 100644
--- a/app/assets/stylesheets/framework/timeline.scss
+++ b/app/assets/stylesheets/framework/timeline.scss
@@ -44,6 +44,10 @@
   &:target,
   &.target {
     background: $line-target-blue;
+
+    &.system-note .note-body .note-text.system-note-commit-list::after {
+      background: linear-gradient(rgba($line-target-blue, 0.1) -100px, $line-target-blue 100%);
+    }
   }
 
   .avatar {
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 49ba0108228a343ba1c6b80988257851f234b6b7..13c9c6c9fb3d01b4704b5797cce3aad0a262e9a7 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -282,6 +282,7 @@ $dropdown-toggle-active-border-color: darken($border-color, 14%);
 /*
 * Filtered Search
 */
+$filtered-search-term-shadow-color: rgba(0, 0, 0, 0.09);
 $dropdown-hover-color: $blue-400;
 
 /*
@@ -322,6 +323,7 @@ $note-disabled-comment-color: #b2b2b2;
 $note-targe3-outside: #fffff0;
 $note-targe3-inside: #ffffd3;
 $note-line2-border: #ddd;
+$note-icon-gutter-width: 55px;
 
 
 /*
diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss
index 7bec4bd5f566e9f00c26c847603fe4d044e6dfe5..3039732ca5bdde7cd9c044d6e1ed11641d804087 100644
--- a/app/assets/stylesheets/pages/cycle_analytics.scss
+++ b/app/assets/stylesheets/pages/cycle_analytics.scss
@@ -4,7 +4,7 @@
   position: relative;
 
   .landing {
-    margin-top: 10px;
+    margin-top: 0;
 
     .inner-content {
       white-space: normal;
diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss
index 72d73b89a2aae0152a16d137a0cbd5193e3aba9f..6f6c683997581c32c568743d28ea6995f066eee8 100644
--- a/app/assets/stylesheets/pages/groups.scss
+++ b/app/assets/stylesheets/pages/groups.scss
@@ -90,8 +90,6 @@
 }
 
 .explore-groups.landing {
-  margin-top: 10px;
-
   .inner-content {
     padding: 0;
 
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index b3f310ff67d964b91f62985507711bad1ea8b6b4..20f2eec9af544ef833e471276804483d1994ca11 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -204,7 +204,7 @@
 
   .issuable-sidebar {
     width: calc(100% + 100px);
-    height: 100%;
+    height: calc(100% - #{$header-height});
     overflow-y: scroll;
     overflow-x: hidden;
     -webkit-overflow-scrolling: touch;
@@ -729,33 +729,3 @@
     }
   }
 }
-
-.confidential-issue-warning {
-  background-color: $gl-gray;
-  border-radius: 3px;
-  padding: $gl-btn-padding $gl-padding;
-  margin-top: $gl-padding-top;
-  font-size: 14px;
-  color: $white-light;
-
-  .fa {
-    margin-right: 8px;
-  }
-
-  a {
-    color: $white-light;
-    text-decoration: underline;
-  }
-
-  &.affix {
-    position: static;
-    width: initial;
-
-    @media (min-width: $screen-sm-min) {
-      position: sticky;
-      position: -webkit-sticky;
-      top: 60px;
-      z-index: 200;
-    }
-  }
-}
diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss
index 335e587b8f44b5a86d65a6a478e762bb621c1490..55e0ee1936eda792c3e26a0cdfccc576ff501730 100644
--- a/app/assets/stylesheets/pages/milestone.scss
+++ b/app/assets/stylesheets/pages/milestone.scss
@@ -111,8 +111,8 @@
   }
 }
 
-.issues-sortable-list,
-.merge_requests-sortable-list {
+.milestone-issues-list,
+.milestone-merge_requests-list {
   .issuable-detail {
     display: block;
     margin-top: 7px;
@@ -197,6 +197,4 @@
 
 .issuable-row {
   background-color: $white-light;
-  cursor: -webkit-grab;
-  cursor: grab;
 }
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index aa3074147379a1b7d2514c26ce5d2a70c4a03c44..9877ed2cfd69321cd89ae0b92a2e74648a1ac080 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -103,6 +103,42 @@
   }
 }
 
+.confidential-issue-warning {
+  background-color: $gray-normal;
+  border-radius: 3px;
+  padding: 3px 12px;
+  margin: auto;
+  margin-top: 0;
+  text-align: center;
+  font-size: 12px;
+  align-items: center;
+
+  @media (max-width: $screen-md-max) {
+    // On smaller devices the warning becomes the fourth item in the list,
+    // rather than centering, and grows to span the full width of the
+    // comment area.
+    order: 4;
+    margin: 6px auto;
+    width: 100%;
+  }
+
+  .fa {
+    margin-right: 8px;
+  }
+}
+
+.right-sidebar-expanded {
+  .confidential-issue-warning {
+    // When the sidebar is open the warning becomes the fourth item in the list,
+    // rather than centering, and grows to span the full width of the
+    // comment area.
+    order: 4;
+    margin: 6px auto;
+    width: 100%;
+  }
+}
+
+
 .discussion-form {
   padding: $gl-padding-top $gl-padding $gl-padding;
   background-color: $white-light;
@@ -112,8 +148,20 @@
   padding: 6px 0;
 }
 
-.notes-form > li {
-  border: 0;
+.notes.notes-form > li.timeline-entry {
+  @include notes-media('max', $screen-sm-max) {
+    padding: 0;
+  }
+
+  .timeline-content {
+    @include notes-media('max', $screen-sm-max) {
+      margin: 0;
+    }
+  }
+
+  .timeline-entry-inner {
+    border: 0;
+  }
 }
 
 .note-edit-form {
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index a04424633901eaeacb50f9f6cef03d62466c566b..f0dbe4249c54a227206b3bb6dbbeea0b35a522cb 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -14,16 +14,6 @@ ul.notes {
   margin: 0;
   padding: 0;
 
-  .timeline-content {
-    margin-left: 55px;
-
-    &.timeline-content-form {
-      @include notes-media('max', $screen-sm-max) {
-        margin-left: 0;
-      }
-    }
-  }
-
   .note-created-ago,
   .note-updated-at {
     white-space: nowrap;
@@ -46,15 +36,47 @@ ul.notes {
     }
   }
 
-  > li {
-    padding: $gl-padding $gl-btn-padding;
+  > li { // .timeline-entry
+    padding: 0;
     display: block;
     position: relative;
-    border-bottom: 1px solid $white-normal;
+    border-bottom: 0;
+
+    @include notes-media('min', $screen-sm-min) {
+      padding-left: $note-icon-gutter-width;
+    }
+
+    .timeline-entry-inner {
+      padding: $gl-padding $gl-btn-padding;
+      border-bottom: 1px solid $white-normal;
+    }
 
-    &:last-child {
-      // Override `.timeline > li:last-child { border-bottom: none; }`
+    &:target,
+    &.target {
       border-bottom: 1px solid $white-normal;
+
+      &:not(:first-child) {
+        border-top: 1px solid $white-normal;
+        margin-top: -1px;
+      }
+
+      .timeline-entry-inner {
+        border-bottom: 0;
+      }
+    }
+
+    .timeline-icon {
+      @include notes-media('min', $screen-sm-min) {
+        margin-left: -$note-icon-gutter-width;
+      }
+    }
+
+    .timeline-content {
+      margin-left: $note-icon-gutter-width;
+
+      @include notes-media('min', $screen-sm-min) {
+        margin-left: 0;
+      }
     }
 
     &.being-posted {
@@ -73,7 +95,7 @@ ul.notes {
     }
 
     &.note-discussion {
-      &.timeline-entry {
+      .timeline-entry-inner {
         padding: $gl-padding 10px;
       }
     }
@@ -152,13 +174,8 @@ ul.notes {
 
   .system-note {
     font-size: 14px;
-    padding-left: 0;
     clear: both;
 
-    @include notes-media('min', $screen-sm-min) {
-      margin-left: 65px;
-    }
-
     .note-header-info {
       padding-bottom: 0;
     }
@@ -192,13 +209,16 @@ ul.notes {
     .timeline-icon {
       float: left;
 
+      @include notes-media('min', $screen-sm-min) {
+        margin-left: 0;
+        width: auto;
+      }
+
       svg {
         width: 16px;
         height: 16px;
         fill: $gray-darkest;
-        position: absolute;
-        left: 0;
-        top: 2px;
+        margin-top: 2px;
       }
     }
 
@@ -250,7 +270,7 @@ ul.notes {
           &::after {
             content: '';
             width: 100%;
-            height: 67px;
+            height: 70px;
             position: absolute;
             left: 0;
             bottom: 0;
@@ -509,11 +529,6 @@ ul.notes {
   display: inline;
   line-height: 20px;
 
-  @include notes-media('min', $screen-sm-min) {
-    margin-left: 10px;
-    line-height: 24px;
-  }
-
   .fa {
     color: $gray-darkest;
     position: relative;
@@ -644,15 +659,12 @@ ul.notes {
 .discussion-body,
 .diff-file {
   .notes .note {
-    padding-left: $gl-padding;
-    padding-right: $gl-padding;
-
-    &.system-note {
-      padding-left: 0;
+    border-bottom: 1px solid $white-normal;
 
-      @media (min-width: $screen-sm-min) {
-        margin-left: 70px;
-      }
+    .timeline-entry-inner {
+      padding-left: $gl-padding;
+      padding-right: $gl-padding;
+      border-bottom: none;
     }
   }
 }
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 062665bc6342f7810fa82cedd695862dbcda09b3..562ecbc69860ab6151eb6c606078e4777b8abad0 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -380,7 +380,7 @@ a.deploy-project-label {
   padding: 0;
   background: transparent;
   border: none;
-  line-height: 36px;
+  line-height: 34px;
   margin: 0;
 
   > li + li::before {
diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss
index 085706a500128b5f6cba5deb60d86fb588f6723a..d69a8e0995c815b2fc9b8445bc0ad2764447efc7 100644
--- a/app/assets/stylesheets/pages/settings.scss
+++ b/app/assets/stylesheets/pages/settings.scss
@@ -29,6 +29,10 @@
   &:first-of-type {
     margin-top: 10px;
   }
+
+  &.expanded {
+    overflow: visible;
+  }
 }
 
 .settings-header {
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index ab63225147f2cb664e80a3af4c8a3703a2e1c9f3..ce1a13c6afa1611ee0f63543b980020c579925e3 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -1,6 +1,72 @@
 .tree-holder {
-  > .nav-block {
-    margin: 11px 0;
+  .nav-block {
+    margin: 10px 0;
+
+    @media (min-width: $screen-sm-min) {
+      display: flex;
+
+      .tree-ref-container {
+        flex: 1;
+      }
+
+      .tree-controls {
+        text-align: right;
+
+        .btn-group {
+          margin-left: 10px;
+        }
+      }
+
+      .tree-ref-holder {
+        float: left;
+        margin-right: 15px;
+      }
+
+      .repo-breadcrumb {
+        li:last-of-type {
+          position: relative;
+        }
+      }
+
+      .add-to-tree-dropdown {
+        position: absolute;
+        left: 18px;
+      }
+    }
+  }
+
+  @media (max-width: $screen-xs-max) {
+    .repo-breadcrumb {
+      margin-top: 10px;
+      position: relative;
+
+      .dropdown-menu {
+        min-width: 100%;
+        width: 100%;
+        left: inherit;
+        right: 0;
+      }
+    }
+
+    .add-to-tree-dropdown {
+      position: absolute;
+      left: 0;
+      right: 0;
+    }
+
+    .tree-controls {
+      margin-bottom: 10px;
+
+      .btn,
+      .dropdown,
+      .btn-group {
+        width: 100%;
+      }
+
+      .btn {
+        margin: 10px 0 0;
+      }
+    }
   }
 
   .file-finder {
@@ -131,11 +197,6 @@
   }
 }
 
-.tree-ref-holder {
-  float: left;
-  margin-right: 15px;
-}
-
 .blob-commit-info {
   list-style: none;
   margin: 0;
@@ -159,16 +220,6 @@
   color: $md-link-color;
 }
 
-.tree-controls {
-  float: right;
-  position: relative;
-  z-index: 2;
-
-  .project-action-button {
-    margin-left: $btn-side-margin;
-  }
-}
-
 .repo-charts {
   .sub-header {
     margin: 20px 0;
diff --git a/app/controllers/concerns/creates_commit.rb b/app/controllers/concerns/creates_commit.rb
index 36ad307a93b04e25a64e8d223f6eea158dbb1846..1a9904bbe5711b77b8db5627269af2e2121d974d 100644
--- a/app/controllers/concerns/creates_commit.rb
+++ b/app/controllers/concerns/creates_commit.rb
@@ -97,8 +97,8 @@ module CreatesCommit
   def merge_request_exists?
     return @merge_request if defined?(@merge_request)
 
-    @merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.
-      find_by(source_project_id: @project_to_commit_into, source_branch: @branch_name, target_branch: @start_branch)
+    @merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened
+      .find_by(source_project_id: @project_to_commit_into, source_branch: @branch_name, target_branch: @start_branch)
   end
 
   def different_project?
diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb
index 8d07780f6c2f32e8565c3a92a0c87f3b6ed7f0f4..47d9ae350ae6fb17b774d9550efe951fa699e957 100644
--- a/app/controllers/concerns/membership_actions.rb
+++ b/app/controllers/concerns/membership_actions.rb
@@ -15,8 +15,8 @@ module MembershipActions
   end
 
   def destroy
-    Members::DestroyService.new(membershipable, current_user, params).
-      execute(:all)
+    Members::DestroyService.new(membershipable, current_user, params)
+      .execute(:all)
 
     respond_to do |format|
       format.html do
@@ -42,8 +42,8 @@ module MembershipActions
   end
 
   def leave
-    member = Members::DestroyService.new(membershipable, current_user, user_id: current_user.id).
-      execute(:all)
+    member = Members::DestroyService.new(membershipable, current_user, user_id: current_user.id)
+      .execute(:all)
 
     notice =
       if member.request?
diff --git a/app/controllers/concerns/milestone_actions.rb b/app/controllers/concerns/milestone_actions.rb
index b2536a1c9497033df0d4f6e9ffcf2ed0fb403a33..1ff785ac2ca259b3a4a081488abbb4e2ba9be265 100644
--- a/app/controllers/concerns/milestone_actions.rb
+++ b/app/controllers/concerns/milestone_actions.rb
@@ -6,7 +6,7 @@ module MilestoneActions
       format.html { redirect_to milestone_redirect_path }
       format.json do
         render json: tabs_json("shared/milestones/_merge_requests_tab", {
-          merge_requests: @milestone.merge_requests,
+          merge_requests: @milestone.sorted_merge_requests,
           show_project_name: true
         })
       end
diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb
index 641c502dbe4dcdd9e9025404da186fff46a1b83b..91c1e4dff79eafa96574c6712ea52ad0f75586c2 100644
--- a/app/controllers/dashboard/projects_controller.rb
+++ b/app/controllers/dashboard/projects_controller.rb
@@ -22,8 +22,8 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
   end
 
   def starred
-    @projects = load_projects(params.merge(starred: true)).
-      includes(:forked_from_project, :tags).page(params[:page])
+    @projects = load_projects(params.merge(starred: true))
+      .includes(:forked_from_project, :tags).page(params[:page])
 
     @groups = []
 
@@ -45,8 +45,8 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
   end
 
   def load_projects(finder_params)
-    ProjectsFinder.new(params: finder_params, current_user: current_user).
-      execute.includes(:route, namespace: :route)
+    ProjectsFinder.new(params: finder_params, current_user: current_user)
+      .execute.includes(:route, namespace: :route)
   end
 
   def load_events
diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb
index 8f1870759e4a05e749a9466c159b26a7a08f6b2c..741879dee35b679beb46daed69ec71c60939e914 100644
--- a/app/controllers/explore/projects_controller.rb
+++ b/app/controllers/explore/projects_controller.rb
@@ -49,7 +49,7 @@ class Explore::ProjectsController < Explore::ApplicationController
   private
 
   def load_projects
-    ProjectsFinder.new(current_user: current_user, params: params).
-      execute.includes(:route, namespace: :route)
+    ProjectsFinder.new(current_user: current_user, params: params)
+      .execute.includes(:route, namespace: :route)
   end
 end
diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb
index 11db164b3fa301a7a5052dcc8e9622fd0f10804f..4bceb1d67a3a52ba242abd51ba1f678ba19f3974 100644
--- a/app/controllers/jwt_controller.rb
+++ b/app/controllers/jwt_controller.rb
@@ -11,8 +11,8 @@ class JwtController < ApplicationController
     service = SERVICES[params[:service]]
     return head :not_found unless service
 
-    result = service.new(@authentication_result.project, @authentication_result.actor, auth_params).
-      execute(authentication_abilities: @authentication_result.authentication_abilities)
+    result = service.new(@authentication_result.project, @authentication_result.actor, auth_params)
+      .execute(authentication_abilities: @authentication_result.authentication_abilities)
 
     render json: result, status: result[:http_status]
   end
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index 2a8c8ca4bad6a6b2b3da0ef3c25d5fbf75897f47..b82681b197ebddf033f4b31ae98d3e26c029b312 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -144,7 +144,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
   end
 
   def log_audit_event(user, options = {})
-    AuditEventService.new(user, user, options).
-      for_authentication.security_event
+    AuditEventService.new(user, user, options)
+      .for_authentication.security_event
   end
 end
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index 72f34930ca8d8d8ffebbfdba18254d9bdd2c7258..f98a9e24de17cad3a82ea0c77b8f03f2240ad4b6 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -49,9 +49,9 @@ class ProfilesController < Profiles::ApplicationController
   end
 
   def audit_log
-    @events = AuditEvent.where(entity_type: "User", entity_id: current_user.id).
-      order("created_at DESC").
-      page(params[:page])
+    @events = AuditEvent.where(entity_type: "User", entity_id: current_user.id)
+      .order("created_at DESC")
+      .page(params[:page])
   end
 
   def update_username
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index 603a51266da1c37d6e503267fbd6c745b25f79c3..3d7ce4f0222a8af27165b58738e11fad4e513ff6 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -53,9 +53,21 @@ class Projects::ApplicationController < ApplicationController
     end
   end
 
+  def check_project_feature_available!(feature)
+    render_404 unless project.feature_available?(feature, current_user)
+  end
+
+  def check_issuables_available!
+    render_404 unless project.feature_available?(:issues, current_user) ||
+        project.feature_available?(:merge_requests, current_user)
+  end
+
   def method_missing(method_sym, *arguments, &block)
-    if method_sym.to_s =~ /\Aauthorize_(.*)!\z/
+    case method_sym.to_s
+    when /\Aauthorize_(.*)!\z/
       authorize_action!($1.to_sym)
+    when /\Acheck_(.*)_available!\z/
+      check_project_feature_available!($1.to_sym)
     else
       super
     end
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 66e6a9a451cef3d7224b15f99752ae860a8348de..a82d6fd5a4ae011e5aa29b7e7ea00e5419fe76ed 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -187,7 +187,7 @@ class Projects::BlobController < Projects::ApplicationController
   end
 
   def set_last_commit_sha
-    @last_commit_sha = Gitlab::Git::Commit.
-      last_for_path(@repository, @ref, @path).sha
+    @last_commit_sha = Gitlab::Git::Commit
+      .last_for_path(@repository, @ref, @path).sha
   end
 end
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index 70b06cfd9b4c5b3547b394b83fe9cacc7d20a2db..94a752c21eb7cc670bf37b708f2382f4cbdc01cf 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -37,8 +37,8 @@ class Projects::BranchesController < Projects::ApplicationController
 
     redirect_to_autodeploy = project.empty_repo? && project.deployment_services.present?
 
-    result = CreateBranchService.new(project, current_user).
-        execute(branch_name, ref)
+    result = CreateBranchService.new(project, current_user)
+        .execute(branch_name, ref)
 
     if params[:issue_iid]
       issue = IssuesFinder.new(current_user, project_id: @project.id).find_by(iid: params[:issue_iid])
diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb
index f33797ca310d2e81d936fea0585a3f2c55b61791..37b5a6e9d481b9735a7cf28e044cc1d868591e96 100644
--- a/app/controllers/projects/commits_controller.rb
+++ b/app/controllers/projects/commits_controller.rb
@@ -18,11 +18,11 @@ class Projects::CommitsController < Projects::ApplicationController
         @repository.commits(@ref, path: @path, limit: @limit, offset: @offset)
       end
 
-    @note_counts = project.notes.where(commit_id: @commits.map(&:id)).
-      group(:commit_id).count
+    @note_counts = project.notes.where(commit_id: @commits.map(&:id))
+      .group(:commit_id).count
 
-    @merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.
-      find_by(source_project: @project, source_branch: @ref, target_branch: @repository.root_ref)
+    @merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened
+      .find_by(source_project: @project, source_branch: @ref, target_branch: @repository.root_ref)
 
     respond_to do |format|
       format.html
diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb
index 88dd600e5fef9ca1758b2c5a4c03e5f2f79e8dc2..ef400c4d7455bb9098a1e74c0bbf961e10a3ffe4 100644
--- a/app/controllers/projects/compare_controller.rb
+++ b/app/controllers/projects/compare_controller.rb
@@ -61,7 +61,7 @@ class Projects::CompareController < Projects::ApplicationController
   end
 
   def merge_request
-    @merge_request ||= MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened.
-      find_by(source_project: @project, source_branch: @head_ref, target_branch: @start_ref)
+    @merge_request ||= MergeRequestsFinder.new(current_user, project_id: @project.id).execute.opened
+      .find_by(source_project: @project, source_branch: @head_ref, target_branch: @start_ref)
   end
 end
diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb
index 7f1469e107da38843b4a37755b09047c80ceb3d1..c2e621fa190bec3b4a4f37476751472ce9a7adcb 100644
--- a/app/controllers/projects/deploy_keys_controller.rb
+++ b/app/controllers/projects/deploy_keys_controller.rb
@@ -6,7 +6,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
   before_action :authorize_admin_project!
   before_action :authorize_update_deploy_key!, only: [:edit, :update]
 
-  layout "project_settings"
+  layout 'project_settings'
 
   def index
     respond_to do |format|
@@ -66,7 +66,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
   protected
 
   def deploy_key
-    @deploy_key ||= @project.deploy_keys.find(params[:id])
+    @deploy_key ||= DeployKey.find(params[:id])
   end
 
   def create_params
diff --git a/app/controllers/projects/discussions_controller.rb b/app/controllers/projects/discussions_controller.rb
index f4a18a5e8f7a7b95f95563cb5d0b8d8ee0211e05..2e6ab7903b8c29fe1041c1e80d1c46aac9f40a81 100644
--- a/app/controllers/projects/discussions_controller.rb
+++ b/app/controllers/projects/discussions_controller.rb
@@ -1,5 +1,5 @@
 class Projects::DiscussionsController < Projects::ApplicationController
-  before_action :module_enabled
+  before_action :check_merge_requests_available!
   before_action :merge_request
   before_action :discussion
   before_action :authorize_resolve_discussion!
@@ -34,8 +34,4 @@ class Projects::DiscussionsController < Projects::ApplicationController
   def authorize_resolve_discussion!
     access_denied! unless discussion.can_resolve?(current_user)
   end
-
-  def module_enabled
-    render_404 unless @project.feature_available?(:merge_requests, current_user)
-  end
 end
diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb
index 3869d4b2d561cf15c0484ebbdd3568c5411cb640..f88a1ffd1e93765b0a6431ebf3409556291b3c00 100644
--- a/app/controllers/projects/environments_controller.rb
+++ b/app/controllers/projects/environments_controller.rb
@@ -15,8 +15,6 @@ class Projects::EnvironmentsController < Projects::ApplicationController
     respond_to do |format|
       format.html
       format.json do
-        Gitlab::PollingInterval.set_header(response, interval: 3_000)
-
         render json: {
           environments: EnvironmentSerializer
             .new(project: @project, current_user: @current_user)
diff --git a/app/controllers/projects/git_http_client_controller.rb b/app/controllers/projects/git_http_client_controller.rb
index 928f17e6a8e6e040e09c8bc6b67e40e9d60236e5..7d0e2b3e2efadfc5e236221abd0ff7eb622e1ade 100644
--- a/app/controllers/projects/git_http_client_controller.rb
+++ b/app/controllers/projects/git_http_client_controller.rb
@@ -4,7 +4,7 @@ class Projects::GitHttpClientController < Projects::ApplicationController
   include ActionController::HttpAuthentication::Basic
   include KerberosSpnegoHelper
 
-  attr_reader :authentication_result
+  attr_reader :authentication_result, :redirected_path
 
   delegate :actor, :authentication_abilities, to: :authentication_result, allow_nil: true
 
@@ -14,7 +14,6 @@ class Projects::GitHttpClientController < Projects::ApplicationController
   skip_before_action :verify_authenticity_token
   skip_before_action :repository
   before_action :authenticate_user
-  before_action :ensure_project_found!
 
   private
 
@@ -68,38 +67,14 @@ class Projects::GitHttpClientController < Projects::ApplicationController
     headers['Www-Authenticate'] = challenges.join("\n") if challenges.any?
   end
 
-  def ensure_project_found!
-    render_not_found if project.blank?
-  end
-
   def project
-    return @project if defined?(@project)
-
-    project_id, _ = project_id_with_suffix
-    @project =
-      if project_id.blank?
-        nil
-      else
-        Project.find_by_full_path("#{params[:namespace_id]}/#{project_id}")
-      end
-  end
+    parse_repo_path unless defined?(@project)
 
-  # This method returns two values so that we can parse
-  # params[:project_id] (untrusted input!) in exactly one place.
-  def project_id_with_suffix
-    id = params[:project_id] || ''
-
-    %w[.wiki.git .git].each do |suffix|
-      if id.end_with?(suffix)
-        # Be careful to only remove the suffix from the end of 'id'.
-        # Accidentally removing it from the middle is how security
-        # vulnerabilities happen!
-        return [id.slice(0, id.length - suffix.length), suffix]
-      end
-    end
+    @project
+  end
 
-    # Something is wrong with params[:project_id]; do not pass it on.
-    [nil, nil]
+  def parse_repo_path
+    @project, @wiki, @redirected_path = Gitlab::RepoPath.parse("#{params[:namespace_id]}/#{params[:project_id]}")
   end
 
   def render_missing_personal_token
@@ -114,14 +89,9 @@ class Projects::GitHttpClientController < Projects::ApplicationController
   end
 
   def wiki?
-    return @wiki if defined?(@wiki)
-
-    _, suffix = project_id_with_suffix
-    @wiki = suffix == '.wiki.git'
-  end
+    parse_repo_path unless defined?(@wiki)
 
-  def render_not_found
-    render plain: 'Not Found', status: :not_found
+    @wiki
   end
 
   def handle_basic_authentication(login, password)
diff --git a/app/controllers/projects/git_http_controller.rb b/app/controllers/projects/git_http_controller.rb
index b6b62da7b60ab20321050e2482cbf15955a10ca8..71ae60cb8cdacbb7578560a09124f2e62997da73 100644
--- a/app/controllers/projects/git_http_controller.rb
+++ b/app/controllers/projects/git_http_controller.rb
@@ -56,7 +56,7 @@ class Projects::GitHttpController < Projects::GitHttpClientController
   end
 
   def access
-    @access ||= access_klass.new(access_actor, project, 'http', authentication_abilities: authentication_abilities)
+    @access ||= access_klass.new(access_actor, project, 'http', authentication_abilities: authentication_abilities, redirected_path: redirected_path)
   end
 
   def access_actor
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 56f76e752d0a3dd2f53e41617ccf42e6c510195b..dfc6baa34a46a5c1f640a8c0e8116bc33a4c76bb 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -9,7 +9,7 @@ class Projects::IssuesController < Projects::ApplicationController
   prepend_before_action :authenticate_user!, only: [:new]
 
   before_action :redirect_to_external_issue_tracker, only: [:index, :new]
-  before_action :module_enabled
+  before_action :check_issues_available!
   before_action :issue, except: [:index, :new, :create, :bulk_update]
 
   # Allow write(create) issue
@@ -250,7 +250,7 @@ class Projects::IssuesController < Projects::ApplicationController
     return render_404 unless can?(current_user, :push_code, @project) && @issue.can_be_worked_on?(current_user)
   end
 
-  def module_enabled
+  def check_issues_available!
     return render_404 unless @project.feature_available?(:issues, current_user) && @project.default_issues_tracker?
   end
 
diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb
index 1beac202efe2f91d2552c678dce4948a3fe65b39..daa973c9281dbcc88aec5c4be0e95daaa269dd7b 100644
--- a/app/controllers/projects/labels_controller.rb
+++ b/app/controllers/projects/labels_controller.rb
@@ -1,7 +1,7 @@
 class Projects::LabelsController < Projects::ApplicationController
   include ToggleSubscriptionAction
 
-  before_action :module_enabled
+  before_action :check_issuables_available!
   before_action :label, only: [:edit, :update, :destroy, :promote]
   before_action :find_labels, only: [:index, :set_priorities, :remove_priority, :toggle_subscription]
   before_action :authorize_read_label!
@@ -135,12 +135,6 @@ class Projects::LabelsController < Projects::ApplicationController
 
   protected
 
-  def module_enabled
-    unless @project.feature_available?(:issues, current_user) || @project.feature_available?(:merge_requests, current_user)
-      return render_404
-    end
-  end
-
   def label_params
     params.require(:label).permit(:title, :description, :color)
   end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 314906b5f09b54aa586819c85076817c80a4c192..164a8824277bc7d98470207f76f732d452292b62 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -7,7 +7,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
   include ToggleAwardEmoji
   include IssuableCollections
 
-  before_action :module_enabled
+  before_action :check_merge_requests_available!
   before_action :merge_request, only: [
     :edit, :update, :show, :diffs, :commits, :conflicts, :conflict_for_path, :pipelines, :merge,
     :pipeline_status, :ci_environments_status, :toggle_subscription, :cancel_merge_when_pipeline_succeeds, :remove_wip, :resolve_conflicts, :assign_related_issues, :commit_change_content
@@ -143,8 +143,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
         # Get commits from repository
         # or from cache if already merged
         @commits = @merge_request.commits
-        @note_counts = Note.where(commit_id: @commits.map(&:id)).
-          group(:commit_id).count
+        @note_counts = Note.where(commit_id: @commits.map(&:id))
+          .group(:commit_id).count
 
         render json: { html: view_to_html_string('projects/merge_requests/show/_commits') }
       end
@@ -192,9 +192,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
     end
 
     begin
-      MergeRequests::Conflicts::ResolveService.
-        new(merge_request).
-        execute(current_user, params)
+      MergeRequests::Conflicts::ResolveService
+        .new(merge_request)
+        .execute(current_user, params)
 
       flash[:notice] = 'All merge conflicts were resolved. The merge request can now be merged.'
 
@@ -461,10 +461,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
     return render_404 unless @conflicts_list.can_be_resolved_by?(current_user)
   end
 
-  def module_enabled
-    return render_404 unless @project.feature_available?(:merge_requests, current_user)
-  end
-
   def validates_merge_request
     # Show git not found page
     # if there is no saved commits between source & target branch
@@ -562,8 +558,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
     @commits = @merge_request.compare_commits.reverse
     @commit = @merge_request.diff_head_commit
 
-    @note_counts = Note.where(commit_id: @commits.map(&:id)).
-      group(:commit_id).count
+    @note_counts = Note.where(commit_id: @commits.map(&:id))
+      .group(:commit_id).count
 
     @labels = LabelsFinder.new(current_user, project_id: @project.id).execute
 
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index ae16f69955a30183b5660a488830ca25726975f6..953b1e83e492fa3766c4c5863694c799543d2dd7 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -1,8 +1,8 @@
 class Projects::MilestonesController < Projects::ApplicationController
   include MilestoneActions
 
-  before_action :module_enabled
-  before_action :milestone, only: [:edit, :update, :destroy, :show, :sort_issues, :sort_merge_requests, :merge_requests, :participants, :labels]
+  before_action :check_issuables_available!
+  before_action :milestone, only: [:edit, :update, :destroy, :show, :merge_requests, :participants, :labels]
 
   # Allow read any milestone
   before_action :authorize_read_milestone!
@@ -85,22 +85,6 @@ class Projects::MilestonesController < Projects::ApplicationController
     end
   end
 
-  def sort_issues
-    @milestone.sort_issues(params['sortable_issue'].map(&:to_i))
-
-    render json: { saved: true }
-  end
-
-  def sort_merge_requests
-    @merge_requests = @milestone.merge_requests.where(id: params['sortable_merge_request'])
-    @merge_requests.each do |merge_request|
-      merge_request.position = params['sortable_merge_request'].index(merge_request.id.to_s) + 1
-      merge_request.save
-    end
-
-    render json: { saved: true }
-  end
-
   protected
 
   def milestone
@@ -111,12 +95,6 @@ class Projects::MilestonesController < Projects::ApplicationController
     return render_404 unless can?(current_user, :admin_milestone, @project)
   end
 
-  def module_enabled
-    unless @project.feature_available?(:issues, current_user) || @project.feature_available?(:merge_requests, current_user)
-      return render_404
-    end
-  end
-
   def milestone_params
     params.require(:milestone).permit(:title, :description, :start_date, :due_date, :state_event)
   end
diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb
index 6f009d6195062cba1af675340f2354b6d9dd113e..24fe78bc1bd0d62bd062c35fed90a3e8b8d32a82 100644
--- a/app/controllers/projects/settings/ci_cd_controller.rb
+++ b/app/controllers/projects/settings/ci_cd_controller.rb
@@ -14,8 +14,8 @@ module Projects
 
       def define_runners_variables
         @project_runners = @project.runners.ordered
-        @assignable_runners = current_user.ci_authorized_runners.
-          assignable_for(project).ordered.page(params[:page]).per(20)
+        @assignable_runners = current_user.ci_authorized_runners
+          .assignable_for(project).ordered.page(params[:page]).per(20)
         @shared_runners = Ci::Runner.shared.active
         @shared_runners_count = @shared_runners.count(:all)
       end
diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb
index 8a8f8d6a27d2630c09208637f0af9363f0976ef6..98dd307bd9dc6908b23bb0bbca2dd685698ea027 100644
--- a/app/controllers/projects/snippets_controller.rb
+++ b/app/controllers/projects/snippets_controller.rb
@@ -5,7 +5,7 @@ class Projects::SnippetsController < Projects::ApplicationController
   include SnippetsActions
   include RendersBlob
 
-  before_action :module_enabled
+  before_action :check_snippets_available!
   before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :toggle_award_emoji, :mark_as_spam]
 
   # Allow read any snippet
@@ -102,10 +102,6 @@ class Projects::SnippetsController < Projects::ApplicationController
     return render_404 unless can?(current_user, :admin_project_snippet, @snippet)
   end
 
-  def module_enabled
-    return render_404 unless @project.feature_available?(:snippets, current_user)
-  end
-
   def snippet_params
     params.require(:project_snippet).permit(:title, :content, :file_name, :private, :visibility_level, :description)
   end
diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb
index afbea3e2b40e748f90dd83e7e7cdad67ea75dcec..ebc9f4edab4cf99bb4d080a587467fd010c03781 100644
--- a/app/controllers/projects/tags_controller.rb
+++ b/app/controllers/projects/tags_controller.rb
@@ -29,8 +29,8 @@ class Projects::TagsController < Projects::ApplicationController
   end
 
   def create
-    result = Tags::CreateService.new(@project, current_user).
-      execute(params[:tag_name], params[:ref], params[:message], params[:release_description])
+    result = Tags::CreateService.new(@project, current_user)
+      .execute(params[:tag_name], params[:ref], params[:message], params[:release_description])
 
     if result[:status] == :success
       @tag = result[:tag]
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index d7c702b94f8157d560fad7be8f3443935f3ce00a..0d8186dce0280982f0e90132dbd804a1ec66829d 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -128,8 +128,8 @@ class SessionsController < Devise::SessionsController
   end
 
   def log_audit_event(user, options = {})
-    AuditEventService.new(user, user, options).
-      for_authentication.security_event
+    AuditEventService.new(user, user, options)
+      .for_authentication.security_event
   end
 
   def log_user_activity(user)
diff --git a/app/controllers/sherlock/application_controller.rb b/app/controllers/sherlock/application_controller.rb
index 682ca5e382155f51ccc84432c29b9ecb13ea296a..6bdd3568a7899d2bd2497363807dc32565a095bc 100644
--- a/app/controllers/sherlock/application_controller.rb
+++ b/app/controllers/sherlock/application_controller.rb
@@ -4,8 +4,8 @@ module Sherlock
 
     def find_transaction
       if params[:transaction_id]
-        @transaction = Gitlab::Sherlock.collection.
-          find_transaction(params[:transaction_id])
+        @transaction = Gitlab::Sherlock.collection
+          .find_transaction(params[:transaction_id])
       end
     end
   end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index c211106fbaa07d86a98d0d3d14a7fac29168e346..8131eba6a2fd3e82e5f3ce84c06ac84868f65f81 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -106,11 +106,11 @@ class UsersController < ApplicationController
 
   def load_events
     # Get user activity feed for projects common for both users
-    @events = user.recent_events.
-      merge(projects_for_current_user).
-      references(:project).
-      with_associations.
-      limit_recent(20, params[:offset])
+    @events = user.recent_events
+      .merge(projects_for_current_user)
+      .references(:project)
+      .with_associations
+      .limit_recent(20, params[:offset])
   end
 
   def load_projects
diff --git a/app/finders/events_finder.rb b/app/finders/events_finder.rb
index b0450ddc1fdb1152ac9c08c88f7156cc40369efc..46ecbaba73af4c8c00f3ef1294eaec9c6b2e7ded 100644
--- a/app/finders/events_finder.rb
+++ b/app/finders/events_finder.rb
@@ -33,7 +33,8 @@ class EventsFinder
   private
 
   def by_current_user_access(events)
-    events.merge(ProjectsFinder.new(current_user: current_user).execute).references(:project)
+    events.merge(ProjectsFinder.new(current_user: current_user).execute)
+      .joins(:project)
   end
 
   def by_action(events)
diff --git a/app/finders/group_members_finder.rb b/app/finders/group_members_finder.rb
index fce3775f40e57fc51f1124bff256e0f3128738c8..067aff408dfa7d0805b8edfd288027e02e1acf26 100644
--- a/app/finders/group_members_finder.rb
+++ b/app/finders/group_members_finder.rb
@@ -8,9 +8,9 @@ class GroupMembersFinder
 
     return group_members unless @group.parent
 
-    parents_members = GroupMember.non_request.
-      where(source_id: @group.ancestors.select(:id)).
-      where.not(user_id: @group.users.select(:id))
+    parents_members = GroupMember.non_request
+      .where(source_id: @group.ancestors.select(:id))
+      .where.not(user_id: @group.users.select(:id))
 
     wheres = ["members.id IN (#{group_members.select(:id).to_sql})"]
     wheres << "members.id IN (#{parents_members.select(:id).to_sql})"
diff --git a/app/finders/group_projects_finder.rb b/app/finders/group_projects_finder.rb
index f043c38c6f9d878eec2e46faefe55cd149aea237..f2d3b90b8e260388e75bdae1bfdf1f2d2599b3cb 100644
--- a/app/finders/group_projects_finder.rb
+++ b/app/finders/group_projects_finder.rb
@@ -29,35 +29,69 @@ class GroupProjectsFinder < ProjectsFinder
   private
 
   def init_collection
-    only_owned  = options.fetch(:only_owned, false)
-    only_shared = options.fetch(:only_shared, false)
+    projects = if current_user
+                 collection_with_user
+               else
+                 collection_without_user
+               end
 
-    projects = []
+    union(projects)
+  end
 
-    if current_user
-      if group.users.include?(current_user)
-        projects << group.projects unless only_shared
-        projects << group.shared_projects unless only_owned
+  def collection_with_user
+    if group.users.include?(current_user)
+      if only_shared?
+        [shared_projects]
+      elsif only_owned?
+        [owned_projects]
       else
-        unless only_shared
-          projects << group.projects.visible_to_user(current_user)
-          projects << group.projects.public_to_user(current_user)
-        end
-
-        unless only_owned
-          projects << group.shared_projects.visible_to_user(current_user)
-          projects << group.shared_projects.public_to_user(current_user)
-        end
+        [shared_projects, owned_projects]
       end
     else
-      projects << group.projects.public_only unless only_shared
-      projects << group.shared_projects.public_only unless only_owned
+      if only_shared?
+        [shared_projects.public_or_visible_to_user(current_user)]
+      elsif only_owned?
+        [owned_projects.public_or_visible_to_user(current_user)]
+      else
+        [
+          owned_projects.public_or_visible_to_user(current_user),
+          shared_projects.public_or_visible_to_user(current_user)
+        ]
+      end
     end
+  end
 
-    projects
+  def collection_without_user
+    if only_shared?
+      [shared_projects.public_only]
+    elsif only_owned?
+      [owned_projects.public_only]
+    else
+      [shared_projects.public_only, owned_projects.public_only]
+    end
   end
 
   def union(items)
-    find_union(items, Project)
+    if items.one?
+      items.first
+    else
+      find_union(items, Project)
+    end
+  end
+
+  def only_owned?
+    options.fetch(:only_owned, false)
+  end
+
+  def only_shared?
+    options.fetch(:only_shared, false)
+  end
+
+  def owned_projects
+    group.projects
+  end
+
+  def shared_projects
+    group.shared_projects
   end
 end
diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb
index f68610e197cc6d6545520940bace1bc313bcae3a..e6fb112e7f282ce6fad43a7cfad901709efb2d2f 100644
--- a/app/finders/groups_finder.rb
+++ b/app/finders/groups_finder.rb
@@ -5,8 +5,10 @@ class GroupsFinder < UnionFinder
   end
 
   def execute
-    groups = find_union(all_groups, Group).with_route.order_id_desc
-    by_parent(groups)
+    items = all_groups.map do |item|
+      by_parent(item)
+    end
+    find_union(items, Group).with_route.order_id_desc
   end
 
   private
@@ -16,12 +18,22 @@ class GroupsFinder < UnionFinder
   def all_groups
     groups = []
 
-    groups << current_user.authorized_groups if current_user
+    if current_user
+      groups << Gitlab::GroupHierarchy.new(groups_for_ancestors, groups_for_descendants).all_groups
+    end
     groups << Group.unscoped.public_to_user(current_user)
 
     groups
   end
 
+  def groups_for_ancestors
+    current_user.authorized_groups
+  end
+
+  def groups_for_descendants
+    current_user.groups
+  end
+
   def by_parent(groups)
     return groups unless params[:parent]
 
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 957ad87585837e4e8268e50323c4b91ab8be843b..558f8b5e2e5dc46bd61b9f7b237372c2e96676fd 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -41,6 +41,7 @@ class IssuableFinder
     items = by_iids(items)
     items = by_milestone(items)
     items = by_label(items)
+    items = by_created_at(items)
 
     # Filtering by project HAS TO be the last because we use the project IDs yielded by the issuable query thus far
     items = by_project(items)
@@ -402,6 +403,18 @@ class IssuableFinder
     params[:non_archived].present? ? items.non_archived : items
   end
 
+  def by_created_at(items)
+    if params[:created_after].present?
+      items = items.where(items.klass.arel_table[:created_at].gteq(params[:created_after]))
+    end
+
+    if params[:created_before].present?
+      items = items.where(items.klass.arel_table[:created_at].lteq(params[:created_before]))
+    end
+
+    items
+  end
+
   def current_user_related?
     params[:scope] == 'created-by-me' || params[:scope] == 'authored' || params[:scope] == 'assigned-to-me'
   end
diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb
index 5bf722d1ec6f30c0107224c6dc21514b0e6047ae..8bfbe37c543247a4d6d4dab8dee24f3e77a97871 100644
--- a/app/finders/projects_finder.rb
+++ b/app/finders/projects_finder.rb
@@ -28,34 +28,56 @@ class ProjectsFinder < UnionFinder
   end
 
   def execute
-    items = init_collection
-    items = items.map do |item|
-      item = by_ids(item)
-      item = by_personal(item)
-      item = by_starred(item)
-      item = by_trending(item)
-      item = by_visibilty_level(item)
-      item = by_tags(item)
-      item = by_search(item)
-      by_archived(item)
-    end
-    items = union(items)
-    sort(items)
+    collection = init_collection
+    collection = by_ids(collection)
+    collection = by_personal(collection)
+    collection = by_starred(collection)
+    collection = by_trending(collection)
+    collection = by_visibilty_level(collection)
+    collection = by_tags(collection)
+    collection = by_search(collection)
+    collection = by_archived(collection)
+
+    sort(collection)
   end
 
   private
 
   def init_collection
-    projects = []
+    if current_user
+      collection_with_user
+    else
+      collection_without_user
+    end
+  end
 
-    if params[:owned].present?
-      projects << current_user.owned_projects if current_user
+  def collection_with_user
+    if owned_projects?
+      current_user.owned_projects
     else
-      projects << current_user.authorized_projects if current_user
-      projects << Project.unscoped.public_to_user(current_user) unless params[:non_public].present?
+      if private_only?
+        current_user.authorized_projects
+      else
+        Project.public_or_visible_to_user(current_user)
+      end
     end
+  end
+
+  # Builds a collection for an anonymous user.
+  def collection_without_user
+    if private_only? || owned_projects?
+      Project.none
+    else
+      Project.public_to_user
+    end
+  end
+
+  def owned_projects?
+    params[:owned].present?
+  end
 
-    projects
+  def private_only?
+    params[:non_public].present?
   end
 
   def by_ids(items)
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 2bfc7586adc93e5dca7e66b68da29f8993721b1d..a3b243fccb73e8d9ae61da02fca8d9b345cb3381 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -68,7 +68,7 @@ module ApplicationHelper
     end
   end
 
-  def avatar_icon(user_or_email = nil, size = nil, scale = 2)
+  def avatar_icon(user_or_email = nil, size = nil, scale = 2, only_path: true)
     user =
       if user_or_email.is_a?(User)
         user_or_email
@@ -77,7 +77,7 @@ module ApplicationHelper
       end
 
     if user
-      user.avatar_url(size: size) || default_avatar
+      user.avatar_url(size: size, only_path: only_path) || default_avatar
     else
       gravatar_icon(user_or_email, size, scale)
     end
@@ -167,9 +167,9 @@ module ApplicationHelper
     css_classes = short_format ? 'js-short-timeago' : 'js-timeago'
     css_classes << " #{html_class}" unless html_class.blank?
 
-    element = content_tag :time, time.strftime("%b %d, %Y"),
+    element = content_tag :time, l(time, format: "%b %d, %Y"),
       class: css_classes,
-      title: time.to_time.in_time_zone.to_s(:medium),
+      title: l(time.to_time.in_time_zone, format: :timeago_tooltip),
       datetime: time.to_time.getutc.iso8601,
       data: {
         toggle: 'tooltip',
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index 06822747d11fcbfe78042bca7a1286fe18b72003..16a99addd0b7822b79b9b96868e0632a15ff9f84 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -66,12 +66,12 @@ module DiffHelper
 
     discussions_left = discussions_right = nil
 
-    if left && (left.unchanged? || left.discussable?)
+    if left && left.discussable? && (left.unchanged? || left.removed?)
       line_code = diff_file.line_code(left)
       discussions_left = @grouped_diff_discussions[line_code]
     end
 
-    if right&.discussable?
+    if right && right.discussable? && right.added?
       line_code = diff_file.line_code(right)
       discussions_right = @grouped_diff_discussions[line_code]
     end
diff --git a/app/helpers/form_helper.rb b/app/helpers/form_helper.rb
index 014fc46b1304bc34643fc30af7a1c8f18e2747c7..8ceb5c36bda69b673f1ec8f90ebce2b604a833b9 100644
--- a/app/helpers/form_helper.rb
+++ b/app/helpers/form_helper.rb
@@ -8,10 +8,10 @@ module FormHelper
     content_tag(:div, class: 'alert alert-danger', id: 'error_explanation') do
       content_tag(:h4, headline) <<
         content_tag(:ul) do
-          model.errors.full_messages.
-            map { |msg| content_tag(:li, msg) }.
-            join.
-            html_safe
+          model.errors.full_messages
+            .map { |msg| content_tag(:li, msg) }
+            .join
+            .html_safe
         end
     end
   end
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 5e8f0849969fa3597b26a4f86f7575d849681aee..3259a9c1933f2a3514a47c2fecbe26b592648f61 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -138,8 +138,8 @@ module IssuablesHelper
     end
 
     output << "&ensp;".html_safe
-    output << content_tag(:span, issuable.task_status, id: "task_status", class: "hidden-xs hidden-sm")
-    output << content_tag(:span, issuable.task_status_short, id: "task_status_short", class: "hidden-md hidden-lg")
+    output << content_tag(:span, (issuable.task_status if issuable.tasks?), id: "task_status", class: "hidden-xs hidden-sm")
+    output << content_tag(:span, (issuable.task_status_short if issuable.tasks?), id: "task_status_short", class: "hidden-md hidden-lg")
 
     output
   end
@@ -216,7 +216,8 @@ module IssuablesHelper
       initialTitleHtml: markdown_field(issuable, :title),
       initialTitleText: issuable.title,
       initialDescriptionHtml: markdown_field(issuable, :description),
-      initialDescriptionText: issuable.description
+      initialDescriptionText: issuable.description,
+      initialTaskStatus: issuable.task_status
     }
 
     data.merge!(updated_at_by(issuable))
diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb
index c59d8dafc83308108ba488f8496b37548b47d692..64ad7b280cb3f0fb5431b05853759096840f52a6 100644
--- a/app/helpers/notes_helper.rb
+++ b/app/helpers/notes_helper.rb
@@ -10,8 +10,8 @@ module NotesHelper
     Ability.can_edit_note?(current_user, note)
   end
 
-  def note_supports_slash_commands?(note)
-    Notes::SlashCommandsService.supported?(note, current_user)
+  def note_supports_quick_actions?(note)
+    Notes::QuickActionsService.supported?(note, current_user)
   end
 
   def noteable_json(noteable)
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index c11dd49f4a7fb79065d9fbd7670ac72d4be23abd..d10e0bd45b0e258621e6338f4bb09b801f86c01d 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -80,7 +80,7 @@ module ProjectsHelper
   end
 
   def remove_fork_project_message(project)
-    _("You are going to remove the fork relationship to source project %{forked_from_project}.  Are you ABSOLUTELY sure?") %
+    _("You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?") %
       { forked_from_project: @project.forked_from_project.name_with_namespace }
   end
 
@@ -151,14 +151,21 @@ module ProjectsHelper
       disabled: disabled_option
     )
 
-    content_tag(
-      :select,
-      options,
-      name: "project[project_feature_attributes][#{field}]",
-      id: "project_project_feature_attributes_#{field}",
-      class: "pull-right form-control #{repo_children_classes(field)}",
-      data: { field: field }
-    ).html_safe
+    content_tag :div, class: "select-wrapper" do
+      concat(
+        content_tag(
+          :select,
+          options,
+          name: "project[project_feature_attributes][#{field}]",
+          id: "project_project_feature_attributes_#{field}",
+          class: "pull-right form-control select-control #{repo_children_classes(field)} ",
+          data: { field: field }
+        )
+      )
+      concat(
+        icon('chevron-down')
+      )
+    end.html_safe
   end
 
   def link_to_autodeploy_doc
@@ -187,8 +194,8 @@ module ProjectsHelper
   end
 
   def load_pipeline_status(projects)
-    Gitlab::Cache::Ci::ProjectPipelineStatus.
-      load_in_batch_for_projects(projects)
+    Gitlab::Cache::Ci::ProjectPipelineStatus
+      .load_in_batch_for_projects(projects)
   end
 
   private
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index 9c46035057f37ddfaf832f5e130461f6f1968611..8f15904f0681672adde8f8a430d1f4faca414d66 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -97,8 +97,8 @@ module SearchHelper
 
   # Autocomplete results for the current user's projects
   def projects_autocomplete(term, limit = 5)
-    current_user.authorized_projects.search_by_title(term).
-      sorted_by_stars.non_archived.limit(limit).map do |p|
+    current_user.authorized_projects.search_by_title(term)
+      .sorted_by_stars.non_archived.limit(limit).map do |p|
       {
         category: "Projects",
         id: p.id,
diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb
index 3e3f6246fc5ef033f939ffd18afc6463bfbbbfea..99212a3438f7f5415a4239bfc2924c763e187fe8 100644
--- a/app/helpers/wiki_helper.rb
+++ b/app/helpers/wiki_helper.rb
@@ -6,8 +6,8 @@ module WikiHelper
   # Returns a String composed of the capitalized name of each directory and the
   # capitalized name of the page itself.
   def breadcrumb(page_slug)
-    page_slug.split('/').
-      map { |dir_or_page| WikiPage.unhyphenize(dir_or_page).capitalize }.
-      join(' / ')
+    page_slug.split('/')
+      .map { |dir_or_page| WikiPage.unhyphenize(dir_or_page).capitalize }
+      .join(' / ')
   end
 end
diff --git a/app/models/award_emoji.rb b/app/models/award_emoji.rb
index ebe6044160344bc28309ba59d9df32b6bda45d1d..91b62dabbcd94a942fc17a50f48fbdb1b91ea1e5 100644
--- a/app/models/award_emoji.rb
+++ b/app/models/award_emoji.rb
@@ -19,9 +19,9 @@ class AwardEmoji < ActiveRecord::Base
 
   class << self
     def votes_for_collection(ids, type)
-      select('name', 'awardable_id', 'COUNT(*) as count').
-        where('name IN (?) AND awardable_type = ? AND awardable_id IN (?)', [DOWNVOTE_NAME, UPVOTE_NAME], type, ids).
-        group('name', 'awardable_id')
+      select('name', 'awardable_id', 'COUNT(*) as count')
+        .where('name IN (?) AND awardable_type = ? AND awardable_id IN (?)', [DOWNVOTE_NAME, UPVOTE_NAME], type, ids)
+        .group('name', 'awardable_id')
     end
   end
 
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 58758f7ca8a940dae1b386bf55f120d67e5d7186..a300536532b4f2068087e3b38b27759c10cc9991 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -138,17 +138,6 @@ module Ci
       ExpandVariables.expand(environment, simple_variables) if environment
     end
 
-    def environment_url
-      return @environment_url if defined?(@environment_url)
-
-      @environment_url =
-        if unexpanded_url = options&.dig(:environment, :url)
-          ExpandVariables.expand(unexpanded_url, simple_variables)
-        else
-          persisted_environment&.external_url
-        end
-    end
-
     def has_environment?
       environment.present?
     end
@@ -192,7 +181,7 @@ module Ci
       slugified.gsub(/[^a-z0-9]/, '-')[0..62]
     end
 
-    # Variables whose value does not depend on other variables
+    # Variables whose value does not depend on environment
     def simple_variables
       variables = predefined_variables
       variables += project.predefined_variables
@@ -207,7 +196,8 @@ module Ci
       variables
     end
 
-    # All variables, including those dependent on other variables
+    # All variables, including those dependent on environment, which could
+    # contain unexpanded variables.
     def variables
       simple_variables.concat(persisted_environment_variables)
     end
@@ -481,9 +471,10 @@ module Ci
 
       variables = persisted_environment.predefined_variables
 
-      if url = environment_url
-        variables << { key: 'CI_ENVIRONMENT_URL', value: url, public: true }
-      end
+      # Here we're passing unexpanded environment_url for runner to expand,
+      # and we need to make sure that CI_ENVIRONMENT_NAME and
+      # CI_ENVIRONMENT_SLUG so on are available for the URL be expanded.
+      variables << { key: 'CI_ENVIRONMENT_URL', value: environment_url, public: true } if environment_url
 
       variables
     end
@@ -506,6 +497,10 @@ module Ci
       variables
     end
 
+    def environment_url
+      options&.dig(:environment, :url) || persisted_environment&.external_url
+    end
+
     def build_attributes_from_config
       return {} unless pipeline.config_processor
 
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 9ddecba518390fe5a36072dc93612d473d523a4c..1b3e5a25ac2bbdd2b81f3bfe7415a48a1019c1d9 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -168,8 +168,8 @@ module Ci
     end
 
     def stages_names
-      statuses.order(:stage_idx).distinct.
-        pluck(:stage, :stage_idx).map(&:first)
+      statuses.order(:stage_idx).distinct
+        .pluck(:stage, :stage_idx).map(&:first)
     end
 
     def legacy_stage(name)
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index 487ba61bc9c7019618cf8a25f5c60961c8b3761f..d12f96f3d0bbf85f9d34635fb29f6916d7d912fb 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -30,8 +30,8 @@ module Ci
     scope :assignable_for, ->(project) do
       # FIXME: That `to_sql` is needed to workaround a weird Rails bug.
       #        Without that, placeholders would miss one and couldn't match.
-      where(locked: false).
-        where.not("id IN (#{project.runners.select(:id).to_sql})").specific
+      where(locked: false)
+        .where.not("id IN (#{project.runners.select(:id).to_sql})").specific
     end
 
     validate :tag_constraints
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index ea10d004c9c9ede49d1a71c71ccfb8f5bb0dedc6..d178ee4422b0f8e0f9b741b42d39cde8f713314f 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -67,7 +67,6 @@ module Issuable
 
     scope :authored, ->(user) { where(author_id: user) }
     scope :recent, -> { reorder(id: :desc) }
-    scope :order_position_asc, -> { reorder(position: :asc) }
     scope :of_projects, ->(ids) { where(project_id: ids) }
     scope :of_milestones, ->(ids) { where(milestone_id: ids) }
     scope :with_milestone, ->(title) { left_joins_milestones.where(milestones: { title: title }) }
@@ -139,7 +138,6 @@ module Issuable
                when 'upvotes_desc' then order_upvotes_desc
                when 'label_priority' then order_labels_priority(excluded_labels: excluded_labels)
                when 'priority' then order_due_date_and_labels_priority(excluded_labels: excluded_labels)
-               when 'position_asc' then  order_position_asc
                else
                  order_by(method)
                end
@@ -163,9 +161,9 @@ module Issuable
       #
       milestones_due_date = 'MIN(milestones.due_date)'
 
-      order_milestone_due_asc.
-        order_labels_priority(excluded_labels: excluded_labels, extra_select_columns: [milestones_due_date]).
-        reorder(Gitlab::Database.nulls_last_order(milestones_due_date, 'ASC'),
+      order_milestone_due_asc
+        .order_labels_priority(excluded_labels: excluded_labels, extra_select_columns: [milestones_due_date])
+        .reorder(Gitlab::Database.nulls_last_order(milestones_due_date, 'ASC'),
                 Gitlab::Database.nulls_last_order('highest_priority', 'ASC'))
     end
 
@@ -184,9 +182,9 @@ module Issuable
         "(#{highest_priority}) AS highest_priority"
       ] + extra_select_columns
 
-      select(select_columns.join(', ')).
-        group(arel_table[:id]).
-        reorder(Gitlab::Database.nulls_last_order('highest_priority', 'ASC'))
+      select(select_columns.join(', '))
+        .group(arel_table[:id])
+        .reorder(Gitlab::Database.nulls_last_order('highest_priority', 'ASC'))
     end
 
     def with_label(title, sort = nil)
diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb
index a3472af5c55c954e721d29bf9030b62f75b30662..01599ce49c64250116997aa5ff9c3c50b779f092 100644
--- a/app/models/concerns/milestoneish.rb
+++ b/app/models/concerns/milestoneish.rb
@@ -40,10 +40,18 @@ module Milestoneish
   def issues_visible_to_user(user)
     memoize_per_user(user, :issues_visible_to_user) do
       IssuesFinder.new(user, issues_finder_params)
-        .execute.includes(:assignees).where(milestone_id: milestoneish_ids)
+        .execute.preload(:assignees).where(milestone_id: milestoneish_ids)
     end
   end
 
+  def sorted_issues(user)
+    issues_visible_to_user(user).preload_associations.sort('label_priority')
+  end
+
+  def sorted_merge_requests
+    merge_requests.sort('label_priority')
+  end
+
   def upcoming?
     start_date && start_date.future?
   end
diff --git a/app/models/concerns/relative_positioning.rb b/app/models/concerns/relative_positioning.rb
index f1d8532a6d6f5e2dd664dbae8ecdf4555c20f735..7cb9a28a2843c6951ec76c672e95e69ad80d3501 100644
--- a/app/models/concerns/relative_positioning.rb
+++ b/app/models/concerns/relative_positioning.rb
@@ -18,10 +18,10 @@ module RelativePositioning
     prev_pos = nil
 
     if self.relative_position
-      prev_pos = self.class.
-        in_projects(project.id).
-        where('relative_position < ?', self.relative_position).
-        maximum(:relative_position)
+      prev_pos = self.class
+        .in_projects(project.id)
+        .where('relative_position < ?', self.relative_position)
+        .maximum(:relative_position)
     end
 
     prev_pos
@@ -31,10 +31,10 @@ module RelativePositioning
     next_pos = nil
 
     if self.relative_position
-      next_pos = self.class.
-        in_projects(project.id).
-        where('relative_position > ?', self.relative_position).
-        minimum(:relative_position)
+      next_pos = self.class
+        .in_projects(project.id)
+        .where('relative_position > ?', self.relative_position)
+        .minimum(:relative_position)
     end
 
     next_pos
diff --git a/app/models/concerns/routable.rb b/app/models/concerns/routable.rb
index 63d02b76f6b67b5768f3169ba5e15c1c476506ed..ec7796a9dbb0f0b245bc4d920f9e8da3a29f0dca 100644
--- a/app/models/concerns/routable.rb
+++ b/app/models/concerns/routable.rb
@@ -107,6 +107,14 @@ module Routable
     RequestStore[key] ||= uncached_full_path
   end
 
+  def build_full_path
+    if parent && path
+      parent.full_path + '/' + path
+    else
+      path
+    end
+  end
+
   private
 
   def uncached_full_path
@@ -135,14 +143,6 @@ module Routable
     end
   end
 
-  def build_full_path
-    if parent && path
-      parent.full_path + '/' + path
-    else
-      path
-    end
-  end
-
   def update_route
     prepare_route
     route.save
diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb
index b9a2d812edd429e9d3b3d4af0d2eacc2c0812921..a155a0640321f033b666b55b92a9f0ed84a9f05a 100644
--- a/app/models/concerns/sortable.rb
+++ b/app/models/concerns/sortable.rb
@@ -39,12 +39,12 @@ module Sortable
     private
 
     def highest_label_priority(target_type_column: nil, target_type: nil, target_column:, project_column:, excluded_labels: [])
-      query = Label.select(LabelPriority.arel_table[:priority].minimum).
-        left_join_priorities.
-        joins(:label_links).
-        where("label_priorities.project_id = #{project_column}").
-        where("label_links.target_id = #{target_column}").
-        reorder(nil)
+      query = Label.select(LabelPriority.arel_table[:priority].minimum)
+        .left_join_priorities
+        .joins(:label_links)
+        .where("label_priorities.project_id = #{project_column}")
+        .where("label_links.target_id = #{target_column}")
+        .reorder(nil)
 
       query =
         if target_type_column
diff --git a/app/models/concerns/subscribable.rb b/app/models/concerns/subscribable.rb
index 83daa9b1a64c87c376b3dec4c36d4b67f5cd2cdb..f60a0f8f438eacd3e3b9b53173bc809d5cdfd324 100644
--- a/app/models/concerns/subscribable.rb
+++ b/app/models/concerns/subscribable.rb
@@ -27,16 +27,16 @@ module Subscribable
   end
 
   def subscribers(project)
-    subscriptions_available(project).
-      where(subscribed: true).
-      map(&:user)
+    subscriptions_available(project)
+      .where(subscribed: true)
+      .map(&:user)
   end
 
   def toggle_subscription(user, project = nil)
     unsubscribe_from_other_levels(user, project)
 
-    find_or_initialize_subscription(user, project).
-      update(subscribed: !subscribed?(user, project))
+    find_or_initialize_subscription(user, project)
+      .update(subscribed: !subscribed?(user, project))
   end
 
   def subscribe(user, project = nil)
@@ -69,14 +69,14 @@ module Subscribable
   end
 
   def find_or_initialize_subscription(user, project)
-    subscriptions.
-      find_or_initialize_by(user_id: user.id, project_id: project.try(:id))
+    subscriptions
+      .find_or_initialize_by(user_id: user.id, project_id: project.try(:id))
   end
 
   def subscriptions_available(project)
     t = Subscription.arel_table
 
-    subscriptions.
-      where(t[:project_id].eq(nil).or(t[:project_id].eq(project.try(:id))))
+    subscriptions
+      .where(t[:project_id].eq(nil).or(t[:project_id].eq(project.try(:id))))
   end
 end
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
index 00bf0c118ae7557f4b200f756543dfab2ea32b82..056c49e7162e879c08376cb287d5cee2822d411d 100644
--- a/app/models/deployment.rb
+++ b/app/models/deployment.rb
@@ -58,10 +58,10 @@ class Deployment < ActiveRecord::Base
   def update_merge_request_metrics!
     return unless environment.update_merge_request_metrics?
 
-    merge_requests = project.merge_requests.
-                     joins(:metrics).
-                     where(target_branch: self.ref, merge_request_metrics: { first_deployed_to_production_at: nil }).
-                     where("merge_request_metrics.merged_at <= ?", self.created_at)
+    merge_requests = project.merge_requests
+                     .joins(:metrics)
+                     .where(target_branch: self.ref, merge_request_metrics: { first_deployed_to_production_at: nil })
+                     .where("merge_request_metrics.merged_at <= ?", self.created_at)
 
     if previous_deployment
       merge_requests = merge_requests.where("merge_request_metrics.merged_at >= ?", previous_deployment.created_at)
@@ -76,17 +76,17 @@ class Deployment < ActiveRecord::Base
         merge_requests.map(&:id)
       end
 
-    MergeRequest::Metrics.
-      where(merge_request_id: merge_request_ids, first_deployed_to_production_at: nil).
-      update_all(first_deployed_to_production_at: self.created_at)
+    MergeRequest::Metrics
+      .where(merge_request_id: merge_request_ids, first_deployed_to_production_at: nil)
+      .update_all(first_deployed_to_production_at: self.created_at)
   end
 
   def previous_deployment
     @previous_deployment ||=
-      project.deployments.joins(:environment).
-      where(environments: { name: self.environment.name }, ref: self.ref).
-      where.not(id: self.id).
-      take
+      project.deployments.joins(:environment)
+      .where(environments: { name: self.environment.name }, ref: self.ref)
+      .where.not(id: self.id)
+      .take
   end
 
   def stop_action
diff --git a/app/models/environment.rb b/app/models/environment.rb
index b391a487b304deccb266123a43273fda5863cf69..7ad36f1d80c36b91a8ceb1344a25d425d8e2d14f 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -40,9 +40,9 @@ class Environment < ActiveRecord::Base
   scope :stopped, -> { with_state(:stopped) }
   scope :order_by_last_deployed_at, -> do
     max_deployment_id_sql =
-      Deployment.select(Deployment.arel_table[:id].maximum).
-      where(Deployment.arel_table[:environment_id].eq(arel_table[:id])).
-      to_sql
+      Deployment.select(Deployment.arel_table[:id].maximum)
+      .where(Deployment.arel_table[:environment_id].eq(arel_table[:id]))
+      .to_sql
     order(Gitlab::Database.nulls_first_order("(#{max_deployment_id_sql})", 'ASC'))
   end
 
diff --git a/app/models/event.rb b/app/models/event.rb
index fad6ff03927f4780dab5e5f5ab4edcf57699a086..29bc141c5cdf7a0d4569e0a5f7849dc9c6343c14 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -376,9 +376,9 @@ class Event < ActiveRecord::Base
     # At this point it's possible for multiple threads/processes to try to
     # update the project. Only one query should actually perform the update,
     # hence we add the extra WHERE clause for last_activity_at.
-    Project.unscoped.where(id: project_id).
-      where('last_activity_at <= ?', RESET_PROJECT_ACTIVITY_INTERVAL.ago).
-      update_all(last_activity_at: created_at)
+    Project.unscoped.where(id: project_id)
+      .where('last_activity_at <= ?', RESET_PROJECT_ACTIVITY_INTERVAL.ago)
+      .update_all(last_activity_at: created_at)
   end
 
   def authored_by?(user)
@@ -392,7 +392,7 @@ class Event < ActiveRecord::Base
   end
 
   def set_last_repository_updated_at
-    Project.unscoped.where(id: project_id).
-      update_all(last_repository_updated_at: created_at)
+    Project.unscoped.where(id: project_id)
+      .update_all(last_repository_updated_at: created_at)
   end
 end
diff --git a/app/models/group.rb b/app/models/group.rb
index 5bb2cdc5eff793e3380049271bd06b109190dc7b..0b93460d4731384540bb9935c405bd30cb6013d3 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -206,8 +206,8 @@ class Group < Namespace
   end
 
   def refresh_members_authorized_projects
-    UserProjectAccessChangedService.new(user_ids_for_project_authorizations).
-      execute
+    UserProjectAccessChangedService.new(user_ids_for_project_authorizations)
+      .execute
   end
 
   def user_ids_for_project_authorizations
@@ -225,10 +225,10 @@ class Group < Namespace
   def max_member_access_for_user(user)
     return GroupMember::OWNER if user.admin?
 
-    members_with_parents.
-      where(user_id: user).
-      reorder(access_level: :desc).
-      first&.
+    members_with_parents
+      .where(user_id: user)
+      .reorder(access_level: :desc)
+      .first&.
       access_level || GroupMember::NO_ACCESS
   end
 
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 693cc21bb405063190fc9fd8f51a0fb5ebb72141..3a9a6dba601bb3641a0e2e6ac00a098f0949dd79 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -9,6 +9,9 @@ class Issue < ActiveRecord::Base
   include Spammable
   include FasterCacheKeys
   include RelativePositioning
+  include IgnorableColumn
+
+  ignore_column :position
 
   DueDateStruct = Struct.new(:title, :name).freeze
   NoDueDate     = DueDateStruct.new('No Due Date', '0').freeze
@@ -44,7 +47,7 @@ class Issue < ActiveRecord::Base
 
   scope :created_after, -> (datetime) { where("created_at >= ?", datetime) }
 
-  scope :include_associations, -> { includes(:labels, project: :namespace) }
+  scope :preload_associations, -> { preload(:labels, project: :namespace) }
 
   after_save :expire_etag_cache
 
@@ -121,8 +124,8 @@ class Issue < ActiveRecord::Base
   end
 
   def self.order_by_position_and_priority
-    order_labels_priority.
-      reorder(Gitlab::Database.nulls_last_order('relative_position', 'ASC'),
+    order_labels_priority
+      .reorder(Gitlab::Database.nulls_last_order('relative_position', 'ASC'),
               Gitlab::Database.nulls_last_order('highest_priority', 'ASC'),
               "id DESC")
   end
diff --git a/app/models/issue_collection.rb b/app/models/issue_collection.rb
index f0b7d9914c80167f9c5aa94cbcaa3dd9e9b019d7..49f011c113f58d96adcf5f3fd41104b5ae599e6f 100644
--- a/app/models/issue_collection.rb
+++ b/app/models/issue_collection.rb
@@ -17,9 +17,9 @@ class IssueCollection
 
     # Given all the issue projects we get a list of projects that the current
     # user has at least reporter access to.
-    projects_with_reporter_access = user.
-      projects_with_reporter_access_limited_to(project_ids).
-      pluck(:id)
+    projects_with_reporter_access = user
+      .projects_with_reporter_access_limited_to(project_ids)
+      .pluck(:id)
 
     collection.select do |issue|
       if projects_with_reporter_access.include?(issue.project_id)
diff --git a/app/models/label.rb b/app/models/label.rb
index 955d6b4079b633f94bd6f7394846e72c0897fb86..ed6a8411da9dd2de3f57bd14a2eee9e3b7822daf 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -46,9 +46,9 @@ class Label < ActiveRecord::Base
     labels = Label.arel_table
     priorities = LabelPriority.arel_table
 
-    label_priorities = labels.join(priorities, Arel::Nodes::OuterJoin).
-                              on(labels[:id].eq(priorities[:label_id]).and(priorities[:project_id].eq(project.id))).
-                              join_sources
+    label_priorities = labels.join(priorities, Arel::Nodes::OuterJoin)
+                              .on(labels[:id].eq(priorities[:label_id]).and(priorities[:project_id].eq(project.id)))
+                              .join_sources
 
     joins(label_priorities).where(priorities[:priority].eq(nil))
   end
@@ -57,9 +57,9 @@ class Label < ActiveRecord::Base
     labels = Label.arel_table
     priorities = LabelPriority.arel_table
 
-    label_priorities = labels.join(priorities, Arel::Nodes::OuterJoin).
-                              on(labels[:id].eq(priorities[:label_id])).
-                              join_sources
+    label_priorities = labels.join(priorities, Arel::Nodes::OuterJoin)
+                              .on(labels[:id].eq(priorities[:label_id]))
+                              .join_sources
 
     joins(label_priorities)
   end
diff --git a/app/models/legacy_diff_note.rb b/app/models/legacy_diff_note.rb
index 7126de2d488ead32b6dac5336c30c4afeac9ee52..2d5909ab25eac5abd871e261e4b5ef447d2f9240 100644
--- a/app/models/legacy_diff_note.rb
+++ b/app/models/legacy_diff_note.rb
@@ -42,7 +42,7 @@ class LegacyDiffNote < Note
   end
 
   def for_line?(line)
-    !line.meta? && diff_file.line_code(line) == self.line_code
+    line.discussable? && diff_file.line_code(line) == self.line_code
   end
 
   def original_line_code
diff --git a/app/models/member.rb b/app/models/member.rb
index 788a32dd8e3b14ba242ca6c2c2e3023ba48d9ba4..dc9247bc9a0da79e93013d09fa4e9f1377cdd708 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -99,9 +99,9 @@ class Member < ActiveRecord::Base
       users = User.arel_table
       members = Member.arel_table
 
-      member_users = members.join(users, Arel::Nodes::OuterJoin).
-                             on(members[:user_id].eq(users[:id])).
-                             join_sources
+      member_users = members.join(users, Arel::Nodes::OuterJoin)
+                             .on(members[:user_id].eq(users[:id]))
+                             .join_sources
 
       joins(member_users)
     end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index dd155252ad5f8049853f92754e72919436d366db..f581a25f09338cb7e652114f4fc51ed04d01a8c4 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -4,6 +4,9 @@ class MergeRequest < ActiveRecord::Base
   include Noteable
   include Referable
   include Sortable
+  include IgnorableColumn
+
+  ignore_column :position
 
   belongs_to :target_project, class_name: "Project"
   belongs_to :source_project, class_name: "Project"
@@ -574,8 +577,8 @@ class MergeRequest < ActiveRecord::Base
       messages = [title, description]
       messages.concat(commits.map(&:safe_message)) if merge_request_diff
 
-      Gitlab::ClosingIssueExtractor.new(project, current_user).
-        closed_by_message(messages.join("\n"))
+      Gitlab::ClosingIssueExtractor.new(project, current_user)
+        .closed_by_message(messages.join("\n"))
     else
       []
     end
@@ -889,7 +892,7 @@ class MergeRequest < ActiveRecord::Base
     !has_commits?
   end
 
-  def mergeable_with_slash_command?(current_user, autocomplete_precheck: false, last_diff_sha: nil)
+  def mergeable_with_quick_action?(current_user, autocomplete_precheck: false, last_diff_sha: nil)
     return false unless can_be_merged_by?(current_user)
 
     return true if autocomplete_precheck
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index 99dd21301880d71465d2b805d495b54a21e1105e..f1ee4d3f7a91d7781005e0c6b997d5b00e546e21 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -10,6 +10,7 @@ class MergeRequestDiff < ActiveRecord::Base
   VALID_CLASSES = [Hash, Rugged::Patch, Rugged::Diff::Delta].freeze
 
   belongs_to :merge_request
+  has_many :merge_request_diff_files, -> { order(:merge_request_diff_id, :relative_order) }
 
   serialize :st_commits # rubocop:disable Cop/ActiverecordSerialize
   serialize :st_diffs # rubocop:disable Cop/ActiverecordSerialize
@@ -91,7 +92,7 @@ class MergeRequestDiff < ActiveRecord::Base
           head_commit_sha).diffs(options)
     else
       @raw_diffs ||= {}
-      @raw_diffs[options] ||= load_diffs(st_diffs, options)
+      @raw_diffs[options] ||= load_diffs(options)
     end
   end
 
@@ -253,24 +254,44 @@ class MergeRequestDiff < ActiveRecord::Base
     update_columns_serialized(new_attributes)
   end
 
-  def dump_diffs(diffs)
-    if diffs.respond_to?(:map)
-      diffs.map(&:to_hash)
+  def create_merge_request_diff_files(diffs)
+    rows = diffs.map.with_index do |diff, index|
+      diff.to_hash.merge(
+        merge_request_diff_id: self.id,
+        relative_order: index
+      )
     end
+
+    Gitlab::Database.bulk_insert('merge_request_diff_files', rows)
   end
 
-  def load_diffs(raw, options)
-    if valid_raw_diff?(raw)
-      if paths = options[:paths]
-        raw = raw.select do |diff|
-          paths.include?(diff[:old_path]) || paths.include?(diff[:new_path])
-        end
-      end
+  def load_diffs(options)
+    return Gitlab::Git::DiffCollection.new([]) unless diffs_from_database
 
-      Gitlab::Git::DiffCollection.new(raw, options)
-    else
-      Gitlab::Git::DiffCollection.new([])
+    raw = diffs_from_database
+
+    if paths = options[:paths]
+      raw = raw.select do |diff|
+        paths.include?(diff[:old_path]) || paths.include?(diff[:new_path])
+      end
     end
+
+    Gitlab::Git::DiffCollection.new(raw, options)
+  end
+
+  def diffs_from_database
+    return @diffs_from_database if defined?(@diffs_from_database)
+
+    @diffs_from_database =
+      if st_diffs.present?
+        if valid_raw_diff?(st_diffs)
+          st_diffs
+        end
+      elsif merge_request_diff_files.present?
+        merge_request_diff_files
+          .as_json(only: Gitlab::Git::Diff::SERIALIZE_KEYS)
+          .map(&:with_indifferent_access)
+      end
   end
 
   # Load diffs between branches related to current merge request diff from repo
@@ -285,11 +306,10 @@ class MergeRequestDiff < ActiveRecord::Base
       new_attributes[:real_size] = diff_collection.real_size
 
       if diff_collection.any?
-        new_diffs = dump_diffs(diff_collection)
         new_attributes[:state] = :collected
-      end
 
-      new_attributes[:st_diffs] = new_diffs || []
+        create_merge_request_diff_files(diff_collection)
+      end
 
       # Set our state to 'overflow' to make the #empty? and #collected?
       # methods (generated by StateMachine) return false.
diff --git a/app/models/merge_request_diff_file.rb b/app/models/merge_request_diff_file.rb
new file mode 100644
index 0000000000000000000000000000000000000000..598ebd4d829ed718e237c72678517ff0c6928d85
--- /dev/null
+++ b/app/models/merge_request_diff_file.rb
@@ -0,0 +1,11 @@
+class MergeRequestDiffFile < ActiveRecord::Base
+  include Gitlab::EncodingHelper
+
+  belongs_to :merge_request_diff
+
+  def utf8_diff
+    return '' if diff.blank?
+
+    encode_utf8(diff) if diff.respond_to?(:encoding)
+  end
+end
diff --git a/app/models/merge_requests_closing_issues.rb b/app/models/merge_requests_closing_issues.rb
index daafb137be4c2be3bb7eb360abad55d025ac10df..7f7c114803dd55154a33ba209a406b504702a4be 100644
--- a/app/models/merge_requests_closing_issues.rb
+++ b/app/models/merge_requests_closing_issues.rb
@@ -7,9 +7,9 @@ class MergeRequestsClosingIssues < ActiveRecord::Base
 
   class << self
     def count_for_collection(ids)
-      group(:issue_id).
-        where(issue_id: ids).
-        pluck('issue_id', 'COUNT(*) as count')
+      group(:issue_id)
+        .where(issue_id: ids)
+        .pluck('issue_id', 'COUNT(*) as count')
     end
   end
 end
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index b04bed4c0147679a04f948771886c3a95047a7f3..d2e2749f70dfce1696b5f9b01e7f752cf1632c49 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -98,11 +98,11 @@ class Milestone < ActiveRecord::Base
     if Gitlab::Database.postgresql?
       rel.order(:project_id, :due_date).select('DISTINCT ON (project_id) id')
     else
-      rel.
-        group(:project_id).
-        having('due_date = MIN(due_date)').
-        pluck(:id, :project_id, :due_date).
-        map(&:first)
+      rel
+        .group(:project_id)
+        .having('due_date = MIN(due_date)')
+        .pluck(:id, :project_id, :due_date)
+        .map(&:first)
     end
   end
 
@@ -164,38 +164,6 @@ class Milestone < ActiveRecord::Base
     write_attribute(:title, sanitize_title(value)) if value.present?
   end
 
-  # Sorts the issues for the given IDs.
-  #
-  # This method runs a single SQL query using a CASE statement to update the
-  # position of all issues in the current milestone (scoped to the list of IDs).
-  #
-  # Given the ids [10, 20, 30] this method produces a SQL query something like
-  # the following:
-  #
-  #     UPDATE issues
-  #     SET position = CASE
-  #       WHEN id = 10 THEN 1
-  #       WHEN id = 20 THEN 2
-  #       WHEN id = 30 THEN 3
-  #       ELSE position
-  #     END
-  #     WHERE id IN (10, 20, 30);
-  #
-  # This method expects that the IDs given in `ids` are already Fixnums.
-  def sort_issues(ids)
-    pairs = []
-
-    ids.each_with_index do |id, index|
-      pairs << id
-      pairs << index + 1
-    end
-
-    conditions = 'WHEN id = ? THEN ? ' * ids.length
-
-    issues.where(id: ids).
-      update_all(["position = CASE #{conditions} ELSE position END", *pairs])
-  end
-
   private
 
   def milestone_format_reference(format = :iid)
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index b48d73dcae7e90e6a05ef9f25a2a958909c3927e..583d4fb52445b6af545e98e75c3cab0b83398061 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -181,16 +181,16 @@ class Namespace < ActiveRecord::Base
   def ancestors
     return self.class.none unless parent_id
 
-    Gitlab::GroupHierarchy.
-      new(self.class.where(id: parent_id)).
-      base_and_ancestors
+    Gitlab::GroupHierarchy
+      .new(self.class.where(id: parent_id))
+      .base_and_ancestors
   end
 
   # Returns all the descendants of the current namespace.
   def descendants
-    Gitlab::GroupHierarchy.
-      new(self.class.where(parent_id: id)).
-      base_and_descendants
+    Gitlab::GroupHierarchy
+      .new(self.class.where(parent_id: id))
+      .base_and_descendants
   end
 
   def user_ids_for_project_authorizations
@@ -253,10 +253,10 @@ class Namespace < ActiveRecord::Base
   end
 
   def refresh_access_of_projects_invited_groups
-    Group.
-      joins(project_group_links: :project).
-      where(projects: { namespace_id: id }).
-      find_each(&:refresh_members_authorized_projects)
+    Group
+      .joins(project_group_links: :project)
+      .where(projects: { namespace_id: id })
+      .find_each(&:refresh_members_authorized_projects)
   end
 
   def remove_exports!
diff --git a/app/models/note.rb b/app/models/note.rb
index 244bf169c290c2c47a6fd6cb55c6cc397e2d4d1c..ca6999427c08bf6ff086643401d70ce4b4ab020b 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -32,7 +32,7 @@ class Note < ActiveRecord::Base
   # Banzai::ObjectRenderer
   attr_accessor :user_visible_reference_count
 
-  # Attribute used to store the attributes that have ben changed by slash commands.
+  # Attribute used to store the attributes that have ben changed by quick actions.
   attr_accessor :commands_changes
 
   default_value_for :system, false
@@ -137,9 +137,9 @@ class Note < ActiveRecord::Base
     end
 
     def count_for_collection(ids, type)
-      user.select('noteable_id', 'COUNT(*) as count').
-        group(:noteable_id).
-        where(noteable_type: type, noteable_id: ids)
+      user.select('noteable_id', 'COUNT(*) as count')
+        .group(:noteable_id)
+        .where(noteable_type: type, noteable_id: ids)
     end
   end
 
diff --git a/app/models/project.rb b/app/models/project.rb
index 4c394646787c50336d6f062944e78d3650cc1bd8..2c2685875f8e3def8924675c466a590fa348b99d 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -244,8 +244,8 @@ class Project < ActiveRecord::Base
   scope :inside_path, ->(path) do
     # We need routes alias rs for JOIN so it does not conflict with
     # includes(:route) which we use in ProjectsFinder.
-    joins("INNER JOIN routes rs ON rs.source_id = projects.id AND rs.source_type = 'Project'").
-      where('rs.path LIKE ?', "#{sanitize_sql_like(path)}/%")
+    joins("INNER JOIN routes rs ON rs.source_id = projects.id AND rs.source_type = 'Project'")
+      .where('rs.path LIKE ?', "#{sanitize_sql_like(path)}/%")
   end
 
   # "enabled" here means "not disabled". It includes private features!
@@ -266,20 +266,49 @@ class Project < ActiveRecord::Base
 
   enum auto_cancel_pending_pipelines: { disabled: 0, enabled: 1 }
 
+  # Returns a collection of projects that is either public or visible to the
+  # logged in user.
+  def self.public_or_visible_to_user(user = nil)
+    if user
+      authorized = user
+        .project_authorizations
+        .select(1)
+        .where('project_authorizations.project_id = projects.id')
+
+      levels = Gitlab::VisibilityLevel.levels_for_user(user)
+
+      where('EXISTS (?) OR projects.visibility_level IN (?)', authorized, levels)
+    else
+      public_to_user
+    end
+  end
+
   # project features may be "disabled", "internal" or "enabled". If "internal",
   # they are only available to team members. This scope returns projects where
   # the feature is either enabled, or internal with permission for the user.
+  #
+  # This method uses an optimised version of `with_feature_access_level` for
+  # logged in users to more efficiently get private projects with the given
+  # feature.
   def self.with_feature_available_for_user(feature, user)
-    return with_feature_enabled(feature) if user.try(:admin?)
+    visible = [nil, ProjectFeature::ENABLED]
 
-    unconditional = with_feature_access_level(feature, [nil, ProjectFeature::ENABLED])
-    return unconditional if user.nil?
+    if user&.admin?
+      with_feature_enabled(feature)
+    elsif user
+      column = ProjectFeature.quoted_access_level_column(feature)
 
-    conditional = with_feature_access_level(feature, ProjectFeature::PRIVATE)
-    authorized = user.authorized_projects.merge(conditional.reorder(nil))
+      authorized = user.project_authorizations.select(1)
+        .where('project_authorizations.project_id = projects.id')
 
-    union = Gitlab::SQL::Union.new([unconditional.select(:id), authorized.select(:id)])
-    where(arel_table[:id].in(Arel::Nodes::SqlLiteral.new(union.to_sql)))
+      with_project_feature
+        .where("#{column} IN (?) OR (#{column} = ? AND EXISTS (?))",
+              visible,
+              ProjectFeature::PRIVATE,
+              authorized)
+    else
+      with_feature_access_level(feature, visible)
+    end
   end
 
   scope :active, -> { joins(:issues, :notes, :merge_requests).order('issues.created_at, notes.created_at, merge_requests.created_at DESC') }
@@ -340,14 +369,14 @@ class Project < ActiveRecord::Base
       # unscoping unnecessary conditions that'll be applied
       # when executing `where("projects.id IN (#{union.to_sql})")`
       projects = unscoped.select(:id).where(
-        ptable[:path].matches(pattern).
-          or(ptable[:name].matches(pattern)).
-          or(ptable[:description].matches(pattern))
+        ptable[:path].matches(pattern)
+          .or(ptable[:name].matches(pattern))
+          .or(ptable[:description].matches(pattern))
       )
 
-      namespaces = unscoped.select(:id).
-        joins(:namespace).
-        where(ntable[:name].matches(pattern))
+      namespaces = unscoped.select(:id)
+        .joins(:namespace)
+        .where(ntable[:name].matches(pattern))
 
       union = Gitlab::SQL::Union.new([projects, namespaces])
 
@@ -388,8 +417,8 @@ class Project < ActiveRecord::Base
     end
 
     def trending
-      joins('INNER JOIN trending_projects ON projects.id = trending_projects.project_id').
-        reorder('trending_projects.id ASC')
+      joins('INNER JOIN trending_projects ON projects.id = trending_projects.project_id')
+        .reorder('trending_projects.id ASC')
     end
 
     def cached_count
diff --git a/app/models/project_authorization.rb b/app/models/project_authorization.rb
index def0967525378578a30d2013ced9c5a0a8514761..73302207e6bbe0bdb963894fd3560d35a3fb3ce9 100644
--- a/app/models/project_authorization.rb
+++ b/app/models/project_authorization.rb
@@ -7,9 +7,9 @@ class ProjectAuthorization < ActiveRecord::Base
   validates :user, uniqueness: { scope: [:project, :access_level] }, presence: true
 
   def self.select_from_union(union)
-    select(['project_id', 'MAX(access_level) AS access_level']).
-      from("(#{union.to_sql}) #{ProjectAuthorization.table_name}").
-      group(:project_id)
+    select(['project_id', 'MAX(access_level) AS access_level'])
+      .from("(#{union.to_sql}) #{ProjectAuthorization.table_name}")
+      .group(:project_id)
   end
 
   def self.insert_authorizations(rows, per_batch = 1000)
diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb
index e3ef4919b2877ec7caa094a836289c50aa6742b2..dde2a11440d7ea2bcdd02e72fea287e2ef8f56c6 100644
--- a/app/models/project_feature.rb
+++ b/app/models/project_feature.rb
@@ -27,6 +27,13 @@ class ProjectFeature < ActiveRecord::Base
 
       "#{feature}_access_level".to_sym
     end
+
+    def quoted_access_level_column(feature)
+      attribute = connection.quote_column_name(access_level_attribute(feature))
+      table = connection.quote_table_name(table_name)
+
+      "#{table}.#{attribute}"
+    end
   end
 
   # Default scopes force us to unscope here since a service may need to check
diff --git a/app/models/project_services/chat_message/pipeline_message.rb b/app/models/project_services/chat_message/pipeline_message.rb
index 3edc395033cf234214725d22ad7b8eff680b1807..d63d4ec2b127d316abc2b1a44989bb282c0cea7f 100644
--- a/app/models/project_services/chat_message/pipeline_message.rb
+++ b/app/models/project_services/chat_message/pipeline_message.rb
@@ -70,7 +70,7 @@ module ChatMessage
     end
 
     def branch_link
-      "`[#{ref}](#{branch_url})`"
+      "[#{ref}](#{branch_url})"
     end
 
     def project_link
diff --git a/app/models/project_services/chat_message/push_message.rb b/app/models/project_services/chat_message/push_message.rb
index 04a59d559cae0f32a61d82134e23f915378b284b..c52dd6ef8ef451595dd26eda7f536bb79db76749 100644
--- a/app/models/project_services/chat_message/push_message.rb
+++ b/app/models/project_services/chat_message/push_message.rb
@@ -61,7 +61,7 @@ module ChatMessage
     end
 
     def removed_branch_message
-      "#{user_name} removed #{ref_type} `#{ref}` from #{project_link}"
+      "#{user_name} removed #{ref_type} #{ref} from #{project_link}"
     end
 
     def push_message
@@ -102,7 +102,7 @@ module ChatMessage
     end
 
     def branch_link
-      "`[#{ref}](#{branch_url})`"
+      "[#{ref}](#{branch_url})"
     end
 
     def project_link
diff --git a/app/models/project_services/mattermost_slash_commands_service.rb b/app/models/project_services/mattermost_slash_commands_service.rb
index 56f42d63b2d0c765c388d747406b4eaf7605f230..4d2037286a2e15f443a7e6851065e4a478549b22 100644
--- a/app/models/project_services/mattermost_slash_commands_service.rb
+++ b/app/models/project_services/mattermost_slash_commands_service.rb
@@ -1,4 +1,4 @@
-class MattermostSlashCommandsService < ChatSlashCommandsService
+class MattermostSlashCommandsService < SlashCommandsService
   include TriggersHelper
 
   prop_accessor :token
@@ -20,8 +20,8 @@ class MattermostSlashCommandsService < ChatSlashCommandsService
   end
 
   def configure(user, params)
-    token = Mattermost::Command.new(user).
-      create(command(params))
+    token = Mattermost::Command.new(user)
+      .create(command(params))
 
     update(active: true, token: token) if token
   rescue Mattermost::Error => e
diff --git a/app/models/project_services/slack_slash_commands_service.rb b/app/models/project_services/slack_slash_commands_service.rb
index 2182c1c7e4b733ba6db50c0319ce54a8f6973d16..1c3892a3f75d579f3e04db5f5a95313954af1bbd 100644
--- a/app/models/project_services/slack_slash_commands_service.rb
+++ b/app/models/project_services/slack_slash_commands_service.rb
@@ -1,4 +1,4 @@
-class SlackSlashCommandsService < ChatSlashCommandsService
+class SlackSlashCommandsService < SlashCommandsService
   include TriggersHelper
 
   def title
diff --git a/app/models/project_services/chat_slash_commands_service.rb b/app/models/project_services/slash_commands_service.rb
similarity index 84%
rename from app/models/project_services/chat_slash_commands_service.rb
rename to app/models/project_services/slash_commands_service.rb
index 8b5bc24fd3c6b047df4365e7e125e41ade56e85a..4592cb747a0bbf5df6d257aaee28476ddaa01344 100644
--- a/app/models/project_services/chat_slash_commands_service.rb
+++ b/app/models/project_services/slash_commands_service.rb
@@ -1,6 +1,6 @@
 # Base class for Chat services
 # This class is not meant to be used directly, but only to inherrit from.
-class ChatSlashCommandsService < Service
+class SlashCommandsService < Service
   default_value_for :category, 'chat'
 
   prop_accessor :token
@@ -33,10 +33,10 @@ class ChatSlashCommandsService < Service
     user = find_chat_user(params)
 
     if user
-      Gitlab::ChatCommands::Command.new(project, user, params).execute
+      Gitlab::SlashCommands::Command.new(project, user, params).execute
     else
       url = authorize_chat_name_url(params)
-      Gitlab::ChatCommands::Presenters::Access.new(url).authorize
+      Gitlab::SlashCommands::Presenters::Access.new(url).authorize
     end
   end
 
diff --git a/app/models/project_team.rb b/app/models/project_team.rb
index e1cc56551bae324b5841703ed6a880dbb09b8d5a..674eacd28e8e143e3ce3547a5db8def792156081 100644
--- a/app/models/project_team.rb
+++ b/app/models/project_team.rb
@@ -172,10 +172,10 @@ class ProjectTeam
 
     return access if user_ids.empty?
 
-    users_access = project.project_authorizations.
-      where(user: user_ids).
-      group(:user_id).
-      maximum(:access_level)
+    users_access = project.project_authorizations
+      .where(user: user_ids)
+      .group(:user_id)
+      .maximum(:access_level)
 
     access.merge!(users_access)
 
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 7460515fea8c166da3f43ce65ce0a7ec2aca05f3..c67475357d99e1642163ed31009c5633d21b6555 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -241,11 +241,11 @@ class Repository
     cache.fetch(:"diverging_commit_counts_#{branch.name}") do
       # Rugged seems to throw a `ReferenceError` when given branch_names rather
       # than SHA-1 hashes
-      number_commits_behind = raw_repository.
-        count_commits_between(branch.dereferenced_target.sha, root_ref_hash)
+      number_commits_behind = raw_repository
+        .count_commits_between(branch.dereferenced_target.sha, root_ref_hash)
 
-      number_commits_ahead = raw_repository.
-        count_commits_between(root_ref_hash, branch.dereferenced_target.sha)
+      number_commits_ahead = raw_repository
+        .count_commits_between(root_ref_hash, branch.dereferenced_target.sha)
 
       { behind: number_commits_behind, ahead: number_commits_ahead }
     end
diff --git a/app/models/todo.rb b/app/models/todo.rb
index 696d139af7485b86a6090f028df48bb8f3a29273..7af54b2beb25f7918ad4cc15e95c167fdba32f9a 100644
--- a/app/models/todo.rb
+++ b/app/models/todo.rb
@@ -70,9 +70,9 @@ class Todo < ActiveRecord::Base
 
       highest_priority = highest_label_priority(params).to_sql
 
-      select("#{table_name}.*, (#{highest_priority}) AS highest_priority").
-        order(Gitlab::Database.nulls_last_order('highest_priority', 'ASC')).
-        order('todos.created_at')
+      select("#{table_name}.*, (#{highest_priority}) AS highest_priority")
+        .order(Gitlab::Database.nulls_last_order('highest_priority', 'ASC'))
+        .order('todos.created_at')
     end
   end
 
diff --git a/app/models/user.rb b/app/models/user.rb
index 5d128e4b390fd1de8321b1ac3b96e3dddfad9f34..954a30155f76d0fb98b07b7f530841f5477c8751 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -139,21 +139,21 @@ class User < ActiveRecord::Base
     presence: true,
     uniqueness: { case_sensitive: false }
 
-  validate :namespace_uniq, if: ->(user) { user.username_changed? }
+  validate :namespace_uniq, if: :username_changed?
   validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
-  validate :unique_email, if: ->(user) { user.email_changed? }
-  validate :owns_notification_email, if: ->(user) { user.notification_email_changed? }
-  validate :owns_public_email, if: ->(user) { user.public_email_changed? }
+  validate :unique_email, if: :email_changed?
+  validate :owns_notification_email, if: :notification_email_changed?
+  validate :owns_public_email, if: :public_email_changed?
   validate :signup_domain_valid?, on: :create, if: ->(user) { !user.created_by_id }
   validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
 
   before_validation :sanitize_attrs
-  before_validation :set_notification_email, if: ->(user) { user.email_changed? }
-  before_validation :set_public_email, if: ->(user) { user.public_email_changed? }
+  before_validation :set_notification_email, if: :email_changed?
+  before_validation :set_public_email, if: :public_email_changed?
 
-  after_update :update_emails_with_primary_email, if: ->(user) { user.email_changed? }
+  after_update :update_emails_with_primary_email, if: :email_changed?
   before_save :ensure_authentication_token, :ensure_incoming_email_token
-  before_save :ensure_external_user_rights
+  before_save :ensure_user_rights_and_limits, if: :external_changed?
   after_save :ensure_namespace_correct
   after_initialize :set_projects_limit
   after_destroy :post_destroy_hook
@@ -223,13 +223,13 @@ class User < ActiveRecord::Base
   scope :order_oldest_sign_in, -> { reorder(Gitlab::Database.nulls_last_order('last_sign_in_at', 'ASC')) }
 
   def self.with_two_factor
-    joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id").
-      where("u2f.id IS NOT NULL OR otp_required_for_login = ?", true).distinct(arel_table[:id])
+    joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id")
+      .where("u2f.id IS NOT NULL OR otp_required_for_login = ?", true).distinct(arel_table[:id])
   end
 
   def self.without_two_factor
-    joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id").
-      where("u2f.id IS NULL AND otp_required_for_login = ?", false)
+    joins("LEFT OUTER JOIN u2f_registrations AS u2f ON u2f.user_id = users.id")
+      .where("u2f.id IS NULL AND otp_required_for_login = ?", false)
   end
 
   #
@@ -300,9 +300,9 @@ class User < ActiveRecord::Base
       pattern = "%#{query}%"
 
       where(
-        table[:name].matches(pattern).
-          or(table[:email].matches(pattern)).
-          or(table[:username].matches(pattern))
+        table[:name].matches(pattern)
+          .or(table[:email].matches(pattern))
+          .or(table[:username].matches(pattern))
       )
     end
 
@@ -317,10 +317,10 @@ class User < ActiveRecord::Base
       matched_by_emails_user_ids = email_table.project(email_table[:user_id]).where(email_table[:email].matches(pattern))
 
       where(
-        table[:name].matches(pattern).
-          or(table[:email].matches(pattern)).
-          or(table[:username].matches(pattern)).
-          or(table[:id].in(matched_by_emails_user_ids))
+        table[:name].matches(pattern)
+          .or(table[:email].matches(pattern))
+          .or(table[:username].matches(pattern))
+          .or(table[:id].in(matched_by_emails_user_ids))
       )
     end
 
@@ -503,8 +503,8 @@ class User < ActiveRecord::Base
 
   # Returns the groups a user has access to
   def authorized_groups
-    union = Gitlab::SQL::Union.
-      new([groups.select(:id), authorized_projects.select(:namespace_id)])
+    union = Gitlab::SQL::Union
+      .new([groups.select(:id), authorized_projects.select(:namespace_id)])
 
     Group.where("namespaces.id IN (#{union.to_sql})")
   end
@@ -533,8 +533,8 @@ class User < ActiveRecord::Base
     projects = super()
 
     if min_access_level
-      projects = projects.
-        where('project_authorizations.access_level >= ?', min_access_level)
+      projects = projects
+        .where('project_authorizations.access_level >= ?', min_access_level)
     end
 
     projects
@@ -619,9 +619,9 @@ class User < ActiveRecord::Base
       next unless project
 
       if project.repository.branch_exists?(event.branch_name)
-        merge_requests = MergeRequest.where("created_at >= ?", event.created_at).
-          where(source_project_id: project.id,
-                source_branch: event.branch_name)
+        merge_requests = MergeRequest.where("created_at >= ?", event.created_at)
+          .where(source_project_id: project.id,
+                 source_branch: event.branch_name)
         merge_requests.empty?
       end
     end
@@ -832,8 +832,8 @@ class User < ActiveRecord::Base
 
   def toggle_star(project)
     UsersStarProject.transaction do
-      user_star_project = users_star_projects.
-          where(project: project, user: self).lock(true).first
+      user_star_project = users_star_projects
+          .where(project: project, user: self).lock(true).first
 
       if user_star_project
         user_star_project.destroy
@@ -869,11 +869,11 @@ class User < ActiveRecord::Base
   # ms on a database with a similar size to GitLab.com's database. On the other
   # hand, using a subquery means we can get the exact same data in about 40 ms.
   def contributed_projects
-    events = Event.select(:project_id).
-      contributions.where(author_id: self).
-      where("created_at > ?", Time.now - 1.year).
-      uniq.
-      reorder(nil)
+    events = Event.select(:project_id)
+      .contributions.where(author_id: self)
+      .where("created_at > ?", Time.now - 1.year)
+      .uniq
+      .reorder(nil)
 
     Project.where(id: events)
   end
@@ -884,9 +884,9 @@ class User < ActiveRecord::Base
 
   def ci_authorized_runners
     @ci_authorized_runners ||= begin
-      runner_ids = Ci::RunnerProject.
-        where("ci_runner_projects.project_id IN (#{ci_projects_union.to_sql})").
-        select(:runner_id)
+      runner_ids = Ci::RunnerProject
+        .where("ci_runner_projects.project_id IN (#{ci_projects_union.to_sql})")
+        .select(:runner_id)
       Ci::Runner.specific.where(id: runner_ids)
     end
   end
@@ -1033,11 +1033,14 @@ class User < ActiveRecord::Base
     super
   end
 
-  def ensure_external_user_rights
-    return unless external?
-
-    self.can_create_group   = false
-    self.projects_limit     = 0
+  def ensure_user_rights_and_limits
+    if external?
+      self.can_create_group = false
+      self.projects_limit   = 0
+    else
+      self.can_create_group = gitlab_config.default_can_create_group
+      self.projects_limit = current_application_settings.default_projects_limit
+    end
   end
 
   def signup_domain_valid?
diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb
index c771c22f46a1620b37986d738eedac588be66cfc..224eb3cd4d01973a09e2c5eacb8b0a920f69d6ef 100644
--- a/app/models/wiki_page.rb
+++ b/app/models/wiki_page.rb
@@ -22,16 +22,16 @@ class WikiPage
   def self.group_by_directory(pages)
     return [] if pages.blank?
 
-    pages.sort_by { |page| [page.directory, page.slug] }.
-      group_by(&:directory).
-      map do |dir, pages|
+    pages.sort_by { |page| [page.directory, page.slug] }
+      .group_by(&:directory)
+      .map do |dir, pages|
         if dir.present?
           WikiDirectory.new(dir, pages)
         else
           pages
         end
-      end.
-      flatten
+      end
+      .flatten
   end
 
   def self.unhyphenize(name)
diff --git a/app/policies/global_policy.rb b/app/policies/global_policy.rb
index 4757ba7168089776b1bb64d5943704095e2d5022..2683aaad9814d9b062871a234491373df9a26a92 100644
--- a/app/policies/global_policy.rb
+++ b/app/policies/global_policy.rb
@@ -10,7 +10,7 @@ class GlobalPolicy < BasePolicy
       can! :access_api
       can! :access_git
       can! :receive_notifications
-      can! :use_slash_commands
+      can! :use_quick_actions
     end
   end
 end
diff --git a/app/serializers/issuable_entity.rb b/app/serializers/issuable_entity.rb
index 65b204d4dd27bee4450f53147265a1dd9bb70fd0..bd5211b8e586e5cf4244c74fa4f59687b82f9efd 100644
--- a/app/serializers/issuable_entity.rb
+++ b/app/serializers/issuable_entity.rb
@@ -5,7 +5,6 @@ class IssuableEntity < Grape::Entity
   expose :description
   expose :lock_version
   expose :milestone_id
-  expose :position
   expose :state
   expose :title
   expose :updated_by_id
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index 769749c99252b5b5ae3dc60939793350a281bdb2..942145c4a8c36f10d5459c5838eb2b27ba036e88 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -67,8 +67,8 @@ module Ci
     def update_merge_requests_head_pipeline
       return unless pipeline.latest?
 
-      MergeRequest.where(source_project: @pipeline.project, source_branch: @pipeline.ref).
-        update_all(head_pipeline_id: @pipeline.id)
+      MergeRequest.where(source_project: @pipeline.project, source_branch: @pipeline.ref)
+        .update_all(head_pipeline_id: @pipeline.id)
     end
 
     def skip_ci?
diff --git a/app/services/ci/create_trigger_request_service.rb b/app/services/ci/create_trigger_request_service.rb
index beb27a5a5974a7b3e52be46d6f9d0dfba10cf886..cf3d4aee2bcff92681a12a24a6bb1aa8ed8383fe 100644
--- a/app/services/ci/create_trigger_request_service.rb
+++ b/app/services/ci/create_trigger_request_service.rb
@@ -3,8 +3,8 @@ module Ci
     def execute(project, trigger, ref, variables = nil)
       trigger_request = trigger.trigger_requests.create(variables: variables)
 
-      pipeline = Ci::CreatePipelineService.new(project, trigger.owner, ref: ref).
-        execute(:trigger, ignore_skip_ci: true, trigger_request: trigger_request)
+      pipeline = Ci::CreatePipelineService.new(project, trigger.owner, ref: ref)
+        .execute(:trigger, ignore_skip_ci: true, trigger_request: trigger_request)
 
       trigger_request if pipeline.persisted?
     end
diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb
index d6a4280ce4c514cd487843c8d4f173e2b2e3a456..af84d4c74278ced81ebbf295ffc308e2408436fd 100644
--- a/app/services/ci/register_job_service.rb
+++ b/app/services/ci/register_job_service.rb
@@ -54,15 +54,15 @@ module Ci
     def builds_for_shared_runner
       new_builds.
         # don't run projects which have not enabled shared runners and builds
-        joins(:project).where(projects: { shared_runners_enabled: true }).
-        joins('LEFT JOIN project_features ON ci_builds.project_id = project_features.project_id').
-        where('project_features.builds_access_level IS NULL or project_features.builds_access_level > 0').
+        joins(:project).where(projects: { shared_runners_enabled: true })
+        .joins('LEFT JOIN project_features ON ci_builds.project_id = project_features.project_id')
+        .where('project_features.builds_access_level IS NULL or project_features.builds_access_level > 0').
 
         # Implement fair scheduling
         # this returns builds that are ordered by number of running builds
         # we prefer projects that don't use shared runners at all
-        joins("LEFT JOIN (#{running_builds_for_shared_runners.to_sql}) AS project_builds ON ci_builds.project_id=project_builds.project_id").
-        order('COALESCE(project_builds.running_builds, 0) ASC', 'ci_builds.id ASC')
+        joins("LEFT JOIN (#{running_builds_for_shared_runners.to_sql}) AS project_builds ON ci_builds.project_id=project_builds.project_id")
+        .order('COALESCE(project_builds.running_builds, 0) ASC', 'ci_builds.id ASC')
     end
 
     def builds_for_specific_runner
@@ -70,8 +70,8 @@ module Ci
     end
 
     def running_builds_for_shared_runners
-      Ci::Build.running.where(runner: Ci::Runner.shared).
-        group(:project_id).select(:project_id, 'count(*) AS running_builds')
+      Ci::Build.running.where(runner: Ci::Runner.shared)
+        .group(:project_id).select(:project_id, 'count(*) AS running_builds')
     end
 
     def new_builds
diff --git a/app/services/concerns/issues/resolve_discussions.rb b/app/services/concerns/issues/resolve_discussions.rb
index 910a2a15e5d6e37556417ad3690605a5a0c7be53..7d45b4aa26a8b686546b55ecd2a5a54d332ac0c8 100644
--- a/app/services/concerns/issues/resolve_discussions.rb
+++ b/app/services/concerns/issues/resolve_discussions.rb
@@ -10,9 +10,9 @@ module Issues
     def merge_request_to_resolve_discussions_of
       return @merge_request_to_resolve_discussions_of if defined?(@merge_request_to_resolve_discussions_of)
 
-      @merge_request_to_resolve_discussions_of = MergeRequestsFinder.new(current_user, project_id: project.id).
-                                                     execute.
-                                                     find_by(iid: merge_request_to_resolve_discussions_of_iid)
+      @merge_request_to_resolve_discussions_of = MergeRequestsFinder.new(current_user, project_id: project.id)
+                                                     .execute
+                                                     .find_by(iid: merge_request_to_resolve_discussions_of_iid)
     end
 
     def discussions_to_resolve
diff --git a/app/services/create_deployment_service.rb b/app/services/create_deployment_service.rb
index 46823418bb02fb7a301e91776f5ba12273cf0ef3..63b85c3de7d612ff6e3478bca2dda5b3377a0a9d 100644
--- a/app/services/create_deployment_service.rb
+++ b/app/services/create_deployment_service.rb
@@ -2,7 +2,7 @@ class CreateDeploymentService
   attr_reader :job
 
   delegate :expanded_environment_name,
-           :environment_url,
+           :variables,
            :project,
            to: :job
 
@@ -14,7 +14,8 @@ class CreateDeploymentService
     return unless executable?
 
     ActiveRecord::Base.transaction do
-      environment.external_url = environment_url if environment_url
+      environment.external_url = expanded_environment_url if
+        expanded_environment_url
       environment.fire_state_event(action)
 
       return unless environment.save
@@ -49,6 +50,17 @@ class CreateDeploymentService
     @environment_options ||= job.options&.dig(:environment) || {}
   end
 
+  def expanded_environment_url
+    return @expanded_environment_url if defined?(@expanded_environment_url)
+
+    @expanded_environment_url =
+      ExpandVariables.expand(environment_url, variables) if environment_url
+  end
+
+  def environment_url
+    environment_options[:url]
+  end
+
   def on_stop
     environment_options[:on_stop]
   end
diff --git a/app/services/files/update_service.rb b/app/services/files/update_service.rb
index f23a9f6d57ce1dce82866ad07d6566dda326cf5e..bcca1386bedd0b21d64d85f9069d67db4624ef2f 100644
--- a/app/services/files/update_service.rb
+++ b/app/services/files/update_service.rb
@@ -28,8 +28,8 @@ module Files
     end
 
     def last_commit
-      @last_commit ||= Gitlab::Git::Commit.
-        last_for_path(@start_project.repository, @start_branch, @file_path)
+      @last_commit ||= Gitlab::Git::Commit
+        .last_for_path(@start_project.repository, @start_branch, @file_path)
     end
 
     def validate!
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index fb1d4aed58bcf05e32faa90a795f198963440013..20d1fb29289e39de0b1071ecd9a65d10a13ad89d 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -86,8 +86,8 @@ class GitPushService < BaseService
 
     push_commits.last(PROCESS_COMMIT_LIMIT).each do |commit|
       if commit.matches_cross_reference_regex?
-        ProcessCommitWorker.
-          perform_async(project.id, current_user.id, commit.to_hash, default)
+        ProcessCommitWorker
+          .perform_async(project.id, current_user.id, commit.to_hash, default)
       end
     end
   end
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index cd4d180824fc86113c604dec9648b20bc7bf2083..8dd0846f3bc4c07f2b47f0ee1c40cac13fadf081 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -142,10 +142,10 @@ class IssuableBaseService < BaseService
     LabelsFinder.new(current_user, project_id: @project.id).execute
   end
 
-  def merge_slash_commands_into_params!(issuable)
+  def merge_quick_actions_into_params!(issuable)
     description, command_params =
-      SlashCommands::InterpretService.new(project, current_user).
-        execute(params[:description], issuable)
+      QuickActions::InterpretService.new(project, current_user)
+        .execute(params[:description], issuable)
 
     # Avoid a description already set on an issuable to be overwritten by a nil
     params[:description] = description if params.key?(:description)
@@ -162,7 +162,7 @@ class IssuableBaseService < BaseService
   end
 
   def create(issuable)
-    merge_slash_commands_into_params!(issuable)
+    merge_quick_actions_into_params!(issuable)
     filter_params(issuable)
 
     params.delete(:state_event)
diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb
index 3cf4b82b9f2a29bdb5419ea2359d180a0d815d96..718a7ac1f2272145c5994501156bb74f26ee0619 100644
--- a/app/services/issues/create_service.rb
+++ b/app/services/issues/create_service.rb
@@ -30,8 +30,8 @@ module Issues
 
       Discussions::ResolveService.new(project, current_user,
                                       merge_request: merge_request_to_resolve_discussions_of,
-                                      follow_up_issue: issue).
-        execute(discussions_to_resolve)
+                                      follow_up_issue: issue)
+        .execute(discussions_to_resolve)
     end
 
     private
diff --git a/app/services/labels/promote_service.rb b/app/services/labels/promote_service.rb
index 76d0ba67b07af6616831f8e64f9369ed210c565f..43b539ded53745302012fde1aefeccad2cde57f7 100644
--- a/app/services/labels/promote_service.rb
+++ b/app/services/labels/promote_service.rb
@@ -26,29 +26,29 @@ module Labels
     private
 
     def label_ids_for_merge(new_label)
-      LabelsFinder.
-        new(current_user, title: new_label.title, group_id: project.group.id).
-        execute(skip_authorization: true).
-        where.not(id: new_label).
-        select(:id)  # Can't use pluck() to avoid object-creation because of the batching
+      LabelsFinder
+        .new(current_user, title: new_label.title, group_id: project.group.id)
+        .execute(skip_authorization: true)
+        .where.not(id: new_label)
+        .select(:id)  # Can't use pluck() to avoid object-creation because of the batching
     end
 
     def update_issuables(new_label, label_ids)
-      LabelLink.
-        where(label: label_ids).
-        update_all(label_id: new_label)
+      LabelLink
+        .where(label: label_ids)
+        .update_all(label_id: new_label)
     end
 
     def update_issue_board_lists(new_label, label_ids)
-      List.
-        where(label: label_ids).
-        update_all(label_id: new_label)
+      List
+        .where(label: label_ids)
+        .update_all(label_id: new_label)
     end
 
     def update_priorities(new_label, label_ids)
-      LabelPriority.
-        where(label: label_ids).
-        update_all(label_id: new_label)
+      LabelPriority
+        .where(label: label_ids)
+        .update_all(label_id: new_label)
     end
 
     def update_project_labels(label_ids)
diff --git a/app/services/labels/transfer_service.rb b/app/services/labels/transfer_service.rb
index 514679ed29d02d6450a60a1194901e9ddab2cf88..d2ece354efc71bd62c88661e87b3bac6ebde47c6 100644
--- a/app/services/labels/transfer_service.rb
+++ b/app/services/labels/transfer_service.rb
@@ -41,16 +41,16 @@ module Labels
     end
 
     def group_labels_applied_to_issues
-      Label.joins(:issues).
-        where(
+      Label.joins(:issues)
+        .where(
           issues: { project_id: project.id },
           labels: { type: 'GroupLabel', group_id: old_group.id }
         )
     end
 
     def group_labels_applied_to_merge_requests
-      Label.joins(:merge_requests).
-        where(
+      Label.joins(:merge_requests)
+        .where(
           merge_requests: { target_project_id: project.id },
           labels: { type: 'GroupLabel', group_id: old_group.id }
         )
@@ -64,15 +64,15 @@ module Labels
     end
 
     def update_label_links(labels, old_label_id:, new_label_id:)
-      LabelLink.joins(:label).
-        merge(labels).
-        where(label_id: old_label_id).
-        update_all(label_id: new_label_id)
+      LabelLink.joins(:label)
+        .merge(labels)
+        .where(label_id: old_label_id)
+        .update_all(label_id: new_label_id)
     end
 
     def update_label_priorities(old_label_id:, new_label_id:)
-      LabelPriority.where(project_id: project.id, label_id: old_label_id).
-        update_all(label_id: new_label_id)
+      LabelPriority.where(project_id: project.id, label_id: old_label_id)
+        .update_all(label_id: new_label_id)
     end
   end
 end
diff --git a/app/services/members/authorized_destroy_service.rb b/app/services/members/authorized_destroy_service.rb
index f846d72498ffcb78fe7e16430486a4cbd901d789..de3a252d6c6797d64e5d87e0530c8e4565d8eba2 100644
--- a/app/services/members/authorized_destroy_service.rb
+++ b/app/services/members/authorized_destroy_service.rb
@@ -26,30 +26,30 @@ module Members
 
     def unassign_issues_and_merge_requests(member)
       if member.is_a?(GroupMember)
-        issues = Issue.unscoped.select(1).
-                 joins(:project).
-                 where('issues.id = issue_assignees.issue_id AND projects.namespace_id = ?', member.source_id)
+        issues = Issue.unscoped.select(1)
+                 .joins(:project)
+                 .where('issues.id = issue_assignees.issue_id AND projects.namespace_id = ?', member.source_id)
 
         # DELETE FROM issue_assignees WHERE user_id = X AND EXISTS (...)
-        IssueAssignee.unscoped.
-          where('user_id = :user_id AND EXISTS (:sub)', user_id: member.user_id, sub: issues).
-          delete_all
+        IssueAssignee.unscoped
+          .where('user_id = :user_id AND EXISTS (:sub)', user_id: member.user_id, sub: issues)
+          .delete_all
 
-        MergeRequestsFinder.new(user, group_id: member.source_id, assignee_id: member.user_id).
-          execute.
-          update_all(assignee_id: nil)
+        MergeRequestsFinder.new(user, group_id: member.source_id, assignee_id: member.user_id)
+          .execute
+          .update_all(assignee_id: nil)
       else
         project = member.source
 
         # SELECT 1 FROM issues WHERE issues.id = issue_assignees.issue_id AND issues.project_id = X
-        issues = Issue.unscoped.select(1).
-                 where('issues.id = issue_assignees.issue_id').
-                 where(project_id: project.id)
+        issues = Issue.unscoped.select(1)
+                 .where('issues.id = issue_assignees.issue_id')
+                 .where(project_id: project.id)
 
         # DELETE FROM issue_assignees WHERE user_id = X AND EXISTS (...)
-        IssueAssignee.unscoped.
-          where('user_id = :user_id AND EXISTS (:sub)', user_id: member.user_id, sub: issues).
-          delete_all
+        IssueAssignee.unscoped
+          .where('user_id = :user_id AND EXISTS (:sub)', user_id: member.user_id, sub: issues)
+          .delete_all
 
         project.merge_requests.opened.assigned_to(member.user).update_all(assignee_id: nil)
       end
diff --git a/app/services/merge_requests/conflicts/resolve_service.rb b/app/services/merge_requests/conflicts/resolve_service.rb
index c2c335b8461f8bbdd850e727000dae372804b1e4..6b6e231f4f9224854a354f28f36cfc8b4172fff3 100644
--- a/app/services/merge_requests/conflicts/resolve_service.rb
+++ b/app/services/merge_requests/conflicts/resolve_service.rb
@@ -27,10 +27,10 @@ module MergeRequests
             tree: merge_index.write_tree(rugged)
           }
 
-          conflicts_for_resolution.
-            project.
-            repository.
-            resolve_conflicts(current_user, merge_request.source_branch, commit_params)
+          conflicts_for_resolution
+            .project
+            .repository
+            .resolve_conflicts(current_user, merge_request.source_branch, commit_params)
         end
       end
 
diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb
index fac3ac7a4c70d1ae08973a7181950acaf1f6ac31..b247cb89e5e7161d538c99f147bff8654620779f 100644
--- a/app/services/merge_requests/merge_service.rb
+++ b/app/services/merge_requests/merge_service.rb
@@ -61,8 +61,8 @@ module MergeRequests
       MergeRequests::PostMergeService.new(project, current_user).execute(merge_request)
 
       if params[:should_remove_source_branch].present? || @merge_request.force_remove_source_branch?
-        DeleteBranchService.new(@merge_request.source_project, branch_deletion_user).
-          execute(merge_request.source_branch)
+        DeleteBranchService.new(@merge_request.source_project, branch_deletion_user)
+          .execute(merge_request.source_branch)
       end
     end
 
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
index 81d217929d551dd1d41b9e8af0e8dd388251ccd3..e0e7c43f802a1b9b742122b4ae757eb894b60b10 100644
--- a/app/services/merge_requests/refresh_service.rb
+++ b/app/services/merge_requests/refresh_service.rb
@@ -43,9 +43,9 @@ module MergeRequests
       end
 
       filter_merge_requests(merge_requests).each do |merge_request|
-        MergeRequests::PostMergeService.
-          new(merge_request.target_project, @current_user).
-          execute(merge_request)
+        MergeRequests::PostMergeService
+          .new(merge_request.target_project, @current_user)
+          .execute(merge_request)
       end
     end
 
@@ -56,8 +56,8 @@ module MergeRequests
     # Refresh merge request diff if we push to source or target branch of merge request
     # Note: we should update merge requests from forks too
     def reload_merge_requests
-      merge_requests = @project.merge_requests.opened.
-        by_source_or_target_branch(@branch_name).to_a
+      merge_requests = @project.merge_requests.opened
+        .by_source_or_target_branch(@branch_name).to_a
 
       # Fork merge requests
       merge_requests += MergeRequest.opened
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index 5c843a258fb3b68f21bb4f81d1f77b8b438f83b7..75a65aecd1aa784539604a9d46b75cf31761ab66 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -7,7 +7,7 @@ module MergeRequests
       params.except!(:target_project_id)
       params.except!(:source_branch)
 
-      merge_from_slash_command(merge_request) if params[:merge]
+      merge_from_quick_action(merge_request) if params[:merge]
 
       if merge_request.closed_without_fork?
         params.except!(:target_branch, :force_remove_source_branch)
@@ -74,9 +74,9 @@ module MergeRequests
       end
     end
 
-    def merge_from_slash_command(merge_request)
+    def merge_from_quick_action(merge_request)
       last_diff_sha = params.delete(:merge)
-      return unless merge_request.mergeable_with_slash_command?(current_user, last_diff_sha: last_diff_sha)
+      return unless merge_request.mergeable_with_quick_action?(current_user, last_diff_sha: last_diff_sha)
 
       merge_request.update(merge_error: nil)
 
diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb
index f3954f6f8c4004102ea2604cc1704a82190dc603..06971483992ae642aeb2bd782714c0ab1c4b3f14 100644
--- a/app/services/notes/create_service.rb
+++ b/app/services/notes/create_service.rb
@@ -9,11 +9,11 @@ module Notes
       # We execute commands (extracted from `params[:note]`) on the noteable
       # **before** we save the note because if the note consists of commands
       # only, there is no need be create a note!
-      slash_commands_service = SlashCommandsService.new(project, current_user)
+      quick_actions_service = QuickActionsService.new(project, current_user)
 
-      if slash_commands_service.supported?(note)
+      if quick_actions_service.supported?(note)
         options = { merge_request_diff_head_sha: merge_request_diff_head_sha }
-        content, command_params = slash_commands_service.extract_commands(note, options)
+        content, command_params = quick_actions_service.extract_commands(note, options)
 
         only_commands = content.empty?
 
@@ -30,7 +30,7 @@ module Notes
       end
 
       if command_params.present?
-        slash_commands_service.execute(command_params, note)
+        quick_actions_service.execute(command_params, note)
 
         # We must add the error after we call #save because errors are reset
         # when #save is called
diff --git a/app/services/notes/slash_commands_service.rb b/app/services/notes/quick_actions_service.rb
similarity index 84%
rename from app/services/notes/slash_commands_service.rb
rename to app/services/notes/quick_actions_service.rb
index ad1e6f6774a174c6b84d28488747d801094ccc67..a8d0cc15527694c7cf3bc1b58b4a233e08a8d568 100644
--- a/app/services/notes/slash_commands_service.rb
+++ b/app/services/notes/quick_actions_service.rb
@@ -1,5 +1,5 @@
 module Notes
-  class SlashCommandsService < BaseService
+  class QuickActionsService < BaseService
     UPDATE_SERVICES = {
       'Issue' => Issues::UpdateService,
       'MergeRequest' => MergeRequests::UpdateService
@@ -22,8 +22,8 @@ module Notes
     def extract_commands(note, options = {})
       return [note.note, {}] unless supported?(note)
 
-      SlashCommands::InterpretService.new(project, current_user, options).
-        execute(note.note, note.noteable)
+      QuickActions::InterpretService.new(project, current_user, options)
+        .execute(note.note, note.noteable)
     end
 
     def execute(command_params, note)
diff --git a/app/services/preview_markdown_service.rb b/app/services/preview_markdown_service.rb
index 10d45bbf73c2090a7f99d6520aebc996e26b780c..4ee2c1796bd734b8218e964361dabe19a0733a71 100644
--- a/app/services/preview_markdown_service.rb
+++ b/app/services/preview_markdown_service.rb
@@ -1,6 +1,6 @@
 class PreviewMarkdownService < BaseService
   def execute
-    text, commands = explain_slash_commands(params[:text])
+    text, commands = explain_quick_actions(params[:text])
     users = find_user_references(text)
 
     success(
@@ -12,11 +12,11 @@ class PreviewMarkdownService < BaseService
 
   private
 
-  def explain_slash_commands(text)
+  def explain_quick_actions(text)
     return text, [] unless %w(Issue MergeRequest).include?(commands_target_type)
 
-    slash_commands_service = SlashCommands::InterpretService.new(project, current_user)
-    slash_commands_service.explain(text, find_commands_target)
+    quick_actions_service = QuickActions::InterpretService.new(project, current_user)
+    quick_actions_service.explain(text, find_commands_target)
   end
 
   def find_user_references(text)
@@ -36,10 +36,10 @@ class PreviewMarkdownService < BaseService
   end
 
   def commands_target_type
-    params[:slash_commands_target_type]
+    params[:quick_actions_target_type]
   end
 
   def commands_target_id
-    params[:slash_commands_target_id]
+    params[:quick_actions_target_id]
   end
 end
diff --git a/app/services/projects/autocomplete_service.rb b/app/services/projects/autocomplete_service.rb
index 015f282892117513f8999f2c0e27de40d8a46483..fc85f398935b9d1fe3aa10006297a416e9de29bf 100644
--- a/app/services/projects/autocomplete_service.rb
+++ b/app/services/projects/autocomplete_service.rb
@@ -32,7 +32,7 @@ module Projects
         issuable: noteable,
         current_user: current_user
       }
-      SlashCommands::InterpretService.command_definitions.map do |definition|
+      QuickActions::InterpretService.command_definitions.map do |definition|
         next unless definition.available?(opts)
 
         definition.to_h(opts)
diff --git a/app/services/projects/transfer_service.rb b/app/services/projects/transfer_service.rb
index 1c24b27a8704f0ba9ef11f6e701451902f944d2d..fd701e335240bfa7c692b44f32be3ab469d9ed61 100644
--- a/app/services/projects/transfer_service.rb
+++ b/app/services/projects/transfer_service.rb
@@ -12,87 +12,121 @@ module Projects
     TransferError = Class.new(StandardError)
 
     def execute(new_namespace)
-      if new_namespace.blank?
+      @new_namespace = new_namespace
+
+      if @new_namespace.blank?
         raise TransferError, 'Please select a new namespace for your project.'
       end
-      unless allowed_transfer?(current_user, project, new_namespace)
+
+      unless allowed_transfer?(current_user, project)
         raise TransferError, 'Transfer failed, please contact an admin.'
       end
-      transfer(project, new_namespace)
+
+      transfer(project)
+
+      true
     rescue Projects::TransferService::TransferError => ex
       project.reload
       project.errors.add(:new_namespace, ex.message)
       false
     end
 
-    def transfer(project, new_namespace)
-      old_namespace = project.namespace
+    private
 
-      Project.transaction do
-        old_path = project.path_with_namespace
-        old_group = project.group
-        new_path = File.join(new_namespace.try(:full_path) || '', project.path)
+    def transfer(project)
+      @old_path = project.path_with_namespace
+      @old_group = project.group
+      @new_path = File.join(@new_namespace.try(:full_path) || '', project.path)
+      @old_namespace = project.namespace
 
-        if Project.where(path: project.path, namespace_id: new_namespace.try(:id)).present?
-          raise TransferError.new("Project with same path in target namespace already exists")
-        end
+      if Project.where(path: project.path, namespace_id: @new_namespace.try(:id)).exists?
+        raise TransferError.new("Project with same path in target namespace already exists")
+      end
 
-        if project.has_container_registry_tags?
-          # we currently doesn't support renaming repository if it contains tags in container registry
-          raise TransferError.new('Project cannot be transferred, because tags are present in its container registry')
-        end
+      if project.has_container_registry_tags?
+        # We currently don't support renaming repository if it contains tags in container registry
+        raise TransferError.new('Project cannot be transferred, because tags are present in its container registry')
+      end
 
-        project.expire_caches_before_rename(old_path)
+      attempt_transfer_transaction
+    end
+
+    def attempt_transfer_transaction
+      Project.transaction do
+        project.expire_caches_before_rename(@old_path)
 
-        # Apply new namespace id and visibility level
-        project.namespace = new_namespace
-        project.visibility_level = new_namespace.visibility_level unless project.visibility_level_allowed_by_group?
-        project.save!
+        update_namespace_and_visibility(@new_namespace)
 
         # Notifications
-        project.send_move_instructions(old_path)
+        project.send_move_instructions(@old_path)
 
         # Move main repository
-        unless gitlab_shell.mv_repository(project.repository_storage_path, old_path, new_path)
+        unless move_repo_folder(@old_path, @new_path)
           raise TransferError.new('Cannot move project')
         end
 
         # Move wiki repo also if present
-        gitlab_shell.mv_repository(project.repository_storage_path, "#{old_path}.wiki", "#{new_path}.wiki")
+        move_repo_folder("#{@old_path}.wiki", "#{@new_path}.wiki")
 
         # Move missing group labels to project
-        Labels::TransferService.new(current_user, old_group, project).execute
+        Labels::TransferService.new(current_user, @old_group, project).execute
 
         # Move uploads
-        Gitlab::UploadsTransfer.new.move_project(project.path, old_namespace.full_path, new_namespace.full_path)
+        Gitlab::UploadsTransfer.new.move_project(project.path, @old_namespace.full_path, @new_namespace.full_path)
 
         # Move pages
-        Gitlab::PagesTransfer.new.move_project(project.path, old_namespace.full_path, new_namespace.full_path)
+        Gitlab::PagesTransfer.new.move_project(project.path, @old_namespace.full_path, @new_namespace.full_path)
 
-        project.old_path_with_namespace = old_path
+        project.old_path_with_namespace = @old_path
 
-        SystemHooksService.new.execute_hooks_for(project, :transfer)
+        execute_system_hooks
       end
-
-      refresh_permissions(old_namespace, new_namespace)
-
-      true
+    rescue Exception # rubocop:disable Lint/RescueException
+      rollback_side_effects
+      raise
+    ensure
+      refresh_permissions
     end
 
-    def allowed_transfer?(current_user, project, namespace)
-      namespace &&
+    def allowed_transfer?(current_user, project)
+      @new_namespace &&
         can?(current_user, :change_namespace, project) &&
-        namespace.id != project.namespace_id &&
-        current_user.can?(:create_projects, namespace)
+        @new_namespace.id != project.namespace_id &&
+        current_user.can?(:create_projects, @new_namespace)
     end
 
-    def refresh_permissions(old_namespace, new_namespace)
+    def update_namespace_and_visibility(to_namespace)
+      # Apply new namespace id and visibility level
+      project.namespace = to_namespace
+      project.visibility_level = to_namespace.visibility_level unless project.visibility_level_allowed_by_group?
+      project.save!
+    end
+
+    def refresh_permissions
       # This ensures we only schedule 1 job for every user that has access to
       # the namespaces.
-      user_ids = old_namespace.user_ids_for_project_authorizations |
-        new_namespace.user_ids_for_project_authorizations
+      user_ids = @old_namespace.user_ids_for_project_authorizations |
+        @new_namespace.user_ids_for_project_authorizations
 
       UserProjectAccessChangedService.new(user_ids).execute
     end
+
+    def rollback_side_effects
+      rollback_folder_move
+      update_namespace_and_visibility(@old_namespace)
+    end
+
+    def rollback_folder_move
+      move_repo_folder(@new_path, @old_path)
+      move_repo_folder("#{@new_path}.wiki", "#{@old_path}.wiki")
+    end
+
+    def move_repo_folder(from_name, to_name)
+      gitlab_shell.mv_repository(project.repository_storage_path, from_name, to_name)
+    end
+
+    def execute_system_hooks
+      SystemHooksService.new.execute_hooks_for(project, :transfer)
+    end
   end
 end
diff --git a/app/services/slash_commands/interpret_service.rb b/app/services/quick_actions/interpret_service.rb
similarity index 97%
rename from app/services/slash_commands/interpret_service.rb
rename to app/services/quick_actions/interpret_service.rb
index 83144b1e0111cd45149f8c698dbfb3fdd9fe81a0..6816b137361094b4ec6cd9add765bbf6013c98b9 100644
--- a/app/services/slash_commands/interpret_service.rb
+++ b/app/services/quick_actions/interpret_service.rb
@@ -1,13 +1,13 @@
-module SlashCommands
+module QuickActions
   class InterpretService < BaseService
-    include Gitlab::SlashCommands::Dsl
+    include Gitlab::QuickActions::Dsl
 
     attr_reader :issuable
 
     # Takes a text and interprets the commands that are extracted from it.
     # Returns the content without commands, and hash of changes to be applied to a record.
     def execute(content, issuable)
-      return [content, {}] unless current_user.can?(:use_slash_commands)
+      return [content, {}] unless current_user.can?(:use_quick_actions)
 
       @issuable = issuable
       @updates = {}
@@ -20,7 +20,7 @@ module SlashCommands
     # Takes a text and interprets the commands that are extracted from it.
     # Returns the content without commands, and array of changes explained.
     def explain(content, issuable)
-      return [content, []] unless current_user.can?(:use_slash_commands)
+      return [content, []] unless current_user.can?(:use_quick_actions)
 
       @issuable = issuable
 
@@ -32,7 +32,7 @@ module SlashCommands
     private
 
     def extractor
-      Gitlab::SlashCommands::Extractor.new(self.class.command_definitions)
+      Gitlab::QuickActions::Extractor.new(self.class.command_definitions)
     end
 
     desc do
@@ -71,7 +71,7 @@ module SlashCommands
       last_diff_sha = params && params[:merge_request_diff_head_sha]
       issuable.is_a?(MergeRequest) &&
         issuable.persisted? &&
-        issuable.mergeable_with_slash_command?(current_user, autocomplete_precheck: !last_diff_sha, last_diff_sha: last_diff_sha)
+        issuable.mergeable_with_quick_action?(current_user, autocomplete_precheck: !last_diff_sha, last_diff_sha: last_diff_sha)
     end
     command :merge do
       @updates[:merge] = params[:merge_request_diff_head_sha]
diff --git a/app/services/tags/create_service.rb b/app/services/tags/create_service.rb
index 1756da9e519a993b6d9b5bf95a11f0c425b84be3..674792f61389b85f63186cafe5f5164958d83834 100644
--- a/app/services/tags/create_service.rb
+++ b/app/services/tags/create_service.rb
@@ -19,8 +19,8 @@ module Tags
 
       if new_tag
         if release_description
-          CreateReleaseService.new(@project, @current_user).
-            execute(tag_name, release_description)
+          CreateReleaseService.new(@project, @current_user)
+            .execute(tag_name, release_description)
         end
 
         success.merge(tag: new_tag)
diff --git a/app/services/users/refresh_authorized_projects_service.rb b/app/services/users/refresh_authorized_projects_service.rb
index 3e07b8110277d0c0118da081ca21bf1ba0ec4661..f028f5eb0d460d881277d51a7a01c3ffb353da2c 100644
--- a/app/services/users/refresh_authorized_projects_service.rb
+++ b/app/services/users/refresh_authorized_projects_service.rb
@@ -34,7 +34,7 @@ module Users
         # Keep trying until we obtain the lease. If we don't do so we may end up
         # not updating the list of authorized projects properly. To prevent
         # hammering Redis too much we'll wait for a bit between retries.
-        sleep(1)
+        sleep(0.1)
       end
 
       begin
diff --git a/app/validators/dynamic_path_validator.rb b/app/validators/dynamic_path_validator.rb
index 27ac60637fd402d6879784be198aad1540f9f15d..4688aabc2a8656ed8ab39f0731db56e9ecfb912b 100644
--- a/app/validators/dynamic_path_validator.rb
+++ b/app/validators/dynamic_path_validator.rb
@@ -26,7 +26,7 @@ class DynamicPathValidator < ActiveModel::EachValidator
   end
 
   def path_valid_for_record?(record, value)
-    full_path = record.respond_to?(:full_path) ? record.full_path : value
+    full_path = record.respond_to?(:build_full_path) ? record.build_full_path : value
 
     return true unless full_path
 
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index 95dffdafabe27e431edc5e3703c487719382a47f..b21d56659706f57ed92f55f56a38a1476fe6de9f 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -325,6 +325,10 @@
           = f.label :prometheus_metrics_enabled do
             = f.check_box :prometheus_metrics_enabled
             Enable Prometheus Metrics
+            - unless Gitlab::Metrics.metrics_folder_present?
+              .help-block
+                %strong.cred WARNING:
+                Environment variable `prometheus_multiproc_dir` does not exist or is not pointing to a valid directory.
 
   %fieldset
     %legend Background Jobs
diff --git a/app/views/admin/broadcast_messages/_form.html.haml b/app/views/admin/broadcast_messages/_form.html.haml
index 2269fb1fd8c8c2eec344d9c2c4f10de68f6ef9d0..5a4ed1c3a2a02049167321f7a8ebf3e70022009e 100644
--- a/app/views/admin/broadcast_messages/_form.html.haml
+++ b/app/views/admin/broadcast_messages/_form.html.haml
@@ -21,11 +21,11 @@
   .form-group.js-toggle-colors-container.hide
     = f.label :color, "Background Color", class: 'control-label'
     .col-sm-10
-      = f.text_field :color, class: "form-control"
+      = f.color_field :color, class: "form-control"
   .form-group.js-toggle-colors-container.hide
     = f.label :font, "Font Color", class: 'control-label'
     .col-sm-10
-      = f.text_field :font, class: "form-control"
+      = f.color_field :font, class: "form-control"
   .form-group
     = f.label :starts_at, class: 'control-label'
     .col-sm-10.datetime-controls
diff --git a/app/views/notify/pipeline_failed_email.html.haml b/app/views/notify/pipeline_failed_email.html.haml
index a83faa839df44b7355591dd8ab0d46ed816b38f6..b7a609381324b3c7f94fb80230a41ba1ea0a67f9 100644
--- a/app/views/notify/pipeline_failed_email.html.haml
+++ b/app/views/notify/pipeline_failed_email.html.haml
@@ -60,7 +60,7 @@
               %tbody
                 %tr
                   %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
-                    %img.avatar{ height: "24", src: avatar_icon(commit.author || commit.author_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
+                    %img.avatar{ height: "24", src: avatar_icon(commit.author || commit.author_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
                   %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
                     - if commit.author
                       %a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" }
@@ -76,7 +76,7 @@
                 %tbody
                   %tr
                     %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
-                      %img.avatar{ height: "24", src: avatar_icon(commit.committer || commit.committer_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
+                      %img.avatar{ height: "24", src: avatar_icon(commit.committer || commit.committer_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
                     %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
                       - if commit.committer
                         %a.muted{ href: user_url(commit.committer), style: "color:#333333;text-decoration:none;" }
@@ -100,7 +100,7 @@
             triggered by
           - if @pipeline.user
             %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;padding-left:5px", width: "24" }
-              %img.avatar{ height: "24", src: avatar_icon(@pipeline.user, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
+              %img.avatar{ height: "24", src: avatar_icon(@pipeline.user, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
             %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;font-weight:500;line-height:1.4;vertical-align:baseline;" }
               %a.muted{ href: user_url(@pipeline.user), style: "color:#333333;text-decoration:none;" }
                 = @pipeline.user.name
diff --git a/app/views/notify/pipeline_success_email.html.haml b/app/views/notify/pipeline_success_email.html.haml
index 9c2e2a599b249ceedf68ed536c825f05c904f9e5..3f16885b8e32fef2ef8b5480c862b9c26b7e623c 100644
--- a/app/views/notify/pipeline_success_email.html.haml
+++ b/app/views/notify/pipeline_success_email.html.haml
@@ -60,7 +60,7 @@
               %tbody
                 %tr
                   %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
-                    %img.avatar{ height: "24", src: avatar_icon(commit.author || commit.author_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
+                    %img.avatar{ height: "24", src: avatar_icon(commit.author || commit.author_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
                   %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
                     - if commit.author
                       %a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" }
@@ -76,7 +76,7 @@
                 %tbody
                   %tr
                     %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
-                      %img.avatar{ height: "24", src: avatar_icon(commit.committer || commit.committer_email, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
+                      %img.avatar{ height: "24", src: avatar_icon(commit.committer || commit.committer_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
                     %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
                       - if commit.committer
                         %a.muted{ href: user_url(commit.committer), style: "color:#333333;text-decoration:none;" }
@@ -100,7 +100,7 @@
             triggered by
           - if @pipeline.user
             %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;padding-left:5px", width: "24" }
-              %img.avatar{ height: "24", src: avatar_icon(@pipeline.user, 24), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
+              %img.avatar{ height: "24", src: avatar_icon(@pipeline.user, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "Avatar" }/
             %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;font-weight:500;line-height:1.4;vertical-align:baseline;" }
               %a.muted{ href: user_url(@pipeline.user), style: "color:#333333;text-decoration:none;" }
                 = @pipeline.user.name
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index fcfd350f0da1acef09062f5f3bf57bfcb48f4ac5..15672289c650d67adc4e919ae2fd11db90a7a20d 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -42,10 +42,17 @@
         - if current_user.ldap_user?
           Some options are unavailable for LDAP accounts
     .col-lg-9
-      .form-group
-        = f.label :name, class: "label-light"
-        = f.text_field :name, class: "form-control", required: true
-        %span.help-block Enter your name, so people you know can recognize you.
+      .row
+        .form-group.col-md-9
+          = f.label :name, class: "label-light"
+          = f.text_field :name, class: "form-control", required: true
+          %span.help-block Enter your name, so people you know can recognize you.
+
+        .form-group.col-md-3
+          = f.label :id, class: 'label-light' do
+            User ID
+          = f.text_field :id, class: 'form-control', readonly: true
+
 
       .form-group
         = f.label :email, class: "label-light"
diff --git a/app/views/projects/_find_file_link.html.haml b/app/views/projects/_find_file_link.html.haml
index c748ccf65e6593cd8a40bf0d377dd3a67f774cbb..cb4d2bbacf541823b289535e9a9afa7be071de6b 100644
--- a/app/views/projects/_find_file_link.html.haml
+++ b/app/views/projects/_find_file_link.html.haml
@@ -1,3 +1,3 @@
-= link_to namespace_project_find_file_path(@project.namespace, @project, @ref), class: 'btn btn-grouped shortcuts-find-file', rel: 'nofollow' do
+= link_to namespace_project_find_file_path(@project.namespace, @project, @ref), class: 'btn shortcuts-find-file', rel: 'nofollow' do
   = icon('search')
   %span= _('Find file')
diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml
index 07445434cf3f579c822351e782f194c0416bb970..d0698285f84919758a38ec5299518a347fdcf704 100644
--- a/app/views/projects/_md_preview.html.haml
+++ b/app/views/projects/_md_preview.html.haml
@@ -9,6 +9,12 @@
       %li
         %a.js-md-preview-button{ href: "#md-preview-holder", tabindex: -1 }
           Preview
+
+      - if defined?(@issue) && @issue.confidential?
+        %li.confidential-issue-warning
+          = icon('warning')
+          %span This is a confidential issue. Your comment will not be visible to the public.
+
       %li.pull-right
         .toolbar-group
           = markdown_toolbar_button({ icon: "bold fw", data: { "md-tag" => "**" }, title: "Add bold text" })
diff --git a/app/views/projects/_visibility_select.html.haml b/app/views/projects/_visibility_select.html.haml
index 65fc0a36ca9e33923e360d2dfbda6ab4f0c3a4fc..4026b9e3c46d6f1e0b6b8a9b5305ed80959013e2 100644
--- a/app/views/projects/_visibility_select.html.haml
+++ b/app/views/projects/_visibility_select.html.haml
@@ -1,5 +1,7 @@
 - if can_change_visibility_level?(@project, current_user)
-  = form.select(model_method, visibility_select_options(@project, selected_level), {}, class: 'form-control visibility-select')
+  .select-wrapper
+    = form.select(model_method, visibility_select_options(@project, selected_level), {}, class: 'form-control visibility-select select-control')
+    = icon('chevron-down')
 - else
   .info.js-locked{ data: { help_block: visibility_level_description(@project.visibility_level, @project) } }
     = visibility_level_icon(@project.visibility_level)
diff --git a/app/views/projects/_zen.html.haml b/app/views/projects/_zen.html.haml
index 3b3d08ddd3c90477608280f2cbf0864fb32a245e..afc40ca4eabd06699f0ead75fd2ce52217e9013e 100644
--- a/app/views/projects/_zen.html.haml
+++ b/app/views/projects/_zen.html.haml
@@ -1,10 +1,15 @@
 - @gfm_form = true
 - current_text ||= nil
-- supports_slash_commands = local_assigns.fetch(:supports_slash_commands, false)
+- supports_autocomplete = local_assigns.fetch(:supports_autocomplete, true)
+- supports_quick_actions = local_assigns.fetch(:supports_quick_actions, false)
 .zen-backdrop
   - classes << ' js-gfm-input js-autosize markdown-area'
   - if defined?(f) && f
-    = f.text_area attr, class: classes, placeholder: placeholder, data: { supports_slash_commands: supports_slash_commands }
+    = f.text_area attr,
+      class: classes,
+      placeholder: placeholder,
+      data: { supports_quick_actions: supports_quick_actions,
+        supports_autocomplete: supports_autocomplete }
   - else
     = text_area_tag attr, current_text, class: classes, placeholder: placeholder
   %a.zen-control.zen-control-leave.js-zen-leave{ href: "#" }
diff --git a/app/views/projects/blob/_breadcrumb.html.haml b/app/views/projects/blob/_breadcrumb.html.haml
index 0ad9f258e4849e00123fca383a3dec04d02dd15b..5840e9863f44340eba13c2e481d053601e170b9e 100644
--- a/app/views/projects/blob/_breadcrumb.html.haml
+++ b/app/views/projects/blob/_breadcrumb.html.haml
@@ -1,9 +1,26 @@
 - blame = local_assigns.fetch(:blame, false)
 .nav-block
+  .tree-ref-container
+    .tree-ref-holder
+      = render 'shared/ref_switcher', destination: 'blob', path: @path
+
+    %ul.breadcrumb.repo-breadcrumb
+      %li
+        = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
+          = @project.path
+      - path_breadcrumbs do |title, path|
+        - title = truncate(title, length: 40)
+        %li
+          - if path == @path
+            = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@ref, path)) do
+              %strong= title
+          - else
+            = link_to title, namespace_project_tree_path(@project.namespace, @project, tree_join(@ref, path))
+
   .tree-controls
     = render 'projects/find_file_link'
 
-    .btn-group.prepend-left-10{ role: "group" }<
+    .btn-group{ role: "group" }<
       -# only show normal/blame view links for text files
       - if blob.readable_text?
         - if blame
@@ -18,19 +35,3 @@
 
       = link_to 'Permalink', namespace_project_blob_path(@project.namespace, @project,
           tree_join(@commit.sha, @path)), class: 'btn js-data-file-blob-permalink-url'
-
-  .tree-ref-holder
-    = render 'shared/ref_switcher', destination: 'blob', path: @path
-
-  %ul.breadcrumb.repo-breadcrumb
-    %li
-      = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
-        = @project.path
-    - path_breadcrumbs do |title, path|
-      - title = truncate(title, length: 40)
-      %li
-        - if path == @path
-          = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@ref, path)) do
-            %strong= title
-        - else
-          = link_to title, namespace_project_tree_path(@project.namespace, @project, tree_join(@ref, path))
diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml
index e14885f264b615b661759cafd4e25d14242f36dc..32dbc1b3417665dfa322f54557aebcb8049fecfe 100644
--- a/app/views/projects/blob/_upload.html.haml
+++ b/app/views/projects/blob/_upload.html.haml
@@ -9,8 +9,10 @@
           .dropzone
             .dropzone-previews.blob-upload-dropzone-previews
               %p.dz-message.light
-                Attach a file by drag &amp; drop or
-                = link_to 'click to upload', '#', class: "markdown-selector"
+                - upload_link = link_to s_('UploadLink|click to upload'), '#', class: "markdown-selector"
+                - dropzone_text = _('Attach a file by drag &amp; drop or %{upload_link}') % { upload_link: upload_link }
+                #{ dropzone_text.html_safe }
+
           %br
           .dropzone-alerts.alert.alert-danger.data{ style: "display:none" }
 
@@ -18,7 +20,7 @@
 
           .form-actions
             = button_tag button_title, class: 'btn btn-small btn-create btn-upload-file', id: 'submit-all'
-            = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
+            = link_to _("Cancel"), '#', class: "btn btn-cancel", "data-dismiss" => "modal"
 
             - unless can?(current_user, :push_code, @project)
               .inline.prepend-left-10
diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml
index 3cf91bf07f79975438a8f2ecf59b597986245b72..a73ddd5eb33183f3939ce33c67ac1a4150ea1533 100644
--- a/app/views/projects/buttons/_download.html.haml
+++ b/app/views/projects/buttons/_download.html.haml
@@ -2,7 +2,7 @@
 
 - if !project.empty_repo? && can?(current_user, :download_code, project)
   .project-action-button.dropdown.inline>
-    %button.btn.has-tooltip{ title: 'Download', 'data-toggle' => 'dropdown', 'aria-label' => s_('DownloadSource|Download') }
+    %button.btn.has-tooltip{ title: s_('DownloadSource|Download'), 'data-toggle' => 'dropdown', 'aria-label' => s_('DownloadSource|Download') }
       = icon('download')
       = icon("caret-down")
       %span.sr-only=  _('Select Archive Format')
diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml
index 312c349da3abd88af67147e3b149dd946d65d59c..960b57a80082ef247cfb7e5bd6a923fb19634487 100644
--- a/app/views/projects/buttons/_dropdown.html.haml
+++ b/app/views/projects/buttons/_dropdown.html.haml
@@ -1,6 +1,6 @@
 - if current_user
   .project-action-button.dropdown.inline
-    %a.btn.dropdown-toggle.has-tooltip{ href: '#', title: 'Create new...', 'data-toggle' => 'dropdown', 'data-container' => 'body', 'aria-label' => 'Create new...' }
+    %a.btn.dropdown-toggle.has-tooltip{ href: '#', title: _('Create new...'), 'data-toggle' => 'dropdown', 'data-container' => 'body', 'aria-label' => _('Create new...') }
       = icon('plus')
       = icon("caret-down")
     %ul.dropdown-menu.dropdown-menu-align-right.project-home-dropdown
diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml
index 7a08bb3749420ed48b197e09322727cfd073d1b5..42f8c75f57b0b3775023f26a4c82ffd8ec76ca2e 100644
--- a/app/views/projects/buttons/_fork.html.haml
+++ b/app/views/projects/buttons/_fork.html.haml
@@ -4,11 +4,15 @@
       = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: _('Go to your fork'), class: 'btn has-tooltip' do
         = custom_icon('icon_fork')
         %span= s_('GoToYourFork|Fork')
+    - elsif !current_user.can_create_project?
+      = link_to new_namespace_project_fork_path(@project.namespace, @project), title: _('You have reached your project limit'), class: 'btn has-tooltip disabled' do
+        = custom_icon('icon_fork')
+        %span= s_('CreateNewFork|Fork')
     - else
       = link_to new_namespace_project_fork_path(@project.namespace, @project), class: 'btn' do
         = custom_icon('icon_fork')
         %span= s_('CreateNewFork|Fork')
     .count-with-arrow
       %span.arrow
-      = link_to namespace_project_forks_path(@project.namespace, @project), title: n_('Forks', @project.forks_count), class: 'count' do
+      = link_to namespace_project_forks_path(@project.namespace, @project), title: n_('Fork', 'Forks', @project.forks_count), class: 'count' do
         = @project.forks_count
diff --git a/app/views/projects/commit/_change.html.haml b/app/views/projects/commit/_change.html.haml
index 281d823da52abc1d302398e81908f30ccc1a42fd..2267f123e3833cfcbeaacdb8733699d9837a7d9b 100644
--- a/app/views/projects/commit/_change.html.haml
+++ b/app/views/projects/commit/_change.html.haml
@@ -1,35 +1,36 @@
 - case type.to_s
 - when 'revert'
-  - label = 'Revert'
-  - branch_label = 'Revert in branch'
+  - label = s_('ChangeTypeAction|Revert')
+  - branch_label = s_('ChangeTypeActionLabel|Revert in branch')
+  - revert_merge_request = _('Revert this merge request')
+  - revert_commit = _('Revert this commit')
+  - title = commit.merged_merge_request(current_user) ? revert_merge_request : revert_commit
 - when 'cherry-pick'
-  - label = 'Cherry-pick'
-  - branch_label = 'Pick into branch'
+  - label = s_('ChangeTypeAction|Cherry-pick')
+  - branch_label = s_('ChangeTypeActionLabel|Pick into branch')
+  - title = commit.merged_merge_request(current_user) ? _('Cherry-pick this merge request') : _('Cherry-pick this commit')
 
 .modal{ id: "modal-#{type}-commit" }
   .modal-dialog
     .modal-content
       .modal-header
         %a.close{ href: "#", "data-dismiss" => "modal" } ×
-        %h3.page-title== #{label} this #{commit.change_type_title(current_user)}
+        %h3.page-title= title
       .modal-body
         = form_tag [type.underscore, @project.namespace.becomes(Namespace), @project, commit], method: :post, remote: false, class: "form-horizontal js-#{type}-form js-requires-input" do
           .form-group.branch
             = label_tag 'start_branch', branch_label, class: 'control-label'
             .col-sm-10
               = hidden_field_tag :start_branch, @project.default_branch, id: 'start_branch'
-              = dropdown_tag(@project.default_branch, options: { title: "Switch branch", filter: true, placeholder: "Search branches", toggle_class: 'js-project-refs-dropdown dynamic', dropdown_class: 'dropdown-menu-selectable', data: { field_name: "start_branch", selected: @project.default_branch, start_branch: @project.default_branch, refs_url: namespace_project_branches_path(@project.namespace, @project), submit_form_on_click: false } })
+              = dropdown_tag(@project.default_branch, options: { title: s_("BranchSwitcherTitle|Switch branch"), filter: true, placeholder: s_("BranchSwitcherPlaceholder|Search branches"), toggle_class: 'js-project-refs-dropdown dynamic', dropdown_class: 'dropdown-menu-selectable', data: { field_name: "start_branch", selected: @project.default_branch, start_branch: @project.default_branch, refs_url: namespace_project_branches_path(@project.namespace, @project), submit_form_on_click: false } })
 
               - if can?(current_user, :push_code, @project)
-                .checkbox
-                  = label_tag do
-                    = check_box_tag 'create_merge_request', 1, true, class: 'js-create-merge-request', id: nil
-                    Start a <strong>new merge request</strong> with these changes
+                = render 'shared/new_merge_request_checkbox'
               - else
                 = hidden_field_tag 'create_merge_request', 1, id: nil
           .form-actions
             = submit_tag label, class: 'btn btn-create'
-            = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
+            = link_to _("Cancel"), '#', class: "btn btn-cancel", "data-dismiss" => "modal"
 
             - unless can?(current_user, :push_code, @project)
               .inline.prepend-left-10
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index aab503102341cb48105d13418d16e50ec15f8418..7fe44816bae9d775e5cc7df7f2ce66ec7924fc6f 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -1,17 +1,17 @@
 .page-content-header
   .header-main-content
     %strong
-      Commit
+      #{ s_('CommitBoxTitle|Commit') }
       %span.commit-sha= @commit.short_id
-    = clipboard_button(text: @commit.id, title: "Copy commit SHA to clipboard")
+    = clipboard_button(text: @commit.id, title: _("Copy commit SHA to clipboard"))
     %span.hidden-xs authored
     #{time_ago_with_tooltip(@commit.authored_date)}
-    %span by
+    %span= s_('ByAuthor|by')
     = author_avatar(@commit, size: 24)
     %strong
       = commit_author_link(@commit, avatar: true, size: 24)
     - if @commit.different_committer?
-      %span.light Committed by
+      %span.light= _('Committed by')
       %strong
         = commit_committer_link(@commit, avatar: true, size: 24)
       #{time_ago_with_tooltip(@commit.committed_date)}
@@ -22,15 +22,15 @@
         = icon('comment')
         = @notes_count
     = link_to namespace_project_tree_path(@project.namespace, @project, @commit), class: "btn btn-default append-right-10 hidden-xs hidden-sm" do
-      Browse files
+      #{ _('Browse files') }
     .dropdown.inline
       %a.btn.btn-default.dropdown-toggle{ data: { toggle: "dropdown" } }
-        %span Options
+        %span= _('Options')
         = icon('caret-down')
       %ul.dropdown-menu.dropdown-menu-align-right
         %li.visible-xs-block.visible-sm-block
           = link_to namespace_project_tree_path(@project.namespace, @project, @commit) do
-            Browse Files
+            _('Browse Files')
         - unless @commit.has_been_reverted?(current_user)
           %li.clearfix
             = revert_commit_link(@commit, namespace_project_commit_path(@project.namespace, @project, @commit.id), has_tooltip: false)
@@ -38,13 +38,13 @@
           = cherry_pick_commit_link(@commit, namespace_project_commit_path(@project.namespace, @project, @commit.id), has_tooltip: false)
         - if can_collaborate_with_project?
           %li.clearfix
-            = link_to "Tag", new_namespace_project_tag_path(@project.namespace, @project, ref: @commit)
+            = link_to s_("CreateTag|Tag"), new_namespace_project_tag_path(@project.namespace, @project, ref: @commit)
         %li.divider
         %li.dropdown-header
-          Download
+          #{ _('Download') }
         - unless @commit.parents.length > 1
-          %li= link_to "Email Patches", namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch)
-        %li= link_to "Plain Diff",    namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff)
+          %li= link_to s_("DownloadCommit|Email Patches"), namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch)
+        %li= link_to s_("DownloadCommit|Plain Diff"),    namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff)
 
 .commit-box
   %h3.commit-title
@@ -57,7 +57,7 @@
   .well-segment.branch-info
     .icon-container.commit-icon
       = custom_icon("icon_commit")
-    %span.cgray= pluralize(@commit.parents.count, "parent")
+    %span.cgray= n_('parent', 'parents', @commit.parents.count)
     - @commit.parents.each do |parent|
       = link_to parent.short_id, namespace_project_commit_path(@project.namespace, @project, parent), class: "commit-sha"
     %span.commit-info.branches
@@ -69,11 +69,11 @@
       .status-icon-container{ class: "ci-status-icon-#{@commit.status}" }
         = link_to namespace_project_pipeline_path(@project.namespace, @project, last_pipeline.id) do
           = ci_icon_for_status(last_pipeline.status)
-      Pipeline
+      #{ _('Pipeline') }
       = link_to "##{last_pipeline.id}", namespace_project_pipeline_path(@project.namespace, @project, last_pipeline.id)
       = ci_label_for_status(last_pipeline.status)
       - if last_pipeline.stages_count.nonzero?
-        with #{"stage".pluralize(last_pipeline.stages_count)}
+        #{ n_(s_('Pipeline|with stage'), s_('Pipeline|with stages'), last_pipeline.stages_count) }
         .mr-widget-pipeline-graph
           = render 'shared/mini_pipeline_graph', pipeline: last_pipeline, klass: 'js-commit-pipeline-graph'
       in
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 7a03c3561af070040f947d6ee5365f58abcf581b..11de6915961364430ba03d7ce566368c1a592a45 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -30,9 +30,11 @@
           %pre.commit-row-description.js-toggle-content
             = preserve(markdown(commit.description, pipeline: :single_line, author: commit.author))
         .commiter
-          = commit_author_link(commit, avatar: false, size: 24)
-          #{ _('committed') }
-          #{time_ago_with_tooltip(commit.committed_date)}
+          - commit_author_link = commit_author_link(commit, avatar: false, size: 24)
+          - commit_timeago = time_ago_with_tooltip(commit.committed_date)
+          - commit_text =  _('%{commit_author_link} committed %{commit_timeago}') % { commit_author_link: commit_author_link, commit_timeago: commit_timeago }
+          #{ commit_text.html_safe }
+
 
       .commit-actions.flex-row.hidden-xs
         - if commit.status(ref)
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index c3dab68cea57c09254a0627bd34b6522db0ce712..296e37e20e62ba168b07ea6572d63857084c725c 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -99,9 +99,9 @@
                   Git Large File Storage
                   = link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
               .col-md-3
-                = f.select :lfs_enabled, [%w(Enabled true), %w(Disabled false)], {}, selected: @project.lfs_enabled?, class: 'pull-right form-control project-repo-select', data: { field: 'lfs_enabled' }
-
-
+                .select-wrapper
+                  = f.select :lfs_enabled, [%w(Enabled true), %w(Disabled false)], {}, selected: @project.lfs_enabled?, class: 'pull-right form-control project-repo-select select-control', data: { field: 'lfs_enabled' }
+                  = icon('chevron-down')
         - if Gitlab.config.registry.enabled
           .form-group.js-container-registry{ style: ("display: none;" if @project.project_feature.send(:repository_access_level) == 0) }
             .checkbox
diff --git a/app/views/projects/issues/_issue_by_email.html.haml b/app/views/projects/issues/_issue_by_email.html.haml
index da65157a10b71e979a359e38b11637d29f897b3d..35b7d1b920cd3a7ef14430d5fcbd20340874ff4d 100644
--- a/app/views/projects/issues/_issue_by_email.html.haml
+++ b/app/views/projects/issues/_issue_by_email.html.haml
@@ -20,7 +20,7 @@
         %p
           The subject will be used as the title of the new issue, and the message will be the description.
 
-          = link_to 'Slash commands', help_page_path('user/project/slash_commands'), target: '_blank', tabindex: -1
+          = link_to 'Quick actions', help_page_path('user/project/quick_actions'), target: '_blank', tabindex: -1
           and styling with
           = link_to 'Markdown', help_page_path('user/markdown'), target: '_blank', tabindex: -1
           are supported.
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index 5f92d020eef08820e43a2764bd0d6c3a5191c3be..d909b0bfbbd22184faf593aa05c68a5268146dfb 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -5,13 +5,6 @@
 - can_update_issue = can?(current_user, :update_issue, @issue)
 - can_report_spam = @issue.submittable_as_spam_by?(current_user)
 
-- if defined?(@issue) && @issue.confidential?
-  .confidential-issue-warning{ data: { spy: 'affix' } }
-    %span.confidential-issue-text
-      #{confidential_icon(@issue)} This issue is confidential.
-      %a{ href: help_page_path('user/project/issues/confidential_issues'), target: '_blank' }
-        What are confidential issues?
-
 .clearfix.detail-page-header
   .issuable-header
     .issuable-status-box.status-box.status-box-closed{ class: issue_button_visibility(@issue, false) }
@@ -26,6 +19,7 @@
       = icon('angle-double-left')
 
     .issuable-meta
+      = confidential_icon(@issue)
       = issuable_meta(@issue, @project, "Issue")
 
   .issuable-actions
diff --git a/app/views/projects/merge_requests/conflicts/_submit_form.html.haml b/app/views/projects/merge_requests/conflicts/_submit_form.html.haml
index 62c9748c5102257b8a6f8eca4c256531a467fb5c..e675e1830d0936d85d7124eee5c74e8958205ef7 100644
--- a/app/views/projects/merge_requests/conflicts/_submit_form.html.haml
+++ b/app/views/projects/merge_requests/conflicts/_submit_form.html.haml
@@ -1,7 +1,7 @@
 .form-horizontal.resolve-conflicts-form
   .form-group
     %label.col-sm-2.control-label{ "for" => "commit-message" }
-      Commit message
+      #{ _('Commit message') }
     .col-sm-10
       .commit-message-container
         .max-width-marker
diff --git a/app/views/projects/notes/_more_actions_dropdown.html.haml b/app/views/projects/notes/_more_actions_dropdown.html.haml
index e0d45054854c905e98060ff4d1167258eed05433..75a4687e1e3ddeb10a192fdc34298bc0f5b42c3f 100644
--- a/app/views/projects/notes/_more_actions_dropdown.html.haml
+++ b/app/views/projects/notes/_more_actions_dropdown.html.haml
@@ -1,14 +1,19 @@
-.dropdown.more-actions
-  = button_tag title: 'More actions', class: 'note-action-button more-actions-toggle has-tooltip btn btn-transparent', data: { toggle: 'dropdown', container: 'body' } do
-    = icon('ellipsis-v', class: 'icon')
-  %ul.dropdown-menu.more-actions-dropdown.dropdown-open-left
-    %li
-      = button_tag 'Edit comment', class: 'js-note-edit btn btn-transparent'
-    %li.divider
-      %li
-        = link_to new_abuse_report_path(user_id: note.author.id, ref_url: noteable_note_url(note)) do
-          Report as abuse
-    - if note_editable
-      %li
-        = link_to note_url(note), method: :delete, data: { confirm: 'Are you sure you want to delete this comment?' }, remote: true, class: 'js-note-delete' do
-          %span.text-danger Delete comment
+- is_current_user = current_user == note.author
+
+- if note_editable || !is_current_user
+  .dropdown.more-actions
+    = button_tag title: 'More actions', class: 'note-action-button more-actions-toggle has-tooltip btn btn-transparent', data: { toggle: 'dropdown', container: 'body' } do
+      = icon('ellipsis-v', class: 'icon')
+    %ul.dropdown-menu.more-actions-dropdown.dropdown-open-left
+      - if note_editable
+        %li
+          = button_tag 'Edit comment', class: 'js-note-edit btn btn-transparent'
+        %li.divider
+      - unless is_current_user
+        %li
+          = link_to new_abuse_report_path(user_id: note.author.id, ref_url: noteable_note_url(note)) do
+            Report as abuse
+      - if note_editable
+        %li
+          = link_to note_url(note), method: :delete, data: { confirm: 'Are you sure you want to delete this comment?' }, remote: true, class: 'js-note-delete' do
+            %span.text-danger Delete comment
diff --git a/app/views/projects/pipeline_schedules/_form.html.haml b/app/views/projects/pipeline_schedules/_form.html.haml
index e8dedf2620645e36ff7e8057aa94589a693e6e70..fc7fa5c1876792b560e968dc7dd8415766557476 100644
--- a/app/views/projects/pipeline_schedules/_form.html.haml
+++ b/app/views/projects/pipeline_schedules/_form.html.haml
@@ -7,7 +7,7 @@
   .form-group
     .col-md-9
       = f.label :description, _('Description'), class: 'label-light'
-      = f.text_field :description, class: 'form-control', required: true, autofocus: true, placeholder: _('PipelineSchedules|Provide a short description for this pipeline')
+      = f.text_field :description, class: 'form-control', required: true, autofocus: true, placeholder: s_('PipelineSchedules|Provide a short description for this pipeline')
   .form-group
     .col-md-9
       = f.label :cron, _('Interval Pattern'), class: 'label-light'
@@ -15,19 +15,19 @@
   .form-group
     .col-md-9
       = f.label :cron_timezone, _('Cron Timezone'), class: 'label-light'
-      = dropdown_tag(_("Select a timezone"), options: { toggle_class: 'btn js-timezone-dropdown', title: _("Select a timezone"), filter: true, placeholder: _("Filter"), data: { data: timezone_data } } )
+      = dropdown_tag(_("Select a timezone"), options: { toggle_class: 'btn js-timezone-dropdown', title: _("Select a timezone"), filter: true, placeholder: s_("OfSearchInADropdown|Filter"), data: { data: timezone_data } } )
       = f.text_field :cron_timezone, value: @schedule.cron_timezone, id: 'schedule_cron_timezone', class: 'hidden', name: 'schedule[cron_timezone]', required: true
   .form-group
     .col-md-9
       = f.label :ref, _('Target Branch'), class: 'label-light'
-      = dropdown_tag(_("Select target branch"), options: { toggle_class: 'btn js-target-branch-dropdown', dropdown_class: 'git-revision-dropdown', title: _("Select target branch"), filter: true, placeholder: _("Filter"), data: { data: @project.repository.branch_names, default_branch: @project.default_branch } } )
+      = dropdown_tag(_("Select target branch"), options: { toggle_class: 'btn js-target-branch-dropdown', dropdown_class: 'git-revision-dropdown', title: _("Select target branch"), filter: true, placeholder: s_("OfSearchInADropdown|Filter"), data: { data: @project.repository.branch_names, default_branch: @project.default_branch } } )
       = f.text_field :ref, value: @schedule.ref, id: 'schedule_ref', class: 'hidden', name: 'schedule[ref]', required: true
   .form-group
     .col-md-9
-      = f.label  :active, _('PipelineSchedules|Activated'), class: 'label-light'
+      = f.label  :active, s_('PipelineSchedules|Activated'), class: 'label-light'
       %div
         = f.check_box :active, required: false, value: @schedule.active?
-        Active
+        = _('Active')
   .footer-block.row-content-block
     = f.submit _('Save pipeline schedule'), class: 'btn btn-create', tabindex: 3
     = link_to _('Cancel'), pipeline_schedules_path(@project), class: 'btn btn-cancel'
diff --git a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml
index 2d3344a4aaf349872bdac0bd687fdf0d838319cd..966d6cd8495e546d80d456de6cf703ed56114ca1 100644
--- a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml
+++ b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml
@@ -13,12 +13,12 @@
             = ci_icon_for_status(pipeline_schedule.last_pipeline.status)
             %span ##{pipeline_schedule.last_pipeline.id}
       - else
-        = _("PipelineSchedules|None")
+        = s_("PipelineSchedules|None")
     %td.next-run-cell
       - if pipeline_schedule.active?
         = time_ago_with_tooltip(pipeline_schedule.real_next_run)
       - else
-        = _("PipelineSchedules|Inactive")
+        = s_("PipelineSchedules|Inactive")
     %td
       - if pipeline_schedule.owner
         = image_tag avatar_icon(pipeline_schedule.owner, 20), class: "avatar s20"
diff --git a/app/views/projects/pipeline_schedules/index.html.haml b/app/views/projects/pipeline_schedules/index.html.haml
index 4a96ee652d2f790cde0cc823c229870870904bd9..c296152e54f18e0e0128a36c146c82b5c93a22f4 100644
--- a/app/views/projects/pipeline_schedules/index.html.haml
+++ b/app/views/projects/pipeline_schedules/index.html.haml
@@ -14,7 +14,7 @@
 
     .nav-controls
       = link_to new_namespace_project_pipeline_schedule_path(@project.namespace, @project), class: 'btn btn-create' do
-        %span New schedule
+        %span= _('New schedule')
 
   - if @schedules.present?
     %ul.content-list
diff --git a/app/views/projects/project_members/_new_project_member.html.haml b/app/views/projects/project_members/_new_project_member.html.haml
index 247c4bdbe2de3bad481bf8f3cad08a0bb8240c7e..8bf2246662a0363aa1a9bc3c48a29496393258b1 100644
--- a/app/views/projects/project_members/_new_project_member.html.haml
+++ b/app/views/projects/project_members/_new_project_member.html.haml
@@ -6,7 +6,9 @@
         = users_select_tag(:user_ids, multiple: true, class: "input-clamp", scope: :all, email_user: true, placeholder: "Search for members to update or invite")
       .form-group
         = label_tag :access_level, "Choose a role permission", class: "label-light"
-        = select_tag :access_level, options_for_select(ProjectMember.access_level_roles, @project_member.access_level), class: "form-control project-access-select"
+        .select-wrapper
+          = select_tag :access_level, options_for_select(ProjectMember.access_level_roles, @project_member.access_level), class: "form-control project-access-select select-control"
+          = icon('chevron-down')
         .help-block.append-bottom-10
           = link_to "Read more", help_page_path("user/permissions"), class: "vlink"
           about role permissions
diff --git a/app/views/projects/project_members/_new_shared_group.html.haml b/app/views/projects/project_members/_new_shared_group.html.haml
index b7cc8dd7062a1671b9894aff84e3bbe21fea3d92..643569db6464351cc0ea25b76edba908c4cdeeb7 100644
--- a/app/views/projects/project_members/_new_shared_group.html.haml
+++ b/app/views/projects/project_members/_new_shared_group.html.haml
@@ -8,7 +8,7 @@
         = label_tag :link_group_access, "Max access level", class: "label-light"
         .select-wrapper
           = select_tag :link_group_access, options_for_select(ProjectGroupLink.access_options, ProjectGroupLink.default_access), class: "form-control select-control"
-          = icon('caret-down')
+          = icon('chevron-down')
         .help-block.append-bottom-10
           = link_to "Read more", help_page_path("user/permissions"), class: "vlink"
           about role permissions
diff --git a/app/views/projects/registry/repositories/_image.html.haml b/app/views/projects/registry/repositories/_image.html.haml
index 8bc78f8d0185e94d5dd998218d766eb9771a86aa..dcdc432b6541a0fcf34ab81b0bc148af25220510 100644
--- a/app/views/projects/registry/repositories/_image.html.haml
+++ b/app/views/projects/registry/repositories/_image.html.haml
@@ -6,13 +6,14 @@
 
     = clipboard_button(clipboard_text: "docker pull #{image.location}")
 
-    .controls.hidden-xs.pull-right
-      = link_to namespace_project_container_registry_path(@project.namespace, @project, image),
-                class: 'btn btn-remove has-tooltip',
-                title: 'Remove repository',
-                data: { confirm: 'Are you sure?' },
-                method: :delete do
-        = icon('trash cred', 'aria-hidden': 'true')
+    - if can?(current_user, :update_container_image, @project)
+      .controls.hidden-xs.pull-right
+        = link_to namespace_project_container_registry_path(@project.namespace, @project, image),
+                  class: 'btn btn-remove has-tooltip',
+                  title: 'Remove repository',
+                  data: { confirm: 'Are you sure?' },
+                  method: :delete do
+          = icon('trash cred', 'aria-hidden': 'true')
 
   .container-image-tags.js-toggle-content.hide
     - if image.has_tags?
diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml
index abde2a485872ea7c1c32834b15b8789b15e35450..00da76349dad1617c6ebd56f7feeb9d0ef8a83e8 100644
--- a/app/views/projects/tree/_tree_header.html.haml
+++ b/app/views/projects/tree/_tree_header.html.haml
@@ -1,79 +1,81 @@
-.tree-controls
-  = render 'projects/find_file_link'
-
-  = link_to s_('Commits|History'), namespace_project_commits_path(@project.namespace, @project, @id), class: 'btn btn-grouped'
-
-  = render 'projects/buttons/download', project: @project, ref: @ref
+.tree-ref-container
+  .tree-ref-holder
+    = render 'shared/ref_switcher', destination: 'tree', path: @path
 
-.tree-ref-holder
-  = render 'shared/ref_switcher', destination: 'tree', path: @path
-
-%ul.breadcrumb.repo-breadcrumb
-  %li
-    = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
-      = @project.path
-  - path_breadcrumbs do |title, path|
+  %ul.breadcrumb.repo-breadcrumb
     %li
-      = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, tree_join(@ref, path))
+      = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
+        = @project.path
+    - path_breadcrumbs do |title, path|
+      %li
+        = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, tree_join(@ref, path))
 
-  - if current_user
-    %li
-      - if !on_top_of_branch?
-        %span.btn.add-to-tree.disabled.has-tooltip{ title: _("You can only add files when you are on a branch"), data: { container: 'body' } }
-          = icon('plus')
-      - else
-        %span.dropdown
-          %a.dropdown-toggle.btn.add-to-tree{ href: '#', "data-toggle" => "dropdown" }
+    - if current_user
+      %li
+        - if !on_top_of_branch?
+          %span.btn.add-to-tree.disabled.has-tooltip{ title: _("You can only add files when you are on a branch"), data: { container: 'body' } }
             = icon('plus')
-          %ul.dropdown-menu
-            - if can_edit_tree?
-              %li
-                = link_to namespace_project_new_blob_path(@project.namespace, @project, @id) do
-                  = icon('pencil fw')
-                  #{ _('New file') }
-              %li
-                = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } do
-                  = icon('file fw')
-                  #{ _('Upload file') }
-              %li
-                = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal' } do
-                  = icon('folder fw')
-                  #{ _('New directory') }
-            - elsif can?(current_user, :fork_project, @project)
-              %li
-                - continue_params = { to:         namespace_project_new_blob_path(@project.namespace, @project, @id),
-                                      notice:     edit_in_new_fork_notice,
-                                      notice_now: edit_in_new_fork_notice_now }
-                - fork_path = namespace_project_forks_path(@project.namespace, @project, namespace_key:  current_user.namespace.id,
-                                                                                        continue:       continue_params)
-                = link_to fork_path, method: :post do
-                  = icon('pencil fw')
-                  #{ _('New file') }
+        - else
+          %span.dropdown
+            %a.dropdown-toggle.btn.add-to-tree{ href: '#', "data-toggle" => "dropdown", "data-target" => ".add-to-tree-dropdown" }
+              = icon('plus')
+          .add-to-tree-dropdown
+            %ul.dropdown-menu
+              - if can_edit_tree?
+                %li
+                  = link_to namespace_project_new_blob_path(@project.namespace, @project, @id) do
+                    = icon('pencil fw')
+                    #{ _('New file') }
+                %li
+                  = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } do
+                    = icon('file fw')
+                    #{ _('Upload file') }
+                %li
+                  = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal' } do
+                    = icon('folder fw')
+                    #{ _('New directory') }
+              - elsif can?(current_user, :fork_project, @project)
+                %li
+                  - continue_params = { to:         namespace_project_new_blob_path(@project.namespace, @project, @id),
+                                        notice:     edit_in_new_fork_notice,
+                                        notice_now: edit_in_new_fork_notice_now }
+                  - fork_path = namespace_project_forks_path(@project.namespace, @project, namespace_key:  current_user.namespace.id,
+                                                                                          continue:       continue_params)
+                  = link_to fork_path, method: :post do
+                    = icon('pencil fw')
+                    #{ _('New file') }
+                %li
+                  - continue_params = { to:         request.fullpath,
+                                        notice:     edit_in_new_fork_notice + " Try to upload a file again.",
+                                        notice_now: edit_in_new_fork_notice_now }
+                  - fork_path = namespace_project_forks_path(@project.namespace, @project, namespace_key:  current_user.namespace.id,
+                                                                                          continue:       continue_params)
+                  = link_to fork_path, method: :post do
+                    = icon('file fw')
+                    #{ _('Upload file') }
+                %li
+                  - continue_params = { to:         request.fullpath,
+                                        notice:     edit_in_new_fork_notice + " Try to create a new directory again.",
+                                        notice_now: edit_in_new_fork_notice_now }
+                  - fork_path = namespace_project_forks_path(@project.namespace, @project, namespace_key:  current_user.namespace.id,
+                                                                                          continue:       continue_params)
+                  = link_to fork_path, method: :post do
+                    = icon('folder fw')
+                    #{ _('New directory') }
+
+              %li.divider
               %li
-                - continue_params = { to:         request.fullpath,
-                                      notice:     edit_in_new_fork_notice + " Try to upload a file again.",
-                                      notice_now: edit_in_new_fork_notice_now }
-                - fork_path = namespace_project_forks_path(@project.namespace, @project, namespace_key:  current_user.namespace.id,
-                                                                                        continue:       continue_params)
-                = link_to fork_path, method: :post do
-                  = icon('file fw')
-                  #{ _('Upload file') }
+                = link_to new_namespace_project_branch_path(@project.namespace, @project) do
+                  = icon('code-fork fw')
+                  #{ _('New branch') }
               %li
-                - continue_params = { to:         request.fullpath,
-                                      notice:     edit_in_new_fork_notice + " Try to create a new directory again.",
-                                      notice_now: edit_in_new_fork_notice_now }
-                - fork_path = namespace_project_forks_path(@project.namespace, @project, namespace_key:  current_user.namespace.id,
-                                                                                        continue:       continue_params)
-                = link_to fork_path, method: :post do
-                  = icon('folder fw')
-                  #{ _('New directory') }
+                = link_to new_namespace_project_tag_path(@project.namespace, @project) do
+                  = icon('tags fw')
+                  #{ _('New tag') }
+
+.tree-controls
+  = render 'projects/find_file_link'
 
-            %li.divider
-            %li
-              = link_to new_namespace_project_branch_path(@project.namespace, @project) do
-                = icon('code-fork fw')
-                #{ _('New branch') }
-            %li
-              = link_to new_namespace_project_tag_path(@project.namespace, @project) do
-                = icon('tags fw')
-                #{ _('New tag') }
+  = link_to s_('Commits|History'), namespace_project_commits_path(@project.namespace, @project, @id), class: 'btn'
+
+  = render 'projects/buttons/download', project: @project, ref: @ref
diff --git a/app/views/shared/_commit_message_container.html.haml b/app/views/shared/_commit_message_container.html.haml
index 4b98ff8824178626f246325f55a2581289001a18..2329de9e11fd1f698f84891913bd5fe376e9cbfe 100644
--- a/app/views/shared/_commit_message_container.html.haml
+++ b/app/views/shared/_commit_message_container.html.haml
@@ -2,7 +2,7 @@
   - nonce = SecureRandom.hex
   - descriptions = local_assigns.slice(:message_with_description, :message_without_description)
   = label_tag "commit_message-#{nonce}", class: 'control-label' do
-    Commit message
+    #{ _('Commit message') }
   .col-sm-10
     .commit-message-container
       .max-width-marker
diff --git a/app/views/shared/_new_commit_form.html.haml b/app/views/shared/_new_commit_form.html.haml
index 25a56f84ec5f4971a0d726a7fc254741f444bb40..0a4a24ae8075a772679784d9312acbe7cd17893d 100644
--- a/app/views/shared/_new_commit_form.html.haml
+++ b/app/views/shared/_new_commit_form.html.haml
@@ -5,16 +5,12 @@
 - else
   - if can?(current_user, :push_code, @project)
     .form-group.branch
-      = label_tag 'branch_name', 'Target branch', class: 'control-label'
+      = label_tag 'branch_name', _('Target Branch'), class: 'control-label'
       .col-sm-10
         = text_field_tag 'branch_name', @branch_name || tree_edit_branch, required: true, class: "form-control js-branch-name ref-name"
 
         .js-create-merge-request-container
-          .checkbox
-            - nonce = SecureRandom.hex
-            = label_tag "create_merge_request-#{nonce}" do
-              = check_box_tag 'create_merge_request', 1, true, class: 'js-create-merge-request', id: "create_merge_request-#{nonce}"
-              Start a <strong>new merge request</strong> with these changes
+          = render 'shared/new_merge_request_checkbox'
   - else
     = hidden_field_tag 'branch_name', @branch_name || tree_edit_branch
     = hidden_field_tag 'create_merge_request', 1
diff --git a/app/views/shared/_new_merge_request_checkbox.html.haml b/app/views/shared/_new_merge_request_checkbox.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..133c31f09c4487a5bbf47ea3a9d97521ad36aec0
--- /dev/null
+++ b/app/views/shared/_new_merge_request_checkbox.html.haml
@@ -0,0 +1,8 @@
+.checkbox
+  - nonce = SecureRandom.hex
+  = label_tag "create_merge_request-#{nonce}" do
+    = check_box_tag 'create_merge_request', 1, true, class: 'js-create-merge-request', id: "create_merge_request-#{nonce}"
+    - translation_variables = { new_merge_request: "<strong>#{_('new merge request')}</strong>" }
+    - translation = _('Start a %{new_merge_request} with these changes') % translation_variables
+    #{ translation.html_safe }
+
diff --git a/app/views/shared/form_elements/_description.html.haml b/app/views/shared/form_elements/_description.html.haml
index 307d4919224e1ae0fd5cbebf939de7f39ebd9f61..f65bb6a29e6072031a2f84cb78fc4c61244db969 100644
--- a/app/views/shared/form_elements/_description.html.haml
+++ b/app/views/shared/form_elements/_description.html.haml
@@ -2,10 +2,10 @@
 - model = local_assigns.fetch(:model)
 
 - form = local_assigns.fetch(:form)
-- supports_slash_commands = model.new_record?
+- supports_quick_actions = model.new_record?
 
-- if supports_slash_commands
-  - preview_url = preview_markdown_path(project, slash_commands_target_type: model.class.name)
+- if supports_quick_actions
+  - preview_url = preview_markdown_path(project, quick_actions_target_type: model.class.name)
 - else
   - preview_url = preview_markdown_path(project)
 
@@ -17,7 +17,7 @@
       = render 'projects/zen', f: form, attr: :description,
                                classes: 'note-textarea',
                                placeholder: "Write a comment or drag your files here...",
-                               supports_slash_commands: supports_slash_commands
-      = render 'shared/notes/hints', supports_slash_commands: supports_slash_commands
+                               supports_quick_actions: supports_quick_actions
+      = render 'shared/notes/hints', supports_quick_actions: supports_quick_actions
       .clearfix
       .error-alert
diff --git a/app/views/shared/issuable/_bulk_update_sidebar.html.haml b/app/views/shared/issuable/_bulk_update_sidebar.html.haml
index a8a6d84128d83e945e40739e018dc1e1cf7f8af0..7cfdfb6e6ee1ace825b21607fda2101b280940d9 100644
--- a/app/views/shared/issuable/_bulk_update_sidebar.html.haml
+++ b/app/views/shared/issuable/_bulk_update_sidebar.html.haml
@@ -1,7 +1,7 @@
 - type = local_assigns.fetch(:type)
 
 %aside.issues-bulk-update.js-right-sidebar.right-sidebar.affix-top{ data: { "offset-top" => "50", "spy" => "affix" }, "aria-live" => "polite" }
-  .issuable-sidebar
+  .issuable-sidebar.hidden
     = form_tag [:bulk_update, @project.namespace.becomes(Namespace), @project, type], method: :post, class: "bulk-update"  do
       .block
         .filter-item.inline.update-issues-btn.pull-left
diff --git a/app/views/shared/issuable/_nav.html.haml b/app/views/shared/issuable/_nav.html.haml
index cf7ba52d840c3cd7b40df84d09f792fab8469897..3f03cc7a275a2a17437fba46bbb650889814417b 100644
--- a/app/views/shared/issuable/_nav.html.haml
+++ b/app/views/shared/issuable/_nav.html.haml
@@ -1,24 +1,25 @@
 - type = local_assigns.fetch(:type, :issues)
 - page_context_word = type.to_s.humanize(capitalize: false)
 - issuables = @issues || @merge_requests
-- closed_title = 'Filter by issues that are currently closed.'
 
 %ul.nav-links.issues-state-filters
   %li{ class: active_when(params[:state] == 'opened') }>
-    %button.btn.btn-link{ id: 'state-opened', title: "Filter by #{page_context_word} that are currently opened.", type: 'button', data: { state: 'opened' } }
+    = link_to page_filter_path(state: 'opened', label: true), id: 'state-opened', title: "Filter by #{page_context_word} that are currently opened.", data: { state: 'opened' } do
       #{issuables_state_counter_text(type, :opened)}
 
   - if type == :merge_requests
     %li{ class: active_when(params[:state] == 'merged') }>
-      %button.btn.btn-link{ id: 'state-merged', title: 'Filter by merge requests that are currently merged.', type: 'button', data: { state: 'merged' } }
+      = link_to page_filter_path(state: 'merged', label: true), id: 'state-merged', title: 'Filter by merge requests that are currently merged.', data: { state: 'merged' } do
         #{issuables_state_counter_text(type, :merged)}
 
-    - closed_title = 'Filter by merge requests that are currently closed and unmerged.'
-
-  %li{ class: active_when(params[:state] == 'closed') }>
-    %button.btn.btn-link{ id: 'state-closed', title: closed_title, type: 'button', data: { state: 'closed' } }
-      #{issuables_state_counter_text(type, :closed)}
+    %li{ class: active_when(params[:state] == 'closed') }>
+      = link_to page_filter_path(state: 'closed', label: true), id: 'state-closed', title: 'Filter by merge requests that are currently closed and unmerged.', data: { state: 'closed' } do
+        #{issuables_state_counter_text(type, :closed)}
+  - else
+    %li{ class: active_when(params[:state] == 'closed') }>
+      = link_to page_filter_path(state: 'closed', label: true), id: 'state-closed', title: 'Filter by issues that are currently closed.', data: { state: 'closed' } do
+        #{issuables_state_counter_text(type, :closed)}
 
   %li{ class: active_when(params[:state] == 'all') }>
-    %button.btn.btn-link{ id: 'state-all', title: "Show all #{page_context_word}.", type: 'button', data: { state: 'all' } }
+    = link_to page_filter_path(state: 'all', label: true), id: 'state-all', title: "Show all #{page_context_word}.", data: { state: 'all' } do
       #{issuables_state_counter_text(type, :all)}
diff --git a/app/views/shared/milestones/_issuables.html.haml b/app/views/shared/milestones/_issuables.html.haml
index 8af3bd597c5a273b86c7d21870fb9f059ed3f56c..7175e275f95ff12ee7cd445c2afb32d44e0b6186 100644
--- a/app/views/shared/milestones/_issuables.html.haml
+++ b/app/views/shared/milestones/_issuables.html.haml
@@ -8,11 +8,11 @@
       = title
     - if show_counter
       .counter
-        = number_with_delimiter(issuables.size)
+        = number_with_delimiter(issuables.length)
 
   - class_prefix = dom_class(issuables).pluralize
-  %ul{ class: "well-list #{class_prefix}-sortable-list", id: "#{class_prefix}-list-#{id}", "data-state" => id }
+  %ul{ class: "well-list milestone-#{class_prefix}-list", id: "#{class_prefix}-list-#{id}" }
     = render partial: 'shared/milestones/issuable',
-             collection: issuables.order_position_asc,
+             collection: issuables,
              as: :issuable,
              locals: { show_project_name: show_project_name, show_full_project_name: show_full_project_name }
diff --git a/app/views/shared/milestones/_tabs.html.haml b/app/views/shared/milestones/_tabs.html.haml
index 6a6d817b344e00ac115b57ad5c411ab2202adac0..4de8a6cb15f0eb70a0adc974ff5b98f590da5bf2 100644
--- a/app/views/shared/milestones/_tabs.html.haml
+++ b/app/views/shared/milestones/_tabs.html.haml
@@ -31,12 +31,12 @@
 .tab-content.milestone-content
   - if milestone.is_a?(GlobalMilestone) || can?(current_user, :read_issue, @project)
     .tab-pane.active#tab-issues{ data: { sort_endpoint: (sort_issues_namespace_project_milestone_path(@project.namespace, @project, @milestone) if @project && current_user) } }
-      = render 'shared/milestones/issues_tab', issues: milestone.issues_visible_to_user(current_user).include_associations, show_project_name: show_project_name, show_full_project_name: show_full_project_name
-    .tab-pane#tab-merge-requests{ data: { sort_endpoint: (sort_merge_requests_namespace_project_milestone_path(@project.namespace, @project, @milestone) if @project && current_user) } }
+      = render 'shared/milestones/issues_tab', issues: milestone.sorted_issues(current_user), show_project_name: show_project_name, show_full_project_name: show_full_project_name
+    .tab-pane#tab-merge-requests
       -# loaded async
       = render "shared/milestones/tab_loading"
   - else
-    .tab-pane.active#tab-merge-requests{ data: { sort_endpoint: (sort_merge_requests_namespace_project_milestone_path(@project.namespace, @project, @milestone) if @project && current_user) } }
+    .tab-pane.active#tab-merge-requests
       -# loaded async
       = render "shared/milestones/tab_loading"
   .tab-pane#tab-participants
diff --git a/app/views/shared/notes/_form.html.haml b/app/views/shared/notes/_form.html.haml
index eaf50bc2115aa6143d4e84fb509f889f0f9d10e9..c6b5dcc3647d37996840ee331087506cdf439052 100644
--- a/app/views/shared/notes/_form.html.haml
+++ b/app/views/shared/notes/_form.html.haml
@@ -1,6 +1,7 @@
-- supports_slash_commands = note_supports_slash_commands?(@note)
-- if supports_slash_commands
-  - preview_url = preview_markdown_path(@project, slash_commands_target_type: @note.noteable_type, slash_commands_target_id: @note.noteable_id)
+- supports_autocomplete = local_assigns.fetch(:supports_autocomplete, true)
+- supports_quick_actions = note_supports_quick_actions?(@note)
+- if supports_quick_actions
+  - preview_url = preview_markdown_path(@project, quick_actions_target_type: @note.noteable_type, quick_actions_target_id: @note.noteable_id)
 - else
   - preview_url = preview_markdown_path(@project)
 
@@ -27,8 +28,9 @@
       attr: :note,
       classes: 'note-textarea js-note-text',
       placeholder: "Write a comment or drag your files here...",
-      supports_slash_commands: supports_slash_commands
-    = render 'shared/notes/hints', supports_slash_commands: supports_slash_commands
+      supports_quick_actions: supports_quick_actions,
+      supports_autocomplete: supports_autocomplete
+    = render 'shared/notes/hints', supports_quick_actions: supports_quick_actions
     .error-alert
 
   .note-form-actions.clearfix
diff --git a/app/views/shared/notes/_hints.html.haml b/app/views/shared/notes/_hints.html.haml
index 7ce6130de6043eaac973d6d120ea817ee4c77199..bc1ac3d8ac22c4cfb98716f4eb6428f7e6aede92 100644
--- a/app/views/shared/notes/_hints.html.haml
+++ b/app/views/shared/notes/_hints.html.haml
@@ -1,10 +1,10 @@
-- supports_slash_commands = local_assigns.fetch(:supports_slash_commands, false)
+- supports_quick_actions = local_assigns.fetch(:supports_quick_actions, false)
 .comment-toolbar.clearfix
   .toolbar-text
     = link_to 'Markdown', help_page_path('user/markdown'), target: '_blank', tabindex: -1
-    - if supports_slash_commands
+    - if supports_quick_actions
       and
-      = link_to 'slash commands', help_page_path('user/project/slash_commands'), target: '_blank', tabindex: -1
+      = link_to 'quick actions', help_page_path('user/project/quick_actions'), target: '_blank', tabindex: -1
       are
     - else
       is
diff --git a/app/views/shared/notes/_notes_with_form.html.haml b/app/views/shared/notes/_notes_with_form.html.haml
index 5902798dfd06119d5957412a7866969cda71e707..f0fcc414756b017faf138629a12b86b8d0244768 100644
--- a/app/views/shared/notes/_notes_with_form.html.haml
+++ b/app/views/shared/notes/_notes_with_form.html.haml
@@ -6,13 +6,14 @@
 - if can_create_note?
   %ul.notes.notes-form.timeline
     %li.timeline-entry
-      .flash-container.timeline-content
+      .timeline-entry-inner
+        .flash-container.timeline-content
 
-      .timeline-icon.hidden-xs.hidden-sm
-        %a.author_link{ href: user_path(current_user) }
-          = image_tag avatar_icon(current_user), alt: current_user.to_reference, class: 'avatar s40'
-      .timeline-content.timeline-content-form
-        = render "shared/notes/form", view: diff_view
+        .timeline-icon.hidden-xs.hidden-sm
+          %a.author_link{ href: user_path(current_user) }
+            = image_tag avatar_icon(current_user), alt: current_user.to_reference, class: 'avatar s40'
+        .timeline-content.timeline-content-form
+          = render "shared/notes/form", view: diff_view, supports_autocomplete: autocomplete
 - elsif !current_user
   .disabled-comment.text-center.prepend-top-default
     Please
diff --git a/app/workers/merge_worker.rb b/app/workers/merge_worker.rb
index 79efca4f2f9443f18e4eb45aa0236f1143bc9358..48e2da338f637a95b8ee04d9c72e5acca6c58b7d 100644
--- a/app/workers/merge_worker.rb
+++ b/app/workers/merge_worker.rb
@@ -7,7 +7,7 @@ class MergeWorker
     current_user = User.find(current_user_id)
     merge_request = MergeRequest.find(merge_request_id)
 
-    MergeRequests::MergeService.new(merge_request.target_project, current_user, params).
-      execute(merge_request)
+    MergeRequests::MergeService.new(merge_request.target_project, current_user, params)
+      .execute(merge_request)
   end
 end
diff --git a/app/workers/process_commit_worker.rb b/app/workers/process_commit_worker.rb
index fe6a49976e0c1196a89b4e81e45e78fc6bd49f3b..c0c03848a403b7d937a53ea5ba7cd6699ac8653a 100644
--- a/app/workers/process_commit_worker.rb
+++ b/app/workers/process_commit_worker.rb
@@ -47,8 +47,8 @@ class ProcessCommitWorker
     # therefor we use IssueCollection here and skip the authorization check in
     # Issues::CloseService#execute.
     IssueCollection.new(issues).updatable_by_user(user).each do |issue|
-      Issues::CloseService.new(project, author).
-        close_issue(issue, commit: commit)
+      Issues::CloseService.new(project, author)
+        .close_issue(issue, commit: commit)
     end
   end
 
@@ -57,8 +57,8 @@ class ProcessCommitWorker
 
     return if mentioned_issues.empty?
 
-    Issue::Metrics.where(issue_id: mentioned_issues.map(&:id), first_mentioned_in_commit_at: nil).
-      update_all(first_mentioned_in_commit_at: commit.committed_date)
+    Issue::Metrics.where(issue_id: mentioned_issues.map(&:id), first_mentioned_in_commit_at: nil)
+      .update_all(first_mentioned_in_commit_at: commit.committed_date)
   end
 
   def build_commit(project, hash)
diff --git a/app/workers/project_cache_worker.rb b/app/workers/project_cache_worker.rb
index 8ff9d07860fdf0ab9eaf7ca85cdca43a0e9b6749..505ff9e086e7d9eee8105022d5bdec0ec66a8077 100644
--- a/app/workers/project_cache_worker.rb
+++ b/app/workers/project_cache_worker.rb
@@ -32,8 +32,8 @@ class ProjectCacheWorker
   private
 
   def try_obtain_lease_for(project_id, section)
-    Gitlab::ExclusiveLease.
-      new("project_cache_worker:#{project_id}:#{section}", timeout: LEASE_TIMEOUT).
-      try_obtain
+    Gitlab::ExclusiveLease
+      .new("project_cache_worker:#{project_id}:#{section}", timeout: LEASE_TIMEOUT)
+      .try_obtain
   end
 end
diff --git a/app/workers/propagate_service_template_worker.rb b/app/workers/propagate_service_template_worker.rb
index 5ce0e0405d0acd412c8df3b5ff18819348ae3485..6b607451c7ab8c826b07df888cc2709837e9d48d 100644
--- a/app/workers/propagate_service_template_worker.rb
+++ b/app/workers/propagate_service_template_worker.rb
@@ -14,8 +14,8 @@ class PropagateServiceTemplateWorker
   private
 
   def try_obtain_lease_for(template_id)
-    Gitlab::ExclusiveLease.
-      new("propagate_service_template_worker:#{template_id}", timeout: LEASE_TIMEOUT).
-      try_obtain
+    Gitlab::ExclusiveLease
+      .new("propagate_service_template_worker:#{template_id}", timeout: LEASE_TIMEOUT)
+      .try_obtain
   end
 end
diff --git a/app/workers/prune_old_events_worker.rb b/app/workers/prune_old_events_worker.rb
index 392abb9c21b43e2476ee37362b3ae7ebc5a09659..2b43bb19ad1a3b97de1283d09bb71aa242fee691 100644
--- a/app/workers/prune_old_events_worker.rb
+++ b/app/workers/prune_old_events_worker.rb
@@ -10,9 +10,9 @@ class PruneOldEventsWorker
       '(id IN (SELECT id FROM (?) ids_to_remove))',
       Event.unscoped.where(
         'created_at < ?',
-        (12.months + 1.day).ago).
-      select(:id).
-      limit(10_000)).
-    delete_all
+        (12.months + 1.day).ago)
+      .select(:id)
+      .limit(10_000))
+    .delete_all
   end
 end
diff --git a/app/workers/repository_check/batch_worker.rb b/app/workers/repository_check/batch_worker.rb
index c3e7491ec4ec9f1f937c9c7c9ee1dc97d16f9bf5..b94d83bd7098313d4fea32df3f39b661114e20c2 100644
--- a/app/workers/repository_check/batch_worker.rb
+++ b/app/workers/repository_check/batch_worker.rb
@@ -32,10 +32,10 @@ module RepositoryCheck
     # has to sit and wait for this query to finish.
     def project_ids
       limit = 10_000
-      never_checked_projects = Project.where('last_repository_check_at IS NULL AND created_at < ?', 24.hours.ago).
-        limit(limit).pluck(:id)
-      old_check_projects = Project.where('last_repository_check_at < ?', 1.month.ago).
-        reorder('last_repository_check_at ASC').limit(limit).pluck(:id)
+      never_checked_projects = Project.where('last_repository_check_at IS NULL AND created_at < ?', 24.hours.ago)
+        .limit(limit).pluck(:id)
+      old_check_projects = Project.where('last_repository_check_at < ?', 1.month.ago)
+        .reorder('last_repository_check_at ASC').limit(limit).pluck(:id)
       never_checked_projects + old_check_projects
     end
 
diff --git a/app/workers/update_user_activity_worker.rb b/app/workers/update_user_activity_worker.rb
index b3c2f13aa33d61adb3d7885093c54d93fec10156..31bbdb69edb18112e756ecd0012f43082d6b2b40 100644
--- a/app/workers/update_user_activity_worker.rb
+++ b/app/workers/update_user_activity_worker.rb
@@ -7,8 +7,8 @@ class UpdateUserActivityWorker
     ids = pairs.keys
     conditions = 'WHEN id = ? THEN ? ' * ids.length
 
-    User.where(id: ids).
-      update_all([
+    User.where(id: ids)
+      .update_all([
         "last_activity_on = CASE #{conditions} ELSE last_activity_on END",
         *pairs.to_a.flatten
       ])
diff --git a/changelogs/unreleased/12151-add-since-and-until-params-to-issuables.yml b/changelogs/unreleased/12151-add-since-and-until-params-to-issuables.yml
new file mode 100644
index 0000000000000000000000000000000000000000..2c915e62357de13721fcfff00f4b6b85b67b2733
--- /dev/null
+++ b/changelogs/unreleased/12151-add-since-and-until-params-to-issuables.yml
@@ -0,0 +1,4 @@
+---
+title: Added "created_after" and "created_before" params to issuables
+merge_request: 12151
+author: Kyle Bishop @kybishop
diff --git a/changelogs/unreleased/25102-files-view-button.yml b/changelogs/unreleased/25102-files-view-button.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4ba815d9464ea73af055234fe62ca1a3c9b07a2c
--- /dev/null
+++ b/changelogs/unreleased/25102-files-view-button.yml
@@ -0,0 +1,4 @@
+---
+title: Fix mobile view of files view buttons
+merge_request:
+author:
diff --git a/changelogs/unreleased/25164-disable-fork-on-project-limit.yml b/changelogs/unreleased/25164-disable-fork-on-project-limit.yml
new file mode 100644
index 0000000000000000000000000000000000000000..9fa824b161d74e41ffcfaa9276f8784037b4cdae
--- /dev/null
+++ b/changelogs/unreleased/25164-disable-fork-on-project-limit.yml
@@ -0,0 +1,4 @@
+---
+title: Disable fork button on project limit
+merge_request: 12145
+author: Ivan Chernov
diff --git a/changelogs/unreleased/26212-upload-user-avatar-trough-api.yml b/changelogs/unreleased/26212-upload-user-avatar-trough-api.yml
new file mode 100644
index 0000000000000000000000000000000000000000..667454ae95d1e07020f698d48f302e95a2204a5a
--- /dev/null
+++ b/changelogs/unreleased/26212-upload-user-avatar-trough-api.yml
@@ -0,0 +1,4 @@
+---
+title: Accept image for avatar in user API
+merge_request: 12143
+author: Ivan Chernov
diff --git a/changelogs/unreleased/27070-rename-slash-commands-to-quick-actions.yml b/changelogs/unreleased/27070-rename-slash-commands-to-quick-actions.yml
new file mode 100644
index 0000000000000000000000000000000000000000..497239db808180efdaf314febeb77731d052a290
--- /dev/null
+++ b/changelogs/unreleased/27070-rename-slash-commands-to-quick-actions.yml
@@ -0,0 +1,5 @@
+---
+title: Rename "Slash commands" to "Quick actions" and deprecate "chat commands" in favor
+  of "slash commands"
+merge_request:
+author:
diff --git a/changelogs/unreleased/27645-html-email-brackets-bug.yml b/changelogs/unreleased/27645-html-email-brackets-bug.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e8004d03884c7e48eb8d0fdc748a9aea1a2e6c5b
--- /dev/null
+++ b/changelogs/unreleased/27645-html-email-brackets-bug.yml
@@ -0,0 +1,4 @@
+---
+title: Fix an email parsing bug where brackets would be inserted in emails from some Outlook clients
+merge_request: 9045
+author: jneen
diff --git a/changelogs/unreleased/27697-make-arrow-icons-consistent-in-dropdown.yml b/changelogs/unreleased/27697-make-arrow-icons-consistent-in-dropdown.yml
new file mode 100644
index 0000000000000000000000000000000000000000..92b5b59f46fb5e8d31db00361217d742627c0fb4
--- /dev/null
+++ b/changelogs/unreleased/27697-make-arrow-icons-consistent-in-dropdown.yml
@@ -0,0 +1,4 @@
+---
+title: Use fa-chevron-down on dropdown arrows for consistency
+merge_request: 9659
+author: TM Lee
diff --git a/changelogs/unreleased/28139-use-color-input-broadcast-messages.yml b/changelogs/unreleased/28139-use-color-input-broadcast-messages.yml
new file mode 100644
index 0000000000000000000000000000000000000000..97ebabaff1c3352d606f7c2ac545b9cfe45ab8c4
--- /dev/null
+++ b/changelogs/unreleased/28139-use-color-input-broadcast-messages.yml
@@ -0,0 +1,4 @@
+---
+title: Use color inputs for broadcast messages
+merge_request:
+author:
diff --git a/changelogs/unreleased/30213-project-transfer-move-rollback.yml b/changelogs/unreleased/30213-project-transfer-move-rollback.yml
new file mode 100644
index 0000000000000000000000000000000000000000..3eb1e399c545ce697250681189d0f5701f1c4bb1
--- /dev/null
+++ b/changelogs/unreleased/30213-project-transfer-move-rollback.yml
@@ -0,0 +1,4 @@
+---
+title: Rollback project repo move if there is an error in Projects::TransferService
+merge_request: 11877
+author:
diff --git a/changelogs/unreleased/30725-reset-user-limits-when-unchecking-external-user.yml b/changelogs/unreleased/30725-reset-user-limits-when-unchecking-external-user.yml
new file mode 100644
index 0000000000000000000000000000000000000000..3058404b3f886060e056b869da53379032cfc01d
--- /dev/null
+++ b/changelogs/unreleased/30725-reset-user-limits-when-unchecking-external-user.yml
@@ -0,0 +1,4 @@
+---
+title: Ensures default user limits when external user is unchecked
+merge_request: 12218
+author:
diff --git a/changelogs/unreleased/32301-filter-archive-project-on-param-present.yml b/changelogs/unreleased/32301-filter-archive-project-on-param-present.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d6534ed4e1a9bee30d0560bbeb87599c9da993eb
--- /dev/null
+++ b/changelogs/unreleased/32301-filter-archive-project-on-param-present.yml
@@ -0,0 +1,4 @@
+---
+title: Filter archived project in API v3 only if param present
+merge_request: 12245
+author: Ivan Chernov
diff --git a/changelogs/unreleased/33441-supplement_simplified_chinese_translation_of_i18n.yml b/changelogs/unreleased/33441-supplement_simplified_chinese_translation_of_i18n.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a7d8ac9054bf6359eac7f10aa539223c40b0cb2c
--- /dev/null
+++ b/changelogs/unreleased/33441-supplement_simplified_chinese_translation_of_i18n.yml
@@ -0,0 +1,4 @@
+---
+title: Supplement Simplified Chinese translation of Project Page & Repository Page
+merge_request: 11994
+author: Huang Tao
diff --git a/changelogs/unreleased/33461-display-user-id.yml b/changelogs/unreleased/33461-display-user-id.yml
new file mode 100644
index 0000000000000000000000000000000000000000..cba94625b07c5df01e6185716f18f37a28fa6c01
--- /dev/null
+++ b/changelogs/unreleased/33461-display-user-id.yml
@@ -0,0 +1,4 @@
+---
+title: Display own user id in account settings page
+merge_request: 12141
+author: Riccardo Padovani
diff --git a/changelogs/unreleased/33837-remove-trash-on-registry-image.yml b/changelogs/unreleased/33837-remove-trash-on-registry-image.yml
new file mode 100644
index 0000000000000000000000000000000000000000..2d337f5e6e41bb4c4f7e63bc1d52fc36b153c8bd
--- /dev/null
+++ b/changelogs/unreleased/33837-remove-trash-on-registry-image.yml
@@ -0,0 +1,4 @@
+---
+title: Remove registry image delete button if user cant delete it
+merge_request: 12317
+author: Ivan Chernov
diff --git a/changelogs/unreleased/33878_fix_edit_deploy_key.yml b/changelogs/unreleased/33878_fix_edit_deploy_key.yml
new file mode 100644
index 0000000000000000000000000000000000000000..bc47d522240d74b9af664b34aa9f6690aa5e067b
--- /dev/null
+++ b/changelogs/unreleased/33878_fix_edit_deploy_key.yml
@@ -0,0 +1,4 @@
+---
+title: Fix edit button for deploy keys available from other projects
+merge_request: 12301
+author: Alexander Randa
diff --git a/changelogs/unreleased/33917-mr-comment-system-note-highlight-don-t-have-the-same-width.yml b/changelogs/unreleased/33917-mr-comment-system-note-highlight-don-t-have-the-same-width.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f3b92878f6df7a38e439393993fefc838e65eee4
--- /dev/null
+++ b/changelogs/unreleased/33917-mr-comment-system-note-highlight-don-t-have-the-same-width.yml
@@ -0,0 +1,4 @@
+---
+title: Standardize timeline note margins across different viewport sizes
+merge_request: 12364
+author:
diff --git a/changelogs/unreleased/34008-fix-CI_ENVIRONMENT_URL-2.yml b/changelogs/unreleased/34008-fix-CI_ENVIRONMENT_URL-2.yml
new file mode 100644
index 0000000000000000000000000000000000000000..7f4d6e3bc67d15027386eca76466bdf0d223b101
--- /dev/null
+++ b/changelogs/unreleased/34008-fix-CI_ENVIRONMENT_URL-2.yml
@@ -0,0 +1,4 @@
+---
+title: Fix passing CI_ENVIRONMENT_NAME and CI_ENVIRONMENT_SLUG for CI_ENVIRONMENT_URL
+merge_request: 12344
+author:
diff --git a/changelogs/unreleased/disable-environment-list-refresh.yml b/changelogs/unreleased/disable-environment-list-refresh.yml
new file mode 100644
index 0000000000000000000000000000000000000000..62fd71496a0ff2fe18af6aedb0acb6cd0114cab8
--- /dev/null
+++ b/changelogs/unreleased/disable-environment-list-refresh.yml
@@ -0,0 +1,4 @@
+---
+title: Disable environment list refresh due to bug https://gitlab.com/gitlab-org/gitlab-ee/issues/2677
+merge_request: 12347
+author:
diff --git a/changelogs/unreleased/dt-printing-to-api.yml b/changelogs/unreleased/dt-printing-to-api.yml
new file mode 100644
index 0000000000000000000000000000000000000000..5253b57f21a81cd6e552a8ccb65201f83eebc377
--- /dev/null
+++ b/changelogs/unreleased/dt-printing-to-api.yml
@@ -0,0 +1,4 @@
+---
+title: Added printing_merge_requst_link_enabled to the API
+merge_request:
+author: David Turner <dturner@twosigma.com>
diff --git a/changelogs/unreleased/fix-33259.yml b/changelogs/unreleased/fix-33259.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c68e42c02cfff33ca9096557aa1c6f3b72f7583a
--- /dev/null
+++ b/changelogs/unreleased/fix-33259.yml
@@ -0,0 +1,4 @@
+---
+title: Fix GitHub importer performance on branch existence check
+merge_request:
+author:
diff --git a/changelogs/unreleased/fix-overflow-slash-commands.yml b/changelogs/unreleased/fix-overflow-slash-commands.yml
new file mode 100644
index 0000000000000000000000000000000000000000..98ec399e8cb8e2569afa34c374c80f266008e50d
--- /dev/null
+++ b/changelogs/unreleased/fix-overflow-slash-commands.yml
@@ -0,0 +1,4 @@
+---
+title: Fixed overflow on mobile screens for the slash commands
+merge_request:
+author:
diff --git a/changelogs/unreleased/fixed-confidential-issue-bar.yml b/changelogs/unreleased/fixed-confidential-issue-bar.yml
deleted file mode 100644
index 6a41590d0af15db4b118a2432164bfd46ab07487..0000000000000000000000000000000000000000
--- a/changelogs/unreleased/fixed-confidential-issue-bar.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Make confidential issues more obviously confidential
-merge_request:
-author:
diff --git a/changelogs/unreleased/issue_20900.yml b/changelogs/unreleased/issue_20900.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e8cef6d2bce8aac58fcb5d3cf6704d6911672038
--- /dev/null
+++ b/changelogs/unreleased/issue_20900.yml
@@ -0,0 +1,4 @@
+---
+title: Remove issues/merge requests drag n drop and sorting from milestone view
+merge_request:
+author:
diff --git a/changelogs/unreleased/issue_33205.yml b/changelogs/unreleased/issue_33205.yml
new file mode 100644
index 0000000000000000000000000000000000000000..54b442048d8926ef673cdfe33ecc027f3bc02cc4
--- /dev/null
+++ b/changelogs/unreleased/issue_33205.yml
@@ -0,0 +1,4 @@
+---
+title: Fix API bug accepting wrong parameter to create merge request
+merge_request:
+author:
diff --git a/changelogs/unreleased/moved-submodules.yml b/changelogs/unreleased/moved-submodules.yml
new file mode 100644
index 0000000000000000000000000000000000000000..eee858717ed7934e05f8ad176c77fc2da7d2f83c
--- /dev/null
+++ b/changelogs/unreleased/moved-submodules.yml
@@ -0,0 +1,4 @@
+---
+title: 'Handle renamed submodules in repository browser'
+merge_request: 10798
+author: David Turner
diff --git a/changelogs/unreleased/mr-widget-memory-usage-tech-debt-fix.yml b/changelogs/unreleased/mr-widget-memory-usage-tech-debt-fix.yml
new file mode 100644
index 0000000000000000000000000000000000000000..14b5493a2466269b20b5a9a6b4e72f5c6c67ee89
--- /dev/null
+++ b/changelogs/unreleased/mr-widget-memory-usage-tech-debt-fix.yml
@@ -0,0 +1,4 @@
+---
+title: Changed utilities imports from ~ to relative paths
+merge_request:
+author:
diff --git a/changelogs/unreleased/reduce-sidekiq-wait-timings.yml b/changelogs/unreleased/reduce-sidekiq-wait-timings.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4d23accc82ebbbd7b71398e2e677f6b437b1a915
--- /dev/null
+++ b/changelogs/unreleased/reduce-sidekiq-wait-timings.yml
@@ -0,0 +1,4 @@
+---
+title: Reduce time spent waiting for certain Sidekiq jobs to complete
+merge_request:
+author:
diff --git a/changelogs/unreleased/refactor-projects-finder-init-collection.yml b/changelogs/unreleased/refactor-projects-finder-init-collection.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c8113419f21256f5c1067bd1148a12da6d1e6cb6
--- /dev/null
+++ b/changelogs/unreleased/refactor-projects-finder-init-collection.yml
@@ -0,0 +1,5 @@
+---
+title: Refactor ProjectsFinder#init_collection to produce more efficient queries for
+  retrieving projects
+merge_request:
+author:
diff --git a/changelogs/unreleased/replase_spinach_spec_create-feature.yml b/changelogs/unreleased/replase_spinach_spec_create-feature.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0613d195d56638b48cb556499c42d31e839a9d98
--- /dev/null
+++ b/changelogs/unreleased/replase_spinach_spec_create-feature.yml
@@ -0,0 +1,4 @@
+---
+title: Replace 'create.feature' spinach test with an rspec analog
+merge_request: 12343
+author: @blackst0ne
diff --git a/config/boot.rb b/config/boot.rb
index db5ab9180216b208d8c9591810a369177c4d9bf7..16de55d7a86289cda784a6f0be99ac79ea6c202c 100644
--- a/config/boot.rb
+++ b/config/boot.rb
@@ -6,7 +6,9 @@ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
 require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
 
 # set default directory for multiproces metrics gathering
-ENV['prometheus_multiproc_dir'] ||= 'tmp/prometheus_multiproc_dir'
+if ENV['RAILS_ENV'] == 'development' || ENV['RAILS_ENV'] == 'test'
+  ENV['prometheus_multiproc_dir'] ||= 'tmp/prometheus_multiproc_dir'
+end
 
 # Default Bootsnap configuration from https://github.com/Shopify/bootsnap#usage
 require 'bootsnap'
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 0b33783869b7b514e3ae738853b3b3468de17e05..43a8c0078cae6b6ab525872eb443d9bbc8f94dc7 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -454,6 +454,10 @@ production: &base
     # introduced in 9.0). Eventually Gitaly use will become mandatory and
     # this option will disappear.
     enabled: true
+    # Default Gitaly authentication token. Can be overriden per storage. Can
+    # be left blank when Gitaly is running locally on a Unix socket, which
+    # is the normal way to deploy Gitaly.
+    token:
 
   #
   # 4. Advanced settings
@@ -469,6 +473,7 @@ production: &base
       default:
         path: /home/git/repositories/
         gitaly_address: unix:/home/git/gitlab/tmp/sockets/private/gitaly.socket # TCP connections are supported too (e.g. tcp://host:port)
+        # gitaly_token: 'special token' # Optional: override global gitaly.token for this storage.
 
   ## Backup settings
   backup:
@@ -594,6 +599,7 @@ test:
         gitaly_address: unix:tmp/tests/gitaly/gitaly.socket
   gitaly:
     enabled: true
+    token: secret
   backup:
     path: tmp/tests/backups
   gitlab_shell:
diff --git a/config/initializers/8_metrics.rb b/config/initializers/8_metrics.rb
index 508b886d6a0059da659499446897420708f05f6b..a0a63ddf8f05acf5e0ce5fe11ff6b9800b02d824 100644
--- a/config/initializers/8_metrics.rb
+++ b/config/initializers/8_metrics.rb
@@ -154,8 +154,8 @@ if Gitlab::Metrics.enabled?
       ActiveRecord::Querying.public_instance_methods(false).map(&:to_s)
     )
 
-    Gitlab::Metrics::Instrumentation.
-      instrument_class_hierarchy(ActiveRecord::Base) do |klass, method|
+    Gitlab::Metrics::Instrumentation
+      .instrument_class_hierarchy(ActiveRecord::Base) do |klass, method|
         # Instrumenting the ApplicationSetting class can lead to an infinite
         # loop. Since the data is cached any way we don't really need to
         # instrument it.
diff --git a/config/karma.config.js b/config/karma.config.js
index 978850e5d709b5adcefc069a2fdfb3f69101d2f1..2f571978e0869a767198595b001c31a2c9ed4180 100644
--- a/config/karma.config.js
+++ b/config/karma.config.js
@@ -54,6 +54,16 @@ module.exports = function(config) {
       subdir: '.',
       fixWebpackSourcePaths: true
     };
+    karmaConfig.browserNoActivityTimeout = 60000; // 60 seconds
+  }
+
+  if (process.env.DEBUG) {
+    karmaConfig.logLevel = config.LOG_DEBUG;
+    process.env.CHROME_LOG_FILE = process.env.CHROME_LOG_FILE || 'chrome_debug.log';
+  }
+
+  if (process.env.CHROME_LOG_FILE) {
+    karmaConfig.customLaunchers.ChromeHeadlessCustom.flags.push('--enable-logging', '--v=1');
   }
 
   if (process.env.DEBUG) {
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 9d47425950a1f324aed2f6824ed2f08be51f0ae2..8932db138d9890a2842f20349a0e65d6d74104ef 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -2,17 +2,63 @@
 # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
 
 en:
-  hello: "Hello world"
-  errors:
-    messages:
-      label_already_exists_at_group_level: "already exists at group level for %{group}. Please choose another one."
-      wrong_size: "is the wrong size (should be %{file_size})"
-      size_too_small: "is too small (should be at least %{file_size})"
-      size_too_big: "is too big (should be at most %{file_size})"
   views:
     pagination:
       previous: "Prev"
       next: "Next"
+  date:
+    abbr_day_names:
+    - Sun
+    - Mon
+    - Tue
+    - Wed
+    - Thu
+    - Fri
+    - Sat
+    abbr_month_names:
+    -
+    - Jan
+    - Feb
+    - Mar
+    - Apr
+    - May
+    - Jun
+    - Jul
+    - Aug
+    - Sep
+    - Oct
+    - Nov
+    - Dec
+    day_names:
+    - Sunday
+    - Monday
+    - Tuesday
+    - Wednesday
+    - Thursday
+    - Friday
+    - Saturday
+    formats:
+      default: "%Y-%m-%d"
+      long: "%B %d, %Y"
+      short: "%b %d"
+    month_names:
+    -
+    - January
+    - February
+    - March
+    - April
+    - May
+    - June
+    - July
+    - August
+    - September
+    - October
+    - November
+    - December
+    order:
+    - :year
+    - :month
+    - :day
   datetime:
     time_ago_in_words:
       half_a_minute: "half a minute ago"
@@ -49,3 +95,158 @@ en:
       almost_x_years:
         one:   "almost 1 year ago"
         other: "almost %{count} years ago"
+    distance_in_words:
+      about_x_hours:
+        one: about 1 hour
+        other: about %{count} hours
+      about_x_months:
+        one: about 1 month
+        other: about %{count} months
+      about_x_years:
+        one: about 1 year
+        other: about %{count} years
+      almost_x_years:
+        one: almost 1 year
+        other: almost %{count} years
+      half_a_minute: half a minute
+      less_than_x_minutes:
+        one: less than a minute
+        other: less than %{count} minutes
+      less_than_x_seconds:
+        one: less than 1 second
+        other: less than %{count} seconds
+      over_x_years:
+        one: over 1 year
+        other: over %{count} years
+      x_days:
+        one: 1 day
+        other: "%{count} days"
+      x_minutes:
+        one: 1 minute
+        other: "%{count} minutes"
+      x_months:
+        one: 1 month
+        other: "%{count} months"
+      x_years:
+        one: 1 year
+        other: "%{count} years"
+      x_seconds:
+        one: 1 second
+        other: "%{count} seconds"
+    prompts:
+      day: Day
+      hour: Hour
+      minute: Minute
+      month: Month
+      second: Seconds
+      year: Year
+  errors:
+    format: "%{attribute} %{message}"
+    messages:
+      label_already_exists_at_group_level: "already exists at group level for %{group}. Please choose another one."
+      wrong_size: "is the wrong size (should be %{file_size})"
+      size_too_small: "is too small (should be at least %{file_size})"
+      size_too_big: "is too big (should be at most %{file_size})"
+      accepted: must be accepted
+      blank: can't be blank
+      present: must be blank
+      confirmation: doesn't match %{attribute}
+      empty: can't be empty
+      equal_to: must be equal to %{count}
+      even: must be even
+      exclusion: is reserved
+      greater_than: must be greater than %{count}
+      greater_than_or_equal_to: must be greater than or equal to %{count}
+      inclusion: is not included in the list
+      invalid: is invalid
+      less_than: must be less than %{count}
+      less_than_or_equal_to: must be less than or equal to %{count}
+      model_invalid: "Validation failed: %{errors}"
+      not_a_number: is not a number
+      not_an_integer: must be an integer
+      odd: must be odd
+      required: must exist
+      taken: has already been taken
+      too_long:
+        one: is too long (maximum is 1 character)
+        other: is too long (maximum is %{count} characters)
+      too_short:
+        one: is too short (minimum is 1 character)
+        other: is too short (minimum is %{count} characters)
+      wrong_length:
+        one: is the wrong length (should be 1 character)
+        other: is the wrong length (should be %{count} characters)
+      other_than: must be other than %{count}
+    template:
+      body: 'There were problems with the following fields:'
+      header:
+        one: 1 error prohibited this %{model} from being saved
+        other: "%{count} errors prohibited this %{model} from being saved"
+  helpers:
+    select:
+      prompt: Please select
+    submit:
+      create: Create %{model}
+      submit: Save %{model}
+      update: Update %{model}
+  number:
+    currency:
+      format:
+        delimiter: ","
+        format: "%u%n"
+        precision: 2
+        separator: "."
+        significant: false
+        strip_insignificant_zeros: false
+        unit: "$"
+    format:
+      delimiter: ","
+      precision: 3
+      separator: "."
+      significant: false
+      strip_insignificant_zeros: false
+    human:
+      decimal_units:
+        format: "%n %u"
+        units:
+          billion: Billion
+          million: Million
+          quadrillion: Quadrillion
+          thousand: Thousand
+          trillion: Trillion
+          unit: ''
+      format:
+        delimiter: ''
+        precision: 3
+        significant: true
+        strip_insignificant_zeros: true
+      storage_units:
+        format: "%n %u"
+        units:
+          byte:
+            one: Byte
+            other: Bytes
+          gb: GB
+          kb: KB
+          mb: MB
+          tb: TB
+    percentage:
+      format:
+        delimiter: ''
+        format: "%n%"
+    precision:
+      format:
+        delimiter: ''
+  support:
+    array:
+      last_word_connector: ", and "
+      two_words_connector: " and "
+      words_connector: ", "
+  time:
+    am: am
+    formats:
+      default: "%a, %d %b %Y %H:%M:%S %z"
+      long: "%B %d, %Y %H:%M"
+      short: "%d %b %H:%M"
+      timeago_tooltip: "%b %-d, %Y %-l:%M%P"
+    pm: pm
diff --git a/config/locales/es.yml b/config/locales/es.yml
index d71c6eb5047524f93bf118a116a11a5fa312f2b7..fdc52b4ae11e19743b0e64da6c5e0f8b902dedc8 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -251,4 +251,5 @@ es:
       default: "%A, %d de %B de %Y %H:%M:%S %z"
       long: "%d de %B de %Y %H:%M"
       short: "%d de %b %H:%M"
+      timeago_tooltip: "%d de %B de %Y %H:%M"
     pm: pm
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 04a25edcdb2405fcac20c3c12ad1b44f49c08082..f27b7cae3105c11b260758ec8345fd26ce1e7536 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -11,22 +11,13 @@ var WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeMod
 
 var ROOT_PATH = path.resolve(__dirname, '..');
 var IS_PRODUCTION = process.env.NODE_ENV === 'production';
-var IS_DEV_SERVER = process.argv[1].indexOf('webpack-dev-server') !== -1;
+var IS_DEV_SERVER = process.argv.join(' ').indexOf('webpack-dev-server') !== -1;
 var DEV_SERVER_HOST = process.env.DEV_SERVER_HOST || 'localhost';
 var DEV_SERVER_PORT = parseInt(process.env.DEV_SERVER_PORT, 10) || 3808;
 var DEV_SERVER_LIVERELOAD = process.env.DEV_SERVER_LIVERELOAD !== 'false';
 var WEBPACK_REPORT = process.env.WEBPACK_REPORT;
 var NO_COMPRESSION = process.env.NO_COMPRESSION;
 
-// optional dependency `node-zopfli` is unavailable on CentOS 6
-var ZOPFLI_AVAILABLE;
-try {
-  require.resolve('node-zopfli');
-  ZOPFLI_AVAILABLE = true;
-} catch(err) {
-  ZOPFLI_AVAILABLE = false;
-}
-
 var config = {
   // because sqljs requires fs.
   node: {
@@ -234,12 +225,12 @@ if (IS_PRODUCTION) {
 
   // zopfli requires a lot of compute time and is disabled in CI
   if (!NO_COMPRESSION) {
-    config.plugins.push(
-      new CompressionPlugin({
-        asset: '[path].gz[query]',
-        algorithm: ZOPFLI_AVAILABLE ? 'zopfli' : 'gzip',
-      })
-    );
+    // gracefully fall back to gzip if `node-zopfli` is unavailable (e.g. in CentOS 6)
+    try {
+      config.plugins.push(new CompressionPlugin({ algorithm: 'zopfli' }));
+    } catch(err) {
+      config.plugins.push(new CompressionPlugin({ algorithm: 'gzip' }));
+    }
   }
 }
 
diff --git a/db/fixtures/development/19_environments.rb b/db/fixtures/development/19_environments.rb
index 93214b9d3e7e1fb0fed57a0ed66ebef74bb5fa8b..c1bbc9af6d6f84d4e3cb3e408bf317edab054336 100644
--- a/db/fixtures/development/19_environments.rb
+++ b/db/fixtures/development/19_environments.rb
@@ -33,7 +33,7 @@ class Gitlab::Seeder::Environments
 
       create_deployment!(
         merge_request.source_project,
-        "review/#{merge_request.source_branch}",
+        "review/#{merge_request.source_branch.gsub(/[^a-zA-Z0-9]/, '')}",
         merge_request.source_branch,
         merge_request.diff_head_sha
       )
diff --git a/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb b/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb
index e5292cfba079cba9f6279b1879dc625da867860d..c0cb9d7874890262df43f8d40ed88288d20d59bb 100644
--- a/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb
+++ b/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb
@@ -6,9 +6,9 @@ class MigrateProcessCommitWorkerJobs < ActiveRecord::Migration
 
   class Project < ActiveRecord::Base
     def self.find_including_path(id)
-      select("projects.*, CONCAT(namespaces.path, '/', projects.path) AS path_with_namespace").
-        joins('INNER JOIN namespaces ON namespaces.id = projects.namespace_id').
-        find_by(id: id)
+      select("projects.*, CONCAT(namespaces.path, '/', projects.path) AS path_with_namespace")
+        .joins('INNER JOIN namespaces ON namespaces.id = projects.namespace_id')
+        .find_by(id: id)
     end
 
     def repository_storage_path
diff --git a/db/migrate/20161207231620_fixup_environment_name_uniqueness.rb b/db/migrate/20161207231620_fixup_environment_name_uniqueness.rb
index a20a903a7522cd83a1beba7c2e415308cd3141d1..f73e4f6c99b19762a024fa410f77fabe0432d5e9 100644
--- a/db/migrate/20161207231620_fixup_environment_name_uniqueness.rb
+++ b/db/migrate/20161207231620_fixup_environment_name_uniqueness.rb
@@ -8,11 +8,11 @@ class FixupEnvironmentNameUniqueness < ActiveRecord::Migration
     environments = Arel::Table.new(:environments)
 
     # Get all [project_id, name] pairs that occur more than once
-    finder_sql = environments.
-      group(environments[:project_id], environments[:name]).
-      having(Arel.sql("COUNT(1)").gt(1)).
-      project(environments[:project_id], environments[:name]).
-      to_sql
+    finder_sql = environments
+      .group(environments[:project_id], environments[:name])
+      .having(Arel.sql("COUNT(1)").gt(1))
+      .project(environments[:project_id], environments[:name])
+      .to_sql
 
     conflicting = connection.exec_query(finder_sql)
 
@@ -28,12 +28,12 @@ class FixupEnvironmentNameUniqueness < ActiveRecord::Migration
   # Rename conflicting environments by appending "-#{id}" to all but the first
   def fix_duplicates(project_id, name)
     environments = Arel::Table.new(:environments)
-    finder_sql = environments.
-      where(environments[:project_id].eq(project_id)).
-      where(environments[:name].eq(name)).
-      order(environments[:id].asc).
-      project(environments[:id], environments[:name]).
-      to_sql
+    finder_sql = environments
+      .where(environments[:project_id].eq(project_id))
+      .where(environments[:name].eq(name))
+      .order(environments[:id].asc)
+      .project(environments[:id], environments[:name])
+      .to_sql
 
     # Now we have the data for all the conflicting rows
     conflicts = connection.exec_query(finder_sql).rows
@@ -41,11 +41,11 @@ class FixupEnvironmentNameUniqueness < ActiveRecord::Migration
 
     conflicts.each do |id, name|
       update_sql =
-        Arel::UpdateManager.new(ActiveRecord::Base).
-          table(environments).
-          set(environments[:name] => name + "-" + id.to_s).
-          where(environments[:id].eq(id)).
-          to_sql
+        Arel::UpdateManager.new(ActiveRecord::Base)
+          .table(environments)
+          .set(environments[:name] => name + "-" + id.to_s)
+          .where(environments[:id].eq(id))
+          .to_sql
 
       connection.exec_update(update_sql, self.class.name, [])
     end
diff --git a/db/migrate/20161207231626_add_environment_slug.rb b/db/migrate/20161207231626_add_environment_slug.rb
index 8e98ee5b9ba18fd3488fe2bf4e1d256637e971b2..83cdd484c4cd0b97e19af2bdfadb22587cc96133 100644
--- a/db/migrate/20161207231626_add_environment_slug.rb
+++ b/db/migrate/20161207231626_add_environment_slug.rb
@@ -19,10 +19,10 @@ class AddEnvironmentSlug < ActiveRecord::Migration
     finder = environments.project(:id, :name)
 
     connection.exec_query(finder.to_sql).rows.each do |id, name|
-      updater = Arel::UpdateManager.new(ActiveRecord::Base).
-        table(environments).
-        set(environments[:slug] => generate_slug(name)).
-        where(environments[:id].eq(id))
+      updater = Arel::UpdateManager.new(ActiveRecord::Base)
+        .table(environments)
+        .set(environments[:slug] => generate_slug(name))
+        .where(environments[:id].eq(id))
 
       connection.exec_update(updater.to_sql, self.class.name, [])
     end
diff --git a/db/migrate/20170316163800_rename_system_namespaces.rb b/db/migrate/20170316163800_rename_system_namespaces.rb
index b5408fbf1122ab8109ec33fb7e81148bca5569f2..9e9fb5ac2256dd64697d7580ebd05092ad695898 100644
--- a/db/migrate/20170316163800_rename_system_namespaces.rb
+++ b/db/migrate/20170316163800_rename_system_namespaces.rb
@@ -159,9 +159,9 @@ class RenameSystemNamespaces < ActiveRecord::Migration
   end
 
   def system_namespace
-    @system_namespace ||= Namespace.where(parent_id: nil).
-                            where(arel_table[:path].matches(system_namespace_path)).
-                            first
+    @system_namespace ||= Namespace.where(parent_id: nil)
+                            .where(arel_table[:path].matches(system_namespace_path))
+                            .first
   end
 
   def system_namespace_path
@@ -209,8 +209,8 @@ class RenameSystemNamespaces < ActiveRecord::Migration
   end
 
   def repo_paths_for_namespace(namespace)
-    projects_for_namespace(namespace).distinct.
-      select(:repository_storage).map(&:repository_storage_path)
+    projects_for_namespace(namespace).distinct
+      .select(:repository_storage).map(&:repository_storage_path)
   end
 
   def uploads_dir
diff --git a/db/migrate/20170503140202_turn_nested_groups_into_regular_groups_for_mysql.rb b/db/migrate/20170503140202_turn_nested_groups_into_regular_groups_for_mysql.rb
index c67690642c98b2a450c5e6405f4f3c7e32ab84a3..33908ae115630efbeec47461c00969898edced29 100644
--- a/db/migrate/20170503140202_turn_nested_groups_into_regular_groups_for_mysql.rb
+++ b/db/migrate/20170503140202_turn_nested_groups_into_regular_groups_for_mysql.rb
@@ -87,8 +87,8 @@ class TurnNestedGroupsIntoRegularGroupsForMysql < ActiveRecord::Migration
     while current&.parent_id
       # We're using find_by(id: ...) here to deal with cases where the
       # parent_id may point to a missing row.
-      current = Namespace.unscoped.select([:id, :parent_id]).
-        find_by(id: current.parent_id)
+      current = Namespace.unscoped.select([:id, :parent_id])
+        .find_by(id: current.parent_id)
 
       ancestors << current.id if current
     end
@@ -99,11 +99,11 @@ class TurnNestedGroupsIntoRegularGroupsForMysql < ActiveRecord::Migration
   # Returns a relation containing all the members that have access to any of
   # the current namespace's parent namespaces.
   def all_members_for(namespace)
-    Member.
-      unscoped.
-      select(['user_id', 'MAX(access_level) AS access_level']).
-      where(source_type: 'Namespace', source_id: ancestors_for(namespace)).
-      group(:user_id)
+    Member
+      .unscoped
+      .select(['user_id', 'MAX(access_level) AS access_level'])
+      .where(source_type: 'Namespace', source_id: ancestors_for(namespace))
+      .group(:user_id)
   end
 
   def bulk_insert_members(rows)
diff --git a/db/migrate/20170526185602_add_stage_id_to_ci_builds.rb b/db/migrate/20170526185602_add_stage_id_to_ci_builds.rb
index d5675d5828bfe0c3ef89d5e64d125f2b6be53a9a..d27cba76d8199e81ed03e52668dceb91912816b5 100644
--- a/db/migrate/20170526185602_add_stage_id_to_ci_builds.rb
+++ b/db/migrate/20170526185602_add_stage_id_to_ci_builds.rb
@@ -3,19 +3,11 @@ class AddStageIdToCiBuilds < ActiveRecord::Migration
 
   DOWNTIME = false
 
-  disable_ddl_transaction!
-
   def up
     add_column :ci_builds, :stage_id, :integer
-
-    add_concurrent_foreign_key :ci_builds, :ci_stages, column: :stage_id, on_delete: :cascade
-    add_concurrent_index :ci_builds, :stage_id
   end
 
   def down
-    remove_foreign_key :ci_builds, column: :stage_id
-    remove_concurrent_index :ci_builds, :stage_id
-
     remove_column :ci_builds, :stage_id, :integer
   end
 end
diff --git a/db/migrate/20170608171156_create_merge_request_diff_files.rb b/db/migrate/20170608171156_create_merge_request_diff_files.rb
new file mode 100644
index 0000000000000000000000000000000000000000..bf0c0d29adcb4add28b4201b71a322aac281d478
--- /dev/null
+++ b/db/migrate/20170608171156_create_merge_request_diff_files.rb
@@ -0,0 +1,22 @@
+class CreateMergeRequestDiffFiles < ActiveRecord::Migration
+  DOWNTIME = false
+
+  disable_ddl_transaction!
+
+  def change
+    create_table :merge_request_diff_files, id: false do |t|
+      t.belongs_to :merge_request_diff, null: false, foreign_key: { on_delete: :cascade }
+      t.integer :relative_order, null: false
+      t.boolean :new_file, null: false
+      t.boolean :renamed_file, null: false
+      t.boolean :deleted_file, null: false
+      t.boolean :too_large, null: false
+      t.string :a_mode, null: false
+      t.string :b_mode, null: false
+      t.text :new_path, null: false
+      t.text :old_path, null: false
+      t.text :diff, null: false
+      t.index [:merge_request_diff_id, :relative_order], name: 'index_merge_request_diff_files_on_mr_diff_id_and_order', unique: true
+    end
+  end
+end
diff --git a/db/migrate/20170614115405_merge_request_diff_file_limits_to_mysql.rb b/db/migrate/20170614115405_merge_request_diff_file_limits_to_mysql.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4c1cf08aa067282ca21d72a1db61e3ea597053d6
--- /dev/null
+++ b/db/migrate/20170614115405_merge_request_diff_file_limits_to_mysql.rb
@@ -0,0 +1 @@
+require_relative 'merge_request_diff_file_limits_to_mysql'
diff --git a/db/migrate/20170619144837_add_index_for_head_pipeline_merge_request.rb b/db/migrate/20170619144837_add_index_for_head_pipeline_merge_request.rb
new file mode 100644
index 0000000000000000000000000000000000000000..02863bee0825533cda5d68e654d29dae5a7978b7
--- /dev/null
+++ b/db/migrate/20170619144837_add_index_for_head_pipeline_merge_request.rb
@@ -0,0 +1,15 @@
+class AddIndexForHeadPipelineMergeRequest < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  DOWNTIME = false
+
+  disable_ddl_transaction!
+
+  def up
+    add_concurrent_index :merge_requests, :head_pipeline_id
+  end
+
+  def down
+    remove_concurrent_index :merge_requests, :head_pipeline_id if index_exists?(:merge_requests, :head_pipeline_id)
+  end
+end
diff --git a/db/migrate/merge_request_diff_file_limits_to_mysql.rb b/db/migrate/merge_request_diff_file_limits_to_mysql.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3958380e4b901e9f62c9969a4ed78ab11500673e
--- /dev/null
+++ b/db/migrate/merge_request_diff_file_limits_to_mysql.rb
@@ -0,0 +1,12 @@
+class MergeRequestDiffFileLimitsToMysql < ActiveRecord::Migration
+  DOWNTIME = false
+
+  def up
+    return unless Gitlab::Database.mysql?
+
+    change_column :merge_request_diff_files, :diff, :text, limit: 2147483647
+  end
+
+  def down
+  end
+end
diff --git a/db/post_migrate/20161109150329_fix_project_records_with_invalid_visibility.rb b/db/post_migrate/20161109150329_fix_project_records_with_invalid_visibility.rb
index 14b5ef476f0cbbf8a5d73ed09b09081073e6341d..69007b8e8ed29e460192408f870cf17cea9b41ce 100644
--- a/db/post_migrate/20161109150329_fix_project_records_with_invalid_visibility.rb
+++ b/db/post_migrate/20161109150329_fix_project_records_with_invalid_visibility.rb
@@ -13,13 +13,13 @@ class FixProjectRecordsWithInvalidVisibility < ActiveRecord::Migration
     namespaces = Arel::Table.new(:namespaces)
 
     finder_sql =
-      projects.
-        join(namespaces, Arel::Nodes::InnerJoin).
-        on(projects[:namespace_id].eq(namespaces[:id])).
-        where(projects[:visibility_level].gt(namespaces[:visibility_level])).
-        project(projects[:id], namespaces[:visibility_level]).
-        take(BATCH_SIZE).
-        to_sql
+      projects
+        .join(namespaces, Arel::Nodes::InnerJoin)
+        .on(projects[:namespace_id].eq(namespaces[:id]))
+        .where(projects[:visibility_level].gt(namespaces[:visibility_level]))
+        .project(projects[:id], namespaces[:visibility_level])
+        .take(BATCH_SIZE)
+        .to_sql
 
     # Update matching rows in batches. Each batch can cause up to 3 UPDATE
     # statements, in addition to the SELECT: one per visibility_level
@@ -33,10 +33,10 @@ class FixProjectRecordsWithInvalidVisibility < ActiveRecord::Migration
       end
 
       updates.each do |visibility_level, project_ids|
-        updater = Arel::UpdateManager.new(ActiveRecord::Base).
-          table(projects).
-          set(projects[:visibility_level] => visibility_level).
-          where(projects[:id].in(project_ids))
+        updater = Arel::UpdateManager.new(ActiveRecord::Base)
+          .table(projects)
+          .set(projects[:visibility_level] => visibility_level)
+          .where(projects[:id].in(project_ids))
 
         ActiveRecord::Base.connection.exec_update(updater.to_sql, self.class.name, [])
       end
diff --git a/db/post_migrate/20161221153951_rename_reserved_project_names.rb b/db/post_migrate/20161221153951_rename_reserved_project_names.rb
index 49a6bc884a8320f1fc4e9c335c91ec9161e80952..d322844e2fd99ba157150a446eeedab6c080d8c7 100644
--- a/db/post_migrate/20161221153951_rename_reserved_project_names.rb
+++ b/db/post_migrate/20161221153951_rename_reserved_project_names.rb
@@ -79,17 +79,17 @@ class RenameReservedProjectNames < ActiveRecord::Migration
   private
 
   def reserved_projects
-    Project.unscoped.
-      includes(:namespace).
-      where('EXISTS (SELECT 1 FROM namespaces WHERE projects.namespace_id = namespaces.id)').
-      where('projects.path' => KNOWN_PATHS)
+    Project.unscoped
+      .includes(:namespace)
+      .where('EXISTS (SELECT 1 FROM namespaces WHERE projects.namespace_id = namespaces.id)')
+      .where('projects.path' => KNOWN_PATHS)
   end
 
   def route_exists?(full_path)
     quoted_path = ActiveRecord::Base.connection.quote_string(full_path)
 
-    ActiveRecord::Base.connection.
-      select_all("SELECT id, path FROM routes WHERE path = '#{quoted_path}'").present?
+    ActiveRecord::Base.connection
+      .select_all("SELECT id, path FROM routes WHERE path = '#{quoted_path}'").present?
   end
 
   # Adds number to the end of the path that is not taken by other route
diff --git a/db/post_migrate/20170104150317_requeue_pending_delete_projects.rb b/db/post_migrate/20170104150317_requeue_pending_delete_projects.rb
index f399950bd5e4981e214b31bc0068b922dccc6d82..d7be004d47f9032c8f62c3aea140c79a7f4f2eab 100644
--- a/db/post_migrate/20170104150317_requeue_pending_delete_projects.rb
+++ b/db/post_migrate/20170104150317_requeue_pending_delete_projects.rb
@@ -39,11 +39,11 @@ class RequeuePendingDeleteProjects < ActiveRecord::Migration
 
   def find_batch
     projects = Arel::Table.new(:projects)
-    projects.project(projects[:id]).
-      where(projects[:pending_delete].eq(true)).
-      where(projects[:namespace_id].not_eq(nil)).
-      skip(@offset * BATCH_SIZE).
-      take(BATCH_SIZE).
-      to_sql
+    projects.project(projects[:id])
+      .where(projects[:pending_delete].eq(true))
+      .where(projects[:namespace_id].not_eq(nil))
+      .skip(@offset * BATCH_SIZE)
+      .take(BATCH_SIZE)
+      .to_sql
   end
 end
diff --git a/db/post_migrate/20170106142508_fill_authorized_projects.rb b/db/post_migrate/20170106142508_fill_authorized_projects.rb
index 314c8440c8b229135add22caef73f989874cabf2..0ca20587981c10924635d0f0fc759d93298480b9 100644
--- a/db/post_migrate/20170106142508_fill_authorized_projects.rb
+++ b/db/post_migrate/20170106142508_fill_authorized_projects.rb
@@ -15,8 +15,8 @@ class FillAuthorizedProjects < ActiveRecord::Migration
   disable_ddl_transaction!
 
   def up
-    relation = User.select(:id).
-      where('authorized_projects_populated IS NOT TRUE')
+    relation = User.select(:id)
+      .where('authorized_projects_populated IS NOT TRUE')
 
     relation.find_in_batches(batch_size: 1_000) do |rows|
       args = rows.map { |row| [row.id] }
diff --git a/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb b/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb
index 44c688fa134ca7e9a497d8379c846f6b97c972aa..6a49450cc50d380536924ec37a26a6f4439a33d2 100644
--- a/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb
+++ b/db/post_migrate/20170313133418_rename_more_reserved_project_names.rb
@@ -21,17 +21,17 @@ class RenameMoreReservedProjectNames < ActiveRecord::Migration
   private
 
   def reserved_projects
-    Project.unscoped.
-      includes(:namespace).
-      where('EXISTS (SELECT 1 FROM namespaces WHERE projects.namespace_id = namespaces.id)').
-      where('projects.path' => KNOWN_PATHS)
+    Project.unscoped
+      .includes(:namespace)
+      .where('EXISTS (SELECT 1 FROM namespaces WHERE projects.namespace_id = namespaces.id)')
+      .where('projects.path' => KNOWN_PATHS)
   end
 
   def route_exists?(full_path)
     quoted_path = ActiveRecord::Base.connection.quote_string(full_path)
 
-    ActiveRecord::Base.connection.
-      select_all("SELECT id, path FROM routes WHERE path = '#{quoted_path}'").present?
+    ActiveRecord::Base.connection
+      .select_all("SELECT id, path FROM routes WHERE path = '#{quoted_path}'").present?
   end
 
   # Adds number to the end of the path that is not taken by other route
diff --git a/db/post_migrate/20170324160416_migrate_user_activities_to_users_last_activity_on.rb b/db/post_migrate/20170324160416_migrate_user_activities_to_users_last_activity_on.rb
index 9ad36482c8a3b9d88c1f8aeeae80aa196e3b74e2..397a9a2d28ea6cec8469d0fa3565c0a8a9094ce2 100644
--- a/db/post_migrate/20170324160416_migrate_user_activities_to_users_last_activity_on.rb
+++ b/db/post_migrate/20170324160416_migrate_user_activities_to_users_last_activity_on.rb
@@ -38,11 +38,11 @@ class MigrateUserActivitiesToUsersLastActivityOn < ActiveRecord::Migration
     activities = activities(day.at_beginning_of_day, day.at_end_of_day, page: page)
 
     update_sql =
-      Arel::UpdateManager.new(ActiveRecord::Base).
-        table(users_table).
-        set(users_table[:last_activity_on] => day.to_date).
-        where(users_table[:username].in(activities.map(&:first))).
-        to_sql
+      Arel::UpdateManager.new(ActiveRecord::Base)
+        .table(users_table)
+        .set(users_table[:last_activity_on] => day.to_date)
+        .where(users_table[:username].in(activities.map(&:first)))
+        .to_sql
 
     connection.exec_update(update_sql, self.class.name, [])
 
diff --git a/db/post_migrate/20170502070007_enable_auto_cancel_pending_pipelines_for_all.rb b/db/post_migrate/20170502070007_enable_auto_cancel_pending_pipelines_for_all.rb
index 3c13a3d2518b6a43178bc6a7bdfe5e4c565bb23d..765daa0a3470346f8e6c05d35ea6d3efefd5b5ec 100644
--- a/db/post_migrate/20170502070007_enable_auto_cancel_pending_pipelines_for_all.rb
+++ b/db/post_migrate/20170502070007_enable_auto_cancel_pending_pipelines_for_all.rb
@@ -7,6 +7,8 @@ class EnableAutoCancelPendingPipelinesForAll < ActiveRecord::Migration
   DOWNTIME = false
 
   def up
+    disable_statement_timeout
+
     update_column_in_batches(:projects, :auto_cancel_pending_pipelines, 1)
   end
 
diff --git a/db/post_migrate/20170502101023_cleanup_namespaceless_pending_delete_projects.rb b/db/post_migrate/20170502101023_cleanup_namespaceless_pending_delete_projects.rb
index ce52de91cdd502d8028ab9a7e4fd24fbe3815f92..c1e64f201090f3a21ab7da6d910a044c094b8d5d 100644
--- a/db/post_migrate/20170502101023_cleanup_namespaceless_pending_delete_projects.rb
+++ b/db/post_migrate/20170502101023_cleanup_namespaceless_pending_delete_projects.rb
@@ -37,11 +37,11 @@ class CleanupNamespacelessPendingDeleteProjects < ActiveRecord::Migration
 
   def find_batch
     projects = Arel::Table.new(:projects)
-    projects.project(projects[:id]).
-      where(projects[:pending_delete].eq(true)).
-      where(projects[:namespace_id].eq(nil)).
-      skip(@offset * BATCH_SIZE).
-      take(BATCH_SIZE).
-      to_sql
+    projects.project(projects[:id])
+      .where(projects[:pending_delete].eq(true))
+      .where(projects[:namespace_id].eq(nil))
+      .skip(@offset * BATCH_SIZE)
+      .take(BATCH_SIZE)
+      .to_sql
   end
 end
diff --git a/db/post_migrate/20170508170547_add_head_pipeline_for_each_merge_request.rb b/db/post_migrate/20170508170547_add_head_pipeline_for_each_merge_request.rb
index bc3850c0c232d3ca627640ecde5eaf391d0b1cce..0a4a2d3867a12a44e3f4839f43547db4ed91daee 100644
--- a/db/post_migrate/20170508170547_add_head_pipeline_for_each_merge_request.rb
+++ b/db/post_migrate/20170508170547_add_head_pipeline_for_each_merge_request.rb
@@ -9,11 +9,11 @@ class AddHeadPipelineForEachMergeRequest < ActiveRecord::Migration
     pipelines = Arel::Table.new(:ci_pipelines)
     merge_requests = Arel::Table.new(:merge_requests)
 
-    head_id = pipelines.
-      project(Arel::Nodes::NamedFunction.new('max', [pipelines[:id]])).
-      from(pipelines).
-      where(pipelines[:ref].eq(merge_requests[:source_branch])).
-      where(pipelines[:project_id].eq(merge_requests[:source_project_id]))
+    head_id = pipelines
+      .project(Arel::Nodes::NamedFunction.new('max', [pipelines[:id]]))
+      .from(pipelines)
+      .where(pipelines[:ref].eq(merge_requests[:source_branch]))
+      .where(pipelines[:project_id].eq(merge_requests[:source_project_id]))
 
     sub_query = Arel::Nodes::SqlLiteral.new(Arel::Nodes::Grouping.new(head_id).to_sql)
 
diff --git a/db/post_migrate/20170526185901_remove_stage_id_index_from_builds.rb b/db/post_migrate/20170526185901_remove_stage_id_index_from_builds.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3879cf9133beed23f1d0e7c8f2fcf7278063af45
--- /dev/null
+++ b/db/post_migrate/20170526185901_remove_stage_id_index_from_builds.rb
@@ -0,0 +1,18 @@
+class RemoveStageIdIndexFromBuilds < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  DOWNTIME = false
+
+  disable_ddl_transaction!
+
+  def up
+    if index_exists?(:ci_builds, :stage_id)
+      remove_foreign_key(:ci_builds, column: :stage_id)
+      remove_concurrent_index(:ci_builds, :stage_id)
+    end
+  end
+
+  def down
+    # noop
+  end
+end
diff --git a/db/post_migrate/20170526185921_migrate_build_stage_reference.rb b/db/post_migrate/20170526185921_migrate_build_stage_reference.rb
index 797e106cae419de19ce820629b5c2f70f569d4ad..98c32d8284c7b3855b7339c131c4308cc20e5f57 100644
--- a/db/post_migrate/20170526185921_migrate_build_stage_reference.rb
+++ b/db/post_migrate/20170526185921_migrate_build_stage_reference.rb
@@ -3,23 +3,17 @@ class MigrateBuildStageReference < ActiveRecord::Migration
 
   DOWNTIME = false
 
-  def up
-    disable_statement_timeout
-
-    stage_id = Arel.sql <<-SQL.strip_heredoc
-      (SELECT id FROM ci_stages
-         WHERE ci_stages.pipeline_id = ci_builds.commit_id
-           AND ci_stages.name = ci_builds.stage)
-    SQL
+  ##
+  # This is an empty migration, content has been moved to a new one:
+  # post migrate 20170526190000 MigrateBuildStageReferenceAgain
+  #
+  # See gitlab-org/gitlab-ce!12337 for more details.
 
-    update_column_in_batches(:ci_builds, :stage_id, stage_id) do |table, query|
-      query.where(table[:stage_id].eq(nil))
-    end
+  def up
+    # noop
   end
 
   def down
-    disable_statement_timeout
-
-    update_column_in_batches(:ci_builds, :stage_id, nil)
+    # noop
   end
 end
diff --git a/db/post_migrate/20170526190000_migrate_build_stage_reference_again.rb b/db/post_migrate/20170526190000_migrate_build_stage_reference_again.rb
new file mode 100644
index 0000000000000000000000000000000000000000..97cb242415dc3cdddcb33011279b04bd62d0b414
--- /dev/null
+++ b/db/post_migrate/20170526190000_migrate_build_stage_reference_again.rb
@@ -0,0 +1,27 @@
+class MigrateBuildStageReferenceAgain < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  DOWNTIME = false
+
+  disable_ddl_transaction!
+
+  def up
+    disable_statement_timeout
+
+    stage_id = Arel.sql <<-SQL.strip_heredoc
+      (SELECT id FROM ci_stages
+         WHERE ci_stages.pipeline_id = ci_builds.commit_id
+           AND ci_stages.name = ci_builds.stage)
+    SQL
+
+    update_column_in_batches(:ci_builds, :stage_id, stage_id) do |table, query|
+      query.where(table[:stage_id].eq(nil))
+    end
+  end
+
+  def down
+    disable_statement_timeout
+
+    update_column_in_batches(:ci_builds, :stage_id, nil)
+  end
+end
diff --git a/db/post_migrate/20170609183112_remove_position_from_issuables.rb b/db/post_migrate/20170609183112_remove_position_from_issuables.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4caaa2e83e8f47b8497ba2b663d205ae40d84b0a
--- /dev/null
+++ b/db/post_migrate/20170609183112_remove_position_from_issuables.rb
@@ -0,0 +1,8 @@
+class RemovePositionFromIssuables < ActiveRecord::Migration
+  DOWNTIME = false
+
+  def change
+    remove_column :issues, :position, :integer
+    remove_column :merge_requests, :position, :integer
+  end
+end
diff --git a/db/post_migrate/20170621102400_add_stage_id_index_to_builds.rb b/db/post_migrate/20170621102400_add_stage_id_index_to_builds.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7d6609b18bf36243088fe6048535080f0b05314a
--- /dev/null
+++ b/db/post_migrate/20170621102400_add_stage_id_index_to_builds.rb
@@ -0,0 +1,21 @@
+class AddStageIdIndexToBuilds < ActiveRecord::Migration
+  include Gitlab::Database::MigrationHelpers
+
+  DOWNTIME = false
+
+  disable_ddl_transaction!
+
+  def up
+    unless index_exists?(:ci_builds, :stage_id)
+      add_concurrent_foreign_key(:ci_builds, :ci_stages, column: :stage_id, on_delete: :cascade)
+      add_concurrent_index(:ci_builds, :stage_id)
+    end
+  end
+
+  def down
+    if index_exists?(:ci_builds, :stage_id)
+      remove_foreign_key(:ci_builds, column: :stage_id)
+      remove_concurrent_index(:ci_builds, :stage_id)
+    end
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 956ca2278f48f424e09c646756dfc56715624a20..028556bdccfda9021b016d2ee42eeca17cb4e2bd 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 20170607121233) do
+ActiveRecord::Schema.define(version: 20170621102400) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -547,7 +547,6 @@ ActiveRecord::Schema.define(version: 20170607121233) do
     t.integer "project_id"
     t.datetime "created_at"
     t.datetime "updated_at"
-    t.integer "position", default: 0
     t.string "branch_name"
     t.text "description"
     t.integer "milestone_id"
@@ -693,6 +692,22 @@ ActiveRecord::Schema.define(version: 20170607121233) do
   add_index "members", ["source_id", "source_type"], name: "index_members_on_source_id_and_source_type", using: :btree
   add_index "members", ["user_id"], name: "index_members_on_user_id", using: :btree
 
+  create_table "merge_request_diff_files", id: false, force: :cascade do |t|
+    t.integer "merge_request_diff_id", null: false
+    t.integer "relative_order", null: false
+    t.boolean "new_file", null: false
+    t.boolean "renamed_file", null: false
+    t.boolean "deleted_file", null: false
+    t.boolean "too_large", null: false
+    t.string "a_mode", null: false
+    t.string "b_mode", null: false
+    t.text "new_path", null: false
+    t.text "old_path", null: false
+    t.text "diff", null: false
+  end
+
+  add_index "merge_request_diff_files", ["merge_request_diff_id", "relative_order"], name: "index_merge_request_diff_files_on_mr_diff_id_and_order", unique: true, using: :btree
+
   create_table "merge_request_diffs", force: :cascade do |t|
     t.string "state"
     t.text "st_commits"
@@ -738,7 +753,6 @@ ActiveRecord::Schema.define(version: 20170607121233) do
     t.integer "target_project_id", null: false
     t.integer "iid"
     t.text "description"
-    t.integer "position", default: 0
     t.datetime "locked_at"
     t.integer "updated_by_id"
     t.text "merge_error"
@@ -763,6 +777,7 @@ ActiveRecord::Schema.define(version: 20170607121233) do
   add_index "merge_requests", ["created_at"], name: "index_merge_requests_on_created_at", using: :btree
   add_index "merge_requests", ["deleted_at"], name: "index_merge_requests_on_deleted_at", using: :btree
   add_index "merge_requests", ["description"], name: "index_merge_requests_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
+  add_index "merge_requests", ["head_pipeline_id"], name: "index_merge_requests_on_head_pipeline_id", using: :btree
   add_index "merge_requests", ["milestone_id"], name: "index_merge_requests_on_milestone_id", using: :btree
   add_index "merge_requests", ["source_branch"], name: "index_merge_requests_on_source_branch", using: :btree
   add_index "merge_requests", ["source_project_id"], name: "index_merge_requests_on_source_project_id", using: :btree
@@ -1532,6 +1547,7 @@ ActiveRecord::Schema.define(version: 20170607121233) do
   add_foreign_key "labels", "namespaces", column: "group_id", on_delete: :cascade
   add_foreign_key "lists", "boards"
   add_foreign_key "lists", "labels"
+  add_foreign_key "merge_request_diff_files", "merge_request_diffs", on_delete: :cascade
   add_foreign_key "merge_request_metrics", "ci_pipelines", column: "pipeline_id", on_delete: :cascade
   add_foreign_key "merge_request_metrics", "merge_requests", on_delete: :cascade
   add_foreign_key "merge_requests_closing_issues", "issues", on_delete: :cascade
diff --git a/doc/README.md b/doc/README.md
index 775fffab2dd58518cdf7bc132b04be3ff06feda5..ab8ea192a264f6676c55e03463bbb659fc7b9a57 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -24,7 +24,7 @@ Shortcuts to GitLab's most visited docs:
 - [GitLab Workflow](workflow/README.md): Enhance your workflow with the best of GitLab Workflow.
   - See also [GitLab Workflow - an overview](https://about.gitlab.com/2016/10/25/gitlab-workflow-an-overview/).
 - [GitLab Markdown](user/markdown.md): GitLab's advanced formatting system (GitLab Flavored Markdown).
-- [GitLab Slash Commands](user/project/slash_commands.md): Textual shortcuts for common actions on issues or merge requests that are usually done by clicking buttons or dropdowns in GitLab's UI.
+- [GitLab Quick Actions](user/project/quick_actions.md): Textual shortcuts for common actions on issues or merge requests that are usually done by clicking buttons or dropdowns in GitLab's UI.
 
 ### User account
 
diff --git a/doc/administration/high_availability/nfs.md b/doc/administration/high_availability/nfs.md
index d8e76d6ab94c3ecbf24b7153503374da1786c25b..bd6b7327aedbddd355797dded50f37779b16b6aa 100644
--- a/doc/administration/high_availability/nfs.md
+++ b/doc/administration/high_availability/nfs.md
@@ -1,12 +1,35 @@
 # NFS
 
-## Required NFS Server features
+You can view information and options set for each of the mounted NFS file
+systems by running `sudo nfsstat -m`.
+
+## NFS Server features
+
+### Required features
 
 **File locking**: GitLab **requires** advisory file locking, which is only
 supported natively in NFS version 4. NFSv3 also supports locking as long as
 Linux Kernel 2.6.5+ is used. We recommend using version 4 and do not
 specifically test NFSv3.
 
+### Recommended options
+
+When you define your NFS exports, we recommend you also add the following
+options:
+
+- `no_root_squash` - NFS normally changes the `root` user to `nobody`. This is
+  a good security measure when NFS shares will be accessed by many different
+  users. However, in this case only GitLab will use the NFS share so it
+  is safe. GitLab recommends the `no_root_squash` setting because we need to
+  manage file permissions automatically. Without the setting you may receive
+  errors when the Omnibus package tries to alter permissions. Note that GitLab
+  and other bundled components do **not** run as `root` but as non-privileged
+  users. The recommendation for `no_root_squash` is to allow the Omnibus package
+  to set ownership and permissions on files, as needed.
+- `sync` - Force synchronous behavior. Default is asynchronous and under certain
+  circumstances it could lead to data loss if a failure occurs before data has
+  synced.
+
 ## AWS Elastic File System
 
 GitLab does not recommend using AWS Elastic File System (EFS).
@@ -26,27 +49,10 @@ GitLab does not recommend using EFS with GitLab.
 For more details on another person's experience with EFS, see
 [Amazon's Elastic File System: Burst Credits](https://www.rawkode.io/2017/04/amazons-elastic-file-system-burst-credits/)
 
-### Recommended options
-
-When you define your NFS exports, we recommend you also add the following
-options:
-
-- `no_root_squash` - NFS normally changes the `root` user to `nobody`. This is
-  a good security measure when NFS shares will be accessed by many different
-  users. However, in this case only GitLab will use the NFS share so it
-  is safe. GitLab recommends the `no_root_squash` setting because we need to
-  manage file permissions automatically. Without the setting you may receive
-  errors when the Omnibus package tries to alter permissions. Note that GitLab
-  and other bundled components do **not** run as `root` but as non-privileged
-  users. The recommendation for `no_root_squash` is to allow the Omnibus package
-  to set ownership and permissions on files, as needed.
-- `sync` - Force synchronous behavior. Default is asynchronous and under certain
-  circumstances it could lead to data loss if a failure occurs before data has
-  synced.
-
 ## NFS Client mount options
 
-Below is an example of an NFS mount point we use on GitLab.com:
+Below is an example of an NFS mount point defined in `/etc/fstab` we use on
+GitLab.com:
 
 ```
 10.1.1.1:/var/opt/gitlab/git-data /var/opt/gitlab/git-data nfs4 defaults,soft,rsize=1048576,wsize=1048576,noatime,nobootwait,lookupcache=positive 0 2
diff --git a/doc/administration/high_availability/redis_source.md b/doc/administration/high_availability/redis_source.md
index 3629772b8af31e7dac09990c949f2fe11d9c4937..fe982ea83c2315545e0950a1378c163e0d059c9e 100644
--- a/doc/administration/high_availability/redis_source.md
+++ b/doc/administration/high_availability/redis_source.md
@@ -364,3 +364,4 @@ When in doubt, please read [Redis Sentinel documentation](http://redis.io/topics
 [downloads]: https://about.gitlab.com/downloads
 [restart]: ../restart_gitlab.md#installations-from-source
 [it]: https://gitlab.com/gitlab-org/gitlab-ce/uploads/c4cc8cd353604bd80315f9384035ff9e/The_Internet_IT_Crowd.png
+[resque]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/config/resque.yml.example
diff --git a/doc/administration/job_artifacts.md b/doc/administration/job_artifacts.md
index 5599435564e647dd2bfb6906db68842d668c4945..3587696225cd77bf2046afa2f0190d60747db934 100644
--- a/doc/administration/job_artifacts.md
+++ b/doc/administration/job_artifacts.md
@@ -46,7 +46,10 @@ To disable artifacts site-wide, follow the steps below.
 After a successful job, GitLab Runner uploads an archive containing the job
 artifacts to GitLab.
 
-To change the location where the artifacts are stored, follow the steps below.
+### Using local storage
+
+To change the location where the artifacts are stored locally, follow the steps
+below.
 
 ---
 
@@ -82,6 +85,13 @@ _The artifacts are stored by default in
 
 1. Save the file and [restart GitLab][] for the changes to take effect.
 
+### Using object storage
+
+In [GitLab Enterprise Edition Premium][eep] you can use an object storage like
+AWS S3 to store the artifacts.
+
+[Learn how to use the object storage option.][ee-os]
+
 ## Expiring artifacts
 
 If an expiry date is used for the artifacts, they are marked for deletion
@@ -148,3 +158,5 @@ memory and disk I/O.
 [reconfigure gitlab]: restart_gitlab.md "How to restart GitLab"
 [restart gitlab]: restart_gitlab.md "How to restart GitLab"
 [gitlab workhorse]: https://gitlab.com/gitlab-org/gitlab-workhorse "GitLab Workhorse repository"
+[ee-os]: https://docs.gitlab.com/ee/administration/job_artifacts.html#using-object-storage
+[eep]: https://about.gitlab.com/gitlab-ee/ "GitLab Enterprise Edition Premium"
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
new file mode 100644
index 0000000000000000000000000000000000000000..07c05b5a6fb229c0e26262a3264eed426f8537b7
--- /dev/null
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -0,0 +1,47 @@
+# GitLab Prometheus metrics
+
+>**Note:**
+Available since [Omnibus GitLab 9.3][29118]. Currently experimental. For installations from source
+you'll have to configure it yourself.
+
+GitLab monitors its own internal service metrics, and makes them available at the `/-/metrics` endpoint. Unlike other [Prometheus] exporters, this endpoint requires authentication as it is available on the same URL and port as user traffic.
+
+To enable the GitLab Prometheus metrics:
+
+1. Log into GitLab as an administrator, and go to the Admin area.
+1. Click on the gear, then click on Settings.
+1. Find the `Metrics - Prometheus` section, and click `Enable Prometheus Metrics`
+1. [Restart GitLab][restart] for the changes to take effect
+
+## Collecting the metrics
+
+Since the metrics endpoint is available on the same host and port as other traffic, it requires authentication. The token and URL to access is displayed on the [Health Check][health-check] page.
+
+Currently the embedded Prometheus server is not automatically configured to collect metrics from this endpoint. We recommend setting up another Prometheus server, because the embedded server configuration is overwritten one every reconfigure of GitLab. In the future this will not be required.
+
+## Metrics available
+
+In this experimental phase, only a few metrics are available:
+
+| Metric | Type | Description |
+| ------ | ---- | ----------- |
+| db_ping_timeout | Gauge | Whether or not the last database ping timed out |
+| db_ping_success | Gauge | Whether or not the last database ping succeeded |
+| db_ping_latency | Gauge | Round trip time of the database ping |
+| redis_ping_timeout | Gauge | Whether or not the last redis ping timed out |
+| redis_ping_success | Gauge | Whether or not the last redis ping succeeded |
+| redis_ping_latency | Gauge | Round trip time of the redis ping |
+| filesystem_access_latency | gauge | Latency in accessing a specific filesystem |
+| filesystem_accessible | gauge | Whether or not a specific filesystem is accessible |
+| filesystem_write_latency | gauge | Write latency of a specific filesystem |
+| filesystem_writable | gauge | Whether or not the filesystem is writable |
+| filesystem_read_latency | gauge | Read latency of a specific filesystem |
+| filesystem_readable | gauge | Whether or not the filesystem is readable |
+| user_sessions_logins | Counter | Counter of how many users have logged in | 
+
+[← Back to the main Prometheus page](index.md)
+
+[29118]: https://gitlab.com/gitlab-org/gitlab-ce/issues/29118
+[Prometheus]: https://prometheus.io
+[restart]: ../../restart_gitlab.md#omnibus-gitlab-restart
+[health-check]: ../../../user/admin_area/monitoring/health_check.md
diff --git a/doc/administration/monitoring/prometheus/gitlab_monitor_exporter.md b/doc/administration/monitoring/prometheus/gitlab_monitor_exporter.md
index edb9c911aac45ce2f1c1c540f35385aead6dc290..f68b03d1adec60f849d2c492fef816fc86923804 100644
--- a/doc/administration/monitoring/prometheus/gitlab_monitor_exporter.md
+++ b/doc/administration/monitoring/prometheus/gitlab_monitor_exporter.md
@@ -4,7 +4,7 @@
 Available since [Omnibus GitLab 8.17][1132]. For installations from source
 you'll have to install and configure it yourself.
 
-The [GitLab monitor exporter] allows you to measure various GitLab metrics.
+The [GitLab monitor exporter] allows you to measure various GitLab metrics, pulled from Redis and the database.
 
 To enable the GitLab monitor exporter:
 
diff --git a/doc/administration/monitoring/prometheus/index.md b/doc/administration/monitoring/prometheus/index.md
index b2445d1c0e511178742eda4ffdfcac95b43bd222..695fdf09a87ce7105bacb761a70ba3cdac01201b 100644
--- a/doc/administration/monitoring/prometheus/index.md
+++ b/doc/administration/monitoring/prometheus/index.md
@@ -110,6 +110,14 @@ To disable the monitoring of Kubernetes:
 1. Save the file and [reconfigure GitLab][reconfigure] for the changes to
    take effect
 
+## GitLab Prometheus metrics
+
+> Introduced as an experimental feature in GitLab 9.3.
+
+GitLab monitors its own internal service metrics, and makes them available at the `/-/metrics` endpoint. Unlike other exporters, this endpoint requires authentication as it is available on the same URL and port as user traffic.
+
+[âž” Read more about the GitLab Metrics.](gitlab_metrics.md)
+
 ## Prometheus exporters
 
 There are a number of libraries and servers which help in exporting existing
@@ -143,7 +151,7 @@ The Postgres exporter allows you to measure various PostgreSQL metrics.
 
 ### GitLab monitor exporter
 
-The GitLab monitor exporter allows you to measure various GitLab metrics.
+The GitLab monitor exporter allows you to measure various GitLab metrics, pulled from Redis and the database.
 
 [âž” Read more about the GitLab monitor exporter.](gitlab_monitor_exporter.md)
 
diff --git a/doc/api/README.md b/doc/api/README.md
index 4f189c16673bfc6fdf866874db9c7498a5c348ed..b7f6ee69193535cb2c9be29b5b5f4c57d844c14b 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -29,10 +29,10 @@ following locations:
 - [Labels](labels.md)
 - [Merge Requests](merge_requests.md)
 - [Milestones](milestones.md)
-- [Open source license templates](templates/licenses.md)
 - [Namespaces](namespaces.md)
 - [Notes](notes.md) (comments)
 - [Notification settings](notification_settings.md)
+- [Open source license templates](templates/licenses.md)
 - [Pipelines](pipelines.md)
 - [Pipeline Triggers](pipeline_triggers.md)
 - [Pipeline Schedules](pipeline_schedules.md)
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 3f949ca5667d6ebc8c795999d5e36288b772f7fe..df5666bb7b6a484a8b38a8872ba50998ed358a80 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -221,7 +221,8 @@ GET /projects/:id/issues?search=issue+title+or+description
 | `order_by`  | string         | no       | Return requests ordered by `created_at` or `updated_at` fields. Default is `created_at`                                     |
 | `sort`      | string         | no       | Return requests sorted in `asc` or `desc` order. Default is `desc`                                                          |
 | `search`    | string         | no       | Search project issues against their `title` and `description`                                                                |
-
+| `created_after` | datetime | no | Return issues created after the given time (inclusive) |
+| `created_before` | datetime | no | Return issues created before the given time (inclusive) |
 
 ```bash
 curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/4/issues
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index cb22b67f556f549fcf8d62fa269d878bec2e34ed..3dc808c196d68ee870d63c88456d497e6daa0325 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -26,6 +26,8 @@ Parameters:
 | `sort`    | string  | no    | Return requests sorted in `asc` or `desc` order. Default is `desc`  |
 | `milestone`  | string  | no | Return merge requests for a specific milestone |
 | `labels`  | string  | no | Return merge requests matching a comma separated list of labels |
+| `created_after` | datetime | no | Return merge requests created after the given time (inclusive) |
+| `created_before` | datetime | no | Return merge requests created before the given time (inclusive) |
 
 ```json
 [
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 58f18105e21ffb0db33cd50d6e7dd72530b607d9..cc1bb3911c83a22ed03948281bef8d5c24380326 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -261,6 +261,7 @@ Parameters:
   ],
   "only_allow_merge_if_pipeline_succeeds": false,
   "only_allow_merge_if_all_discussions_are_resolved": false,
+  "printing_merge_requests_link_enabled": true,
   "request_access_enabled": false,
   "statistics": {
     "commit_count": 37,
@@ -344,6 +345,7 @@ Parameters:
 | `request_access_enabled` | boolean | no | Allow users to request member access |
 | `tag_list`    | array   | no       | The list of tags for a project; put array of tags, that should be finally assigned to a project |
 | `avatar`    | mixed   | no      | Image file for avatar of the project                |
+| `printing_merge_request_link_enabled` | boolean | no | Show link to create/view merge request when pushing from the command line |
 
 ### Create project for user
 
@@ -379,6 +381,7 @@ Parameters:
 | `request_access_enabled` | boolean | no | Allow users to request member access |
 | `tag_list`    | array   | no       | The list of tags for a project; put array of tags, that should be finally assigned to a project |
 | `avatar`    | mixed   | no      | Image file for avatar of the project                |
+| `printing_merge_request_link_enabled` | boolean | no | Show link to create/view merge request when pushing from the command line |
 
 ### Edit project
 
diff --git a/doc/api/users.md b/doc/api/users.md
index 91ce4f6dac3511c047b4a982e089f7745597ede0..cf09b8f44aa3e528b448fe0d9ae6aeba54a95d24 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -62,6 +62,7 @@ GET /users
     "avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg",
     "web_url": "http://localhost:3000/john_smith",
     "created_at": "2012-05-23T08:00:58Z",
+    "is_admin": false,
     "bio": null,
     "location": null,
     "skype": "",
@@ -94,6 +95,7 @@ GET /users
     "avatar_url": "http://localhost:3000/uploads/user/avatar/2/index.jpg",
     "web_url": "http://localhost:3000/jack_smith",
     "created_at": "2012-05-23T08:01:01Z",
+    "is_admin": false,
     "bio": null,
     "location": null,
     "skype": "",
@@ -197,6 +199,7 @@ Parameters:
   "avatar_url": "http://localhost:3000/uploads/user/avatar/1/index.jpg",
   "web_url": "http://localhost:3000/john_smith",
   "created_at": "2012-05-23T08:00:58Z",
+  "is_admin": false,
   "bio": null,
   "location": null,
   "skype": "",
@@ -251,6 +254,7 @@ Parameters:
 - `can_create_group` (optional) - User can create groups - true or false
 - `confirm` (optional)          - Require confirmation - true (default) or false
 - `external` (optional)         - Flags the user as external - true or false(default)
+- `avatar` (optional)           - Image file for user's avatar
 
 ## User modification
 
@@ -279,6 +283,7 @@ Parameters:
 - `admin` (optional)            - User is admin - true or false (default)
 - `can_create_group` (optional) - User can create groups - true or false
 - `external` (optional)         - Flags the user as external - true or false(default)
+- `avatar` (optional)           - Image file for user's avatar
 
 On password update, user will be forced to change it upon next login.
 Note, at the moment this method does only return a `404` error,
diff --git a/doc/development/limit_ee_conflicts.md b/doc/development/limit_ee_conflicts.md
index 51b4b398f2ca8769721ba1fbc79dc2265cbd5907..899be9eae4b1350e8bcc95722ec6a0c57c49e2c8 100644
--- a/doc/development/limit_ee_conflicts.md
+++ b/doc/development/limit_ee_conflicts.md
@@ -166,8 +166,8 @@ For instance this kind of thing:
       = render 'projects/zen', f: form, attr: :description,
                                classes: 'note-textarea',
                                placeholder: "Write a comment or drag your files here...",
-                               supports_slash_commands: !issuable.persisted?
-      = render 'projects/notes/hints', supports_slash_commands: !issuable.persisted?
+                               supports_quick_actions: !issuable.persisted?
+      = render 'projects/notes/hints', supports_quick_actions: !issuable.persisted?
       .clearfix
       .error-alert
 - if issuable.is_a?(Issue)
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 197a92905c80862b005f034ab8ccec5340cff80b..643fe5b686b1b34a8c4f1d4a453ee0cca35f3e6f 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -86,56 +86,31 @@ if your available memory changes.
 
 Notice: The 25 workers of Sidekiq will show up as separate processes in your process overview (such as top or htop) but they share the same RAM allocation since Sidekiq is a multithreaded application. Please see the section below about Unicorn workers for information about many you need of those.
 
-## GitLab Runner
-
-We strongly advise against installing GitLab Runner on the same machine you plan
-to install GitLab on. Depending on how you decide to configure GitLab Runner and
-what tools you use to exercise your application in the CI environment, GitLab
-Runner can consume significant amount of available memory.
-
-Memory consumption calculations, that are available above, will not be valid if
-you decide to run GitLab Runner and the GitLab Rails application on the same
-machine.
-
-It is also not safe to install everything on a single machine, because of the
-[security reasons] - especially when you plan to use shell executor with GitLab
-Runner.
-
-We recommend using a separate machine for each GitLab Runner, if you plan to
-use the CI features.
-
-[security reasons]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/docs/security/index.md
-
-## Unicorn Workers
-
-It's possible to increase the amount of unicorn workers and this will usually help to reduce the response time of the applications and increase the ability to handle parallel requests.
-
-For most instances we recommend using: CPU cores + 1 = unicorn workers.
-So for a machine with 2 cores, 3 unicorn workers is ideal.
+## Database
 
-For all machines that have 2GB and up we recommend a minimum of three unicorn workers.
-If you have a 1GB machine we recommend to configure only two Unicorn workers to prevent excessive swapping.
+The server running the database should have _at least_ 5-10 GB of storage
+available, though the exact requirements depend on the size of the GitLab
+installation (e.g. the number of users, projects, etc).
 
-To change the Unicorn workers when you have the Omnibus package please see [the Unicorn settings in the Omnibus GitLab documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/unicorn.md#unicorn-settings).
+We currently support the following databases:
 
-## Database
+- PostgreSQL (highly recommended)
+- MySQL/MariaDB (doesn't support all features)
 
-We currently support the following databases:
+We **highly recommend** the use of PostgreSQL instead of MySQL/MariaDB as not all
+features of GitLab work with MySQL/MariaDB:
 
-- PostgreSQL
-- MySQL/MariaDB
+1. MySQL support for subgroups was [dropped with GitLab 9.3][post].
+   See [issue #30472][30472] for more information.
+1. GitLab Geo does [not support MySQL](https://docs.gitlab.com/ee/gitlab-geo/database.html#mysql-replication).
+1. [Zero downtime migrations][zero] do not work with MySQL
 
-We _highly_ recommend the use of PostgreSQL instead of MySQL/MariaDB as not all
-features of GitLab may work with MySQL/MariaDB. For example, MySQL does not have
-the right features to support nested groups in an efficient manner; see
-<https://gitlab.com/gitlab-org/gitlab-ce/issues/30472> for more information
-about this. GitLab Geo also does [not support MySQL](https://docs.gitlab.com/ee/gitlab-geo/database.html#mysql-replication).
 Existing users using GitLab with MySQL/MariaDB are advised to
-migrate to PostgreSQL instead.
+[migrate to PostgreSQL](../update/mysql_to_postgresql.md) instead.
 
-The server running the database should have _at least_ 5-10 GB of storage
-available, though the exact requirements depend on the size of the GitLab
-installation (e.g. the number of users, projects, etc).
+[30472]: https://gitlab.com/gitlab-org/gitlab-ce/issues/30472
+[zero]: ../update/README.md#upgrading-without-downtime
+[post]: https://about.gitlab.com/2017/06/22/gitlab-9-3-released/#dropping-support-for-subgroups-in-mysql
 
 ### PostgreSQL Requirements
 
@@ -154,6 +129,18 @@ CREATE EXTENSION pg_trgm;
 On some systems you may need to install an additional package (e.g.
 `postgresql-contrib`) for this extension to become available.
 
+## Unicorn Workers
+
+It's possible to increase the amount of unicorn workers and this will usually help to reduce the response time of the applications and increase the ability to handle parallel requests.
+
+For most instances we recommend using: CPU cores + 1 = unicorn workers.
+So for a machine with 2 cores, 3 unicorn workers is ideal.
+
+For all machines that have 2GB and up we recommend a minimum of three unicorn workers.
+If you have a 1GB machine we recommend to configure only two Unicorn workers to prevent excessive swapping.
+
+To change the Unicorn workers when you have the Omnibus package please see [the Unicorn settings in the Omnibus GitLab documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/unicorn.md#unicorn-settings).
+
 ## Redis and Sidekiq
 
 Redis stores all user sessions and the background task queue.
@@ -172,6 +159,26 @@ default settings.
 If you would like to disable Prometheus and it's exporters or read more information
 about it, check the [Prometheus documentation](../administration/monitoring/prometheus/index.md).
 
+## GitLab Runner
+
+We strongly advise against installing GitLab Runner on the same machine you plan
+to install GitLab on. Depending on how you decide to configure GitLab Runner and
+what tools you use to exercise your application in the CI environment, GitLab
+Runner can consume significant amount of available memory.
+
+Memory consumption calculations, that are available above, will not be valid if
+you decide to run GitLab Runner and the GitLab Rails application on the same
+machine.
+
+It is also not safe to install everything on a single machine, because of the
+[security reasons] - especially when you plan to use shell executor with GitLab
+Runner.
+
+We recommend using a separate machine for each GitLab Runner, if you plan to
+use the CI features.
+
+[security reasons]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/blob/master/docs/security/index.md
+
 ## Supported web browsers
 
 We support the current and the previous major release of Firefox, Chrome/Chromium, Safari and Microsoft browsers (Microsoft Edge and Internet Explorer 11).
diff --git a/doc/integration/chat_commands.md b/doc/integration/chat_commands.md
index c878dc7e6500d9d1ef1f7b6d20a4bfe4895dfe40..2856992ee25a5aef7a8532b27b99ceb5c6e47ab2 100644
--- a/doc/integration/chat_commands.md
+++ b/doc/integration/chat_commands.md
@@ -1,14 +1 @@
-# Chat Commands
-
-Chat commands in Mattermost and Slack (also called Slack slash commands) allow you to control GitLab and view GitLab content right inside your chat client, without having to leave it. For Slack, this requires a [project service configuration](../user/project/integrations/slack_slash_commands.md). Simply type the command as a message in your chat client to activate it. 
-
-Commands are scoped to a project, with a trigger term that is specified during configuration. (We suggest you use the project name as the trigger term for simplicty and clarity.) Taking the trigger term as `project-name`, the commands are:
-
-
-| Command | Effect |
-| ------- | ------ |
-| `/project-name help` | Shows all available chat commands |
-| `/project-name issue new <title> <shift+return> <description>` | Creates a new issue with title `<title>` and description `<description>` |
-| `/project-name issue show <id>` | Shows the issue with id `<id>` |
-| `/project-name issue search <query>` | Shows up to 5 issues matching `<query>` |
-| `/project-name deploy <from> to <to>` | Deploy from the `<from>` environment to the `<to>` environment |
\ No newline at end of file
+This document was moved to [integration/slash_commands.md](slash_commands.md).
diff --git a/doc/integration/slash_commands.md b/doc/integration/slash_commands.md
new file mode 100644
index 0000000000000000000000000000000000000000..5d880ba785c46006db11cd15fc60204627121070
--- /dev/null
+++ b/doc/integration/slash_commands.md
@@ -0,0 +1,14 @@
+# Slash Commands
+
+Slash commands in Mattermost and Slack allow you to control GitLab and view GitLab content right inside your chat client, without having to leave it. For Slack, this requires a [project service configuration](../user/project/integrations/slack_slash_commands.md). Simply type the command as a message in your chat client to activate it.
+
+Commands are scoped to a project, with a trigger term that is specified during configuration. (We suggest you use the project name as the trigger term for simplicty and clarity.) Taking the trigger term as `project-name`, the commands are:
+
+
+| Command | Effect |
+| ------- | ------ |
+| `/project-name help` | Shows all available slash commands |
+| `/project-name issue new <title> <shift+return> <description>` | Creates a new issue with title `<title>` and description `<description>` |
+| `/project-name issue show <id>` | Shows the issue with id `<id>` |
+| `/project-name issue search <query>` | Shows up to 5 issues matching `<query>` |
+| `/project-name deploy <from> to <to>` | Deploy from the `<from>` environment to the `<to>` environment |
diff --git a/doc/update/README.md b/doc/update/README.md
index d024a809f24b26101159a08652f3799556e028d2..22dbc7c750f6b6e4dacae83bf3b8a93900265a81 100644
--- a/doc/update/README.md
+++ b/doc/update/README.md
@@ -11,22 +11,6 @@ There are currently 3 official ways to install GitLab:
 
 Based on your installation, choose a section below that fits your needs.
 
----
-
-<!-- START doctoc generated TOC please keep comment here to allow auto update -->
-<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
-**Table of Contents**  *generated with [DocToc](https://github.com/thlorenz/doctoc)*
-
-- [Omnibus Packages](#omnibus-packages)
-- [Installation from source](#installation-from-source)
-- [Installation using Docker](#installation-using-docker)
-- [Upgrading between editions](#upgrading-between-editions)
-    - [Community to Enterprise Edition](#community-to-enterprise-edition)
-    - [Enterprise to Community Edition](#enterprise-to-community-edition)
-- [Miscellaneous](#miscellaneous)
-
-<!-- END doctoc generated TOC please keep comment here to allow auto update -->
-
 ## Omnibus Packages
 
 - The [Omnibus update guide](http://docs.gitlab.com/omnibus/update/README.html)
diff --git a/doc/user/discussions/index.md b/doc/user/discussions/index.md
index 59e343ebe51a4bdb784f55be12171f39dc28d01b..8b1d299484c18814693839f31c80f3b3ffb5ff0c 100644
--- a/doc/user/discussions/index.md
+++ b/doc/user/discussions/index.md
@@ -10,7 +10,7 @@ You can leave a comment in the following places:
 - commits
 - commit diffs
 
-The comment area supports [Markdown] and [slash commands]. One can edit their
+The comment area supports [Markdown] and [quick actions]. One can edit their
 own comment at any time, and anyone with [Master access level][permissions] or
 higher can also edit a comment made by someone else.
 
@@ -146,5 +146,5 @@ comments in greater detail.
 [discussion-view]: img/discussion_view.png
 [discussions-resolved]: img/discussions_resolved.png
 [markdown]: ../markdown.md
-[slash commands]: ../project/slash_commands.md
+[quick actions]: ../project/quick_actions.md
 [permissions]: ../permissions.md
diff --git a/doc/user/group/subgroups/index.md b/doc/user/group/subgroups/index.md
index c4921c74a17bcc9eb2c71ce87f406b0996104abc..5724dcfab480024784da3b610630a19c9f40d2aa 100644
--- a/doc/user/group/subgroups/index.md
+++ b/doc/user/group/subgroups/index.md
@@ -1,6 +1,9 @@
 # Subgroups
 
-> [Introduced][ce-2772] in GitLab 9.0.
+>**Notes:**
+- [Introduced][ce-2772] in GitLab 9.0.
+- Not available when using MySQL as external database (support removed in
+  GitLab 9.3 [due to performance reasons][issue]).
 
 With subgroups (aka nested groups or hierarchical groups) you can have
 up to 20 levels of nested groups, which among other things can help you to:
@@ -173,3 +176,4 @@ Here's a list of what you can't do with subgroups:
 [ce-2772]: https://gitlab.com/gitlab-org/gitlab-ce/issues/2772
 [permissions]: ../../permissions.md#group
 [reserved]:  https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/path_regex.rb
+[issue]: https://gitlab.com/gitlab-org/gitlab-ce/issues/30472#note_27747600
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index 3fda47b9e3436e53029f2f181b3eb1ac7d58c065..3d47e644ad2ad3f42d255fdb576781cd34325741 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -89,6 +89,7 @@ group.
 | Create project in group |       |          |           | ✓      | ✓     |
 | Manage group members    |       |          |           |        | ✓     |
 | Remove group            |       |          |           |        | ✓     |
+| Manage group labels     |       | ✓        | ✓         | ✓      | ✓     |
 
 ## External Users
 
diff --git a/doc/user/project/integrations/img/merge_request_performance.png b/doc/user/project/integrations/img/merge_request_performance.png
index 93b2626fed7868a36cec5138cc4f5f245edaa331..eba6515a6ae01a9a9d33c9522fd7db083b5ed306 100644
Binary files a/doc/user/project/integrations/img/merge_request_performance.png and b/doc/user/project/integrations/img/merge_request_performance.png differ
diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md
index d3fb5916dc64263e74a69dcbcc77ec01291f0520..86ceb14b9653de2bec58a76d080ea3c6f4960e77 100644
--- a/doc/user/project/integrations/prometheus.md
+++ b/doc/user/project/integrations/prometheus.md
@@ -167,15 +167,15 @@ environment which has had a successful deployment.
 ## Determining the performance impact of a merge
 
 > [Introduced][ce-10408] in GitLab 9.2.
+> GitLab 9.3 added the [numeric comparison](https://gitlab.com/gitlab-org/gitlab-ce/issues/27439) of the 30 minute averages.
 
 Developers can view the performance impact of their changes within the merge
-request workflow. When a source branch has been deployed to an environment, a
-sparkline will appear showing the average memory consumption of the app. The dot
+request workflow. When a source branch has been deployed to an environment, a sparkline and numeric comparison of the average memory consumption will appear. On the sparkline, a dot
 indicates when the current changes were deployed, with up to 30 minutes of
-performance data displayed before and after. The sparkline will be updated after
+performance data displayed before and after. The comparison shows the difference between the 30 minute average before and after the deployment. This information is updated after
 each commit has been deployed.
 
-Once merged and the target branch has been redeployed, the sparkline will switch
+Once merged and the target branch has been redeployed, the metrics will switch
 to show the new environments this revision has been deployed to.
 
 Performance data will be available for the duration it is persisted on the
diff --git a/doc/user/project/integrations/slack_slash_commands.md b/doc/user/project/integrations/slack_slash_commands.md
index 54e0ee611cb0fd2f4df19413ecd46af521c8aeb9..c267da69bb3707db76ef58d5991665cf795e9b81 100644
--- a/doc/user/project/integrations/slack_slash_commands.md
+++ b/doc/user/project/integrations/slack_slash_commands.md
@@ -2,7 +2,7 @@
 
 > Introduced in GitLab 8.15
 
-Slack slash commands (also known as chat commmands) allow you to control GitLab and view content right inside Slack, without having to leave it. This requires configurations in both Slack and GitLab. 
+Slack slash commands allow you to control GitLab and view content right inside Slack, without having to leave it. This requires configurations in both Slack and GitLab.
 
 > Note: GitLab can also send events (e.g. issue created) to Slack as notifications. This is the separately configured [Slack Notifications Service](slack.md).
 
@@ -20,4 +20,4 @@ Slack slash commands (also known as chat commmands) allow you to control GitLab
 
 ## Usage
 
-You can now use the [Slack slash commands](../../../integration/chat_commands.md).
\ No newline at end of file
+You can now use the [Slack slash commands](../../../integration/slash_commands.md).
diff --git a/doc/user/project/issues/confidential_issues.md b/doc/user/project/issues/confidential_issues.md
index 208be7d0ed579bd47a8cc7684e7faa66a31c5072..1760b1821142fb1b034bc63e63757bd2b7157e25 100644
--- a/doc/user/project/issues/confidential_issues.md
+++ b/doc/user/project/issues/confidential_issues.md
@@ -43,8 +43,9 @@ next to the issues that are marked as confidential.
 
 ---
 
-While inside the issue, you can see a persistent dark banner at the top of the
-screen.
+Likewise, while inside the issue, you can see the eye-slash icon right next to
+the issue number, but there is also an indicator in the comment area that the
+issue you are commenting on is confidential.
 
 ![Confidential issue page](img/confidential_issues_issue_page.png)
 
diff --git a/doc/user/project/issues/img/confidential_issues_issue_page.png b/doc/user/project/issues/img/confidential_issues_issue_page.png
index 91f7cc8d3caabe9d07b4d6f57b2f5bd14c8b28f3..f04ec8ff32bf49b31873f678c37933ecad1ef516 100755
Binary files a/doc/user/project/issues/img/confidential_issues_issue_page.png and b/doc/user/project/issues/img/confidential_issues_issue_page.png differ
diff --git a/doc/user/project/issues/issues_functionalities.md b/doc/user/project/issues/issues_functionalities.md
index ba843201e1a9c6f89b8b0574dceea0c430ed8832..294176e61f92b5394f2f8fe5e120f4fa228589cf 100644
--- a/doc/user/project/issues/issues_functionalities.md
+++ b/doc/user/project/issues/issues_functionalities.md
@@ -68,7 +68,7 @@ This feature is available only in [GitLab Enterprise Edition](https://about.gitl
 - Spend: add the time spent on the implementation of that issue
 
 > **Note:**
-both estimate and spend times are set via [GitLab Slash Commands](../slash_commands.md).
+both estimate and spend times are set via [GitLab Quick Actions](../quick_actions.md).
 
 Learn more on the [Time Tracking documentation](https://docs.gitlab.com/ee/workflow/time_tracking.html).
 
@@ -147,7 +147,7 @@ or in the issue thread.
 
 #### 15. Award emoji
 
-- Award an emoji to that issue. 
+- Award an emoji to that issue.
 
 > **Tip:**
 Posting "+1" as comments in threads spam all
diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md
new file mode 100644
index 0000000000000000000000000000000000000000..19b51c83222df56a9f3fcf12ada2ef5ddef799e4
--- /dev/null
+++ b/doc/user/project/quick_actions.md
@@ -0,0 +1,39 @@
+# GitLab quick actions
+
+Quick actions are textual shortcuts for common actions on issues or merge
+requests that are usually done by clicking buttons or dropdowns in GitLab's UI.
+You can enter these commands while creating a new issue or merge request, and
+in comments. Each command should be on a separate line in order to be properly
+detected and executed. The commands are removed from the issue, merge request or
+comment body before it is saved and will not be visible to anyone else.
+
+Below is a list of all of the available commands and descriptions about what they
+do.
+
+| Command                    | Action       |
+|:---------------------------|:-------------|
+| `/close`                   | Close the issue or merge request |
+| `/reopen`                  | Reopen the issue or merge request |
+| `/merge`                   | Merge (when pipeline succeeds) |
+| `/title <New title>`       | Change title |
+| `/assign @username`        | Assign |
+| `/unassign`                | Remove assignee |
+| `/milestone %milestone`    | Set milestone |
+| `/remove_milestone`        | Remove milestone |
+| `/label ~foo ~"bar baz"`   | Add label(s) |
+| `/unlabel ~foo ~"bar baz"` | Remove all or specific label(s) |
+| `/relabel ~foo ~"bar baz"` | Replace all label(s) |
+| `/todo`                    | Add a todo |
+| `/done`                    | Mark todo as done |
+| `/subscribe`               | Subscribe |
+| `/unsubscribe`             | Unsubscribe |
+| <code>/due &lt;in 2 days &#124; this Friday &#124; December 31st&gt;</code> | Set due date |
+| `/remove_due_date`         | Remove due date |
+| `/wip`                     | Toggle the Work In Progress status |
+| <code>/estimate &lt;1w 3d 2h 14m&gt;</code> | Set time estimate |
+| `/remove_estimate`       | Remove estimated time |
+| <code>/spend &lt;1h 30m &#124; -1h 5m&gt;</code> | Add or subtract spent time |
+| `/remove_time_spent`       | Remove time spent |
+| `/target_branch <Branch Name>` | Set target branch for current merge request |
+| `/award :emoji:`  | Toggle award for :emoji: |
+| `/board_move ~column`      | Move issue to column on the board |
diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md
index 58d2fd76c6102d3c6d1edb0b578dab244c0cd514..35960ade3d4550d6b44708b1e0a8105cf029be6d 100644
--- a/doc/user/project/settings/import_export.md
+++ b/doc/user/project/settings/import_export.md
@@ -27,14 +27,15 @@ with all their related data and be moved into a new GitLab instance.
 
 | GitLab version | Import/Export version |
 | -------- | -------- |
-| 9.2.0 to current | 0.1.7 |
-| 8.17.0   | 0.1.6    |
-| 8.13.0   | 0.1.5    |
-| 8.12.0   | 0.1.4    |
-| 8.10.3   | 0.1.3    |
-| 8.10.0   | 0.1.2    |
-| 8.9.5    | 0.1.1    |
-| 8.9.0    | 0.1.0    |
+| 9.4.0 to current | 0.1.8 |
+| 9.2.0    | 0.1.7 |
+| 8.17.0   | 0.1.6 |
+| 8.13.0   | 0.1.5 |
+| 8.12.0   | 0.1.4 |
+| 8.10.3   | 0.1.3 |
+| 8.10.0   | 0.1.2 |
+| 8.9.5    | 0.1.1 |
+| 8.9.0    | 0.1.0 |
 
  > The table reflects what GitLab version we updated the Import/Export version at.
  > For instance, 8.10.3 and 8.11 will have the same Import/Export version (0.1.3)
diff --git a/doc/user/project/slash_commands.md b/doc/user/project/slash_commands.md
index 08452ca75cd3d7a88b9aa5905fd5e35fc37ab8ab..e9103a3f49cc76dc218a7a63617d58f5d75b97f3 100644
--- a/doc/user/project/slash_commands.md
+++ b/doc/user/project/slash_commands.md
@@ -1,39 +1 @@
-# GitLab slash commands
-
-Slash commands are textual shortcuts for common actions on issues or merge
-requests that are usually done by clicking buttons or dropdowns in GitLab's UI.
-You can enter these commands while creating a new issue or merge request, and
-in comments. Each command should be on a separate line in order to be properly
-detected and executed. The commands are removed from the issue, merge request or
-comment body before it is saved and will not be visible to anyone else.
-
-Below is a list of all of the available commands and descriptions about what they
-do.
-
-| Command                    | Action       |
-|:---------------------------|:-------------|
-| `/close`                   | Close the issue or merge request |
-| `/reopen`                  | Reopen the issue or merge request |
-| `/merge`                   | Merge (when pipeline succeeds) |
-| `/title <New title>`       | Change title |
-| `/assign @username`        | Assign |
-| `/unassign`                | Remove assignee |
-| `/milestone %milestone`    | Set milestone |
-| `/remove_milestone`        | Remove milestone |
-| `/label ~foo ~"bar baz"`   | Add label(s) |
-| `/unlabel ~foo ~"bar baz"` | Remove all or specific label(s) |
-| `/relabel ~foo ~"bar baz"` | Replace all label(s) |
-| `/todo`                    | Add a todo |
-| `/done`                    | Mark todo as done |
-| `/subscribe`               | Subscribe |
-| `/unsubscribe`             | Unsubscribe |
-| <code>/due &lt;in 2 days &#124; this Friday &#124; December 31st&gt;</code> | Set due date |
-| `/remove_due_date`         | Remove due date |
-| `/wip`                     | Toggle the Work In Progress status |
-| <code>/estimate &lt;1w 3d 2h 14m&gt;</code> | Set time estimate |
-| `/remove_estimate`       | Remove estimated time |
-| <code>/spend &lt;1h 30m &#124; -1h 5m&gt;</code> | Add or subtract spent time |
-| `/remove_time_spent`       | Remove time spent |
-| `/target_branch <Branch Name>` | Set target branch for current merge request |
-| `/award :emoji:`  | Toggle award for :emoji: |
-| `/board_move ~column`      | Move issue to column on the board |
+This document was moved to [user/project/quick_actions.md](quick_actions.md).
diff --git a/doc/workflow/README.md b/doc/workflow/README.md
index 604c7d5cefb046d13e58e2df866a39ad2362a016..54d4028a50aadf431f2070cbba25ed0515829780 100644
--- a/doc/workflow/README.md
+++ b/doc/workflow/README.md
@@ -21,7 +21,7 @@
 - [Project users](add-user/add-user.md)
 - [Protected branches](../user/project/protected_branches.md)
 - [Protected tags](../user/project/protected_tags.md)
-- [Slash commands](../user/project/slash_commands.md)
+- [Quick Actions](../user/project/quick_actions.md)
 - [Sharing a project with a group](share_with_group.md)
 - [Share projects with other groups](share_projects_with_other_groups.md)
 - [Time tracking](time_tracking.md)
diff --git a/doc/workflow/time_tracking.md b/doc/workflow/time_tracking.md
index de12994c516e6bafa7e523341c38e8215beedb41..bfe87bb2ceb02efb6fc3bfe90c83a1cc41c91849 100644
--- a/doc/workflow/time_tracking.md
+++ b/doc/workflow/time_tracking.md
@@ -21,13 +21,13 @@ below.
 
 ## How to enter data
 
-Time Tracking uses two [slash commands] that GitLab introduced with this new
+Time Tracking uses two [quick actions] that GitLab introduced with this new
 feature: `/spend` and `/estimate`.
 
-Slash commands can be used in the body of an issue or a merge request, but also
+Quick actions can be used in the body of an issue or a merge request, but also
 in a comment in both an issue or a merge request.
 
-Below is an example of how you can use those new slash commands inside a comment.
+Below is an example of how you can use those new quick actions inside a comment.
 
 ![Time tracking example in a comment](time-tracking/time-tracking-example.png)
 
@@ -70,4 +70,4 @@ The following time units are available:
 Default conversion rates are 1w = 5d and 1d = 8h.
 
 [landing]: https://about.gitlab.com/features/time-tracking
-[slash-commands]: ../user/project/slash_commands.md
+[quick actions]: ../user/project/quick_actions.md
diff --git a/features/project/create.feature b/features/project/create.feature
deleted file mode 100644
index 67336d73bf729a94699cd4ccdf6de0857815ca28..0000000000000000000000000000000000000000
--- a/features/project/create.feature
+++ /dev/null
@@ -1,14 +0,0 @@
-@project-create
-Feature: Project Create
-  In order to get access to project sections
-  A user with ability to create a project
-  Should be able to create a new one
-
-  @javascript
-  Scenario: User create a project
-    Given I sign in as a user
-    And I have an ssh key
-    When I visit new project page
-    And fill project form with valid data
-    Then I should see project page
-    And I should see empty project instructions
diff --git a/lib/api/access_requests.rb b/lib/api/access_requests.rb
index a5c9f0b509c39287540ccde133ac4d9453118df9..c9b5f58c55796555bc8c0a9ba5b967b9b69cc747 100644
--- a/lib/api/access_requests.rb
+++ b/lib/api/access_requests.rb
@@ -68,8 +68,8 @@ module API
         delete ":id/access_requests/:user_id" do
           source = find_source(source_type, params[:id])
 
-          ::Members::DestroyService.new(source, current_user, params).
-            execute(:requesters)
+          ::Members::DestroyService.new(source, current_user, params)
+            .execute(:requesters)
         end
       end
     end
diff --git a/lib/api/branches.rb b/lib/api/branches.rb
index f35084a582ac286f5f69f8f2b1c42c996d975bf5..3d816f8771db32344b22aa3e56d8d2b96df95925 100644
--- a/lib/api/branches.rb
+++ b/lib/api/branches.rb
@@ -102,8 +102,8 @@ module API
       post ":id/repository/branches" do
         authorize_push_project
 
-        result = CreateBranchService.new(user_project, current_user).
-                 execute(params[:branch], params[:ref])
+        result = CreateBranchService.new(user_project, current_user)
+                 .execute(params[:branch], params[:ref])
 
         if result[:status] == :success
           present result[:branch],
@@ -121,8 +121,8 @@ module API
       delete ":id/repository/branches/:branch", requirements: { branch: /.+/ } do
         authorize_push_project
 
-        result = DeleteBranchService.new(user_project, current_user).
-                 execute(params[:branch])
+        result = DeleteBranchService.new(user_project, current_user)
+                 .execute(params[:branch])
 
         if result[:status] != :success
           render_api_error!(result[:message], result[:return_code])
diff --git a/lib/api/deploy_keys.rb b/lib/api/deploy_keys.rb
index 7cdee8aced7659392448684919ed628ef6e8757d..d5c2f3d509448efc6dd9e4d23651db5632100a97 100644
--- a/lib/api/deploy_keys.rb
+++ b/lib/api/deploy_keys.rb
@@ -86,7 +86,7 @@ module API
         at_least_one_of :title, :can_push
       end
       put ":id/deploy_keys/:key_id" do
-        key = user_project.deploy_keys.find(params.delete(:key_id))
+        key = DeployKey.find(params.delete(:key_id))
 
         authorize!(:update_deploy_key, key)
 
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 412443a2405fb0c5fa44fc02f930c4fb4a3fd86f..aa91451c9f499a5193148a025b27cba531549335 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -43,11 +43,14 @@ module API
       expose :external
     end
 
-    class UserWithPrivateDetails < UserPublic
-      expose :private_token
+    class UserWithAdmin < UserPublic
       expose :admin?, as: :is_admin
     end
 
+    class UserWithPrivateDetails < UserWithAdmin
+      expose :private_token
+    end
+
     class Email < Grape::Entity
       expose :id, :email
     end
@@ -115,6 +118,7 @@ module API
       expose :only_allow_merge_if_pipeline_succeeds
       expose :request_access_enabled
       expose :only_allow_merge_if_all_discussions_are_resolved
+      expose :printing_merge_request_link_enabled
 
       expose :statistics, using: 'API::Entities::ProjectStatistics', if: :statistics
     end
@@ -480,9 +484,9 @@ module API
       expose :job_events
       # Expose serialized properties
       expose :properties do |service, options|
-        field_names = service.fields.
-          select { |field| options[:include_passwords] || field[:type] != 'password' }.
-          map { |field| field[:name] }
+        field_names = service.fields
+          .select { |field| options[:include_passwords] || field[:type] != 'password' }
+          .map { |field| field[:name] }
         service.properties.slice(*field_names)
       end
     end
diff --git a/lib/api/helpers/internal_helpers.rb b/lib/api/helpers/internal_helpers.rb
index d3732d67622933e2d60aa18792b6558af6c01a20..5e9cf5e68b11baec287fe9cc1284c10485b733c5 100644
--- a/lib/api/helpers/internal_helpers.rb
+++ b/lib/api/helpers/internal_helpers.rb
@@ -10,6 +10,10 @@ module API
         set_project unless defined?(@project)
         @project
       end
+      
+      def redirected_path
+        @redirected_path
+      end
 
       def ssh_authentication_abilities
         [
@@ -38,8 +42,9 @@ module API
       def set_project
         if params[:gl_repository]
           @project, @wiki = Gitlab::GlRepository.parse(params[:gl_repository])
+          @redirected_path = nil
         else
-          @project, @wiki = Gitlab::RepoPath.parse(params[:project])
+          @project, @wiki, @redirected_path = Gitlab::RepoPath.parse(params[:project])
         end
       end
 
diff --git a/lib/api/internal.rb b/lib/api/internal.rb
index ecd6d672cf7fca2fd4ff97035abf9c0872d35b66..479ee16a611436b94e2f71f5fb257a32c74b54ea 100644
--- a/lib/api/internal.rb
+++ b/lib/api/internal.rb
@@ -34,7 +34,7 @@ module API
 
         access_checker_klass = wiki? ? Gitlab::GitAccessWiki : Gitlab::GitAccess
         access_checker = access_checker_klass
-          .new(actor, project, protocol, authentication_abilities: ssh_authentication_abilities)
+          .new(actor, project, protocol, authentication_abilities: ssh_authentication_abilities, redirected_path: redirected_path)
 
         begin
           access_checker.check(params[:action], params[:changes])
@@ -71,11 +71,16 @@ module API
       end
 
       #
-      # Discover user by ssh key
+      # Discover user by ssh key or user id
       #
       get "/discover" do
-        key = Key.find(params[:key_id])
-        present key.user, with: Entities::UserSafe
+        if params[:key_id]
+          key = Key.find(params[:key_id])
+          user = key.user
+        elsif params[:user_id]
+          user = User.find_by(id: params[:user_id])
+        end
+        present user, with: Entities::UserSafe
       end
 
       get "/check" do
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 78db960ae28fa2b8499306bfcfc88aa5d9b3454b..09dca0dff8bc04f9d770aa22394561e1ed3acae0 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -27,6 +27,8 @@ module API
         optional :milestone, type: String, desc: 'Return issues for a specific milestone'
         optional :iids, type: Array[Integer], desc: 'The IID array of issues'
         optional :search, type: String, desc: 'Search issues for text present in the title or description'
+        optional :created_after, type: DateTime, desc: 'Return issues created after the specified time'
+        optional :created_before, type: DateTime, desc: 'Return issues created before the specified time'
         use :pagination
       end
 
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 710deba5ae3f230ae0e115d380bca69db23d1a72..1118fc7465b6209c5d3f91c3ee0740d1b35d8697 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -72,6 +72,8 @@ module API
         optional :iids, type: Array[Integer], desc: 'The IID array of merge requests'
         optional :milestone, type: String, desc: 'Return merge requests for a specific milestone'
         optional :labels, type: String, desc: 'Comma-separated list of label names'
+        optional :created_after, type: DateTime, desc: 'Return merge requests created after the specified time'
+        optional :created_before, type: DateTime, desc: 'Return merge requests created before the specified time'
         use :pagination
       end
       get ":id/merge_requests" do
@@ -97,7 +99,7 @@ module API
         authorize! :create_merge_request, user_project
 
         mr_params = declared_params(include_missing: false)
-        mr_params[:force_remove_source_branch] = mr_params.delete(:remove_source_branch) if mr_params[:remove_source_branch].present?
+        mr_params[:force_remove_source_branch] = mr_params.delete(:remove_source_branch)
 
         merge_request = ::MergeRequests::CreateService.new(user_project, current_user, mr_params).execute
 
diff --git a/lib/api/milestones.rb b/lib/api/milestones.rb
index a3ea619a2fbafcde0d17a89ae2d30ba6d32d54f5..3541d3c95fb616339cb54624116a2aa8d17352cf 100644
--- a/lib/api/milestones.rb
+++ b/lib/api/milestones.rb
@@ -117,7 +117,7 @@ module API
         finder_params = {
           project_id: user_project.id,
           milestone_title: milestone.title,
-          sort: 'position_asc'
+          sort: 'label_priority'
         }
 
         issues = IssuesFinder.new(current_user, finder_params).execute
@@ -140,7 +140,7 @@ module API
         finder_params = {
           project_id: user_project.id,
           milestone_title: milestone.title,
-          sort: 'position_asc'
+          sort: 'label_priority'
         }
 
         merge_requests = MergeRequestsFinder.new(current_user, finder_params).execute
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index e281e3230fdab9766f44bc8c535e358f9004d266..01ca62b593f5b158d03e5f8db88320bb22b775a2 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -33,8 +33,8 @@ module API
               # paginate() only works with a relation. This could lead to a
               # mismatch between the pagination headers info and the actual notes
               # array returned, but this is really a edge-case.
-              paginate(noteable.notes).
-              reject { |n| n.cross_reference_not_visible_for?(current_user) }
+              paginate(noteable.notes)
+              .reject { |n| n.cross_reference_not_visible_for?(current_user) }
             present notes, with: Entities::Note
           else
             not_found!("Notes")
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 50d34e8a7382a6958233c8351033db38ecfc20b4..c5df45b79028cb8b67489290122a2e2ffa5258cf 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -23,6 +23,7 @@ module API
         optional :only_allow_merge_if_all_discussions_are_resolved, type: Boolean, desc: 'Only allow to merge if all discussions are resolved'
         optional :tag_list, type: Array[String], desc: 'The list of tags for a project'
         optional :avatar, type: File, desc: 'Avatar image for project'
+        optional :printing_merge_request_link_enabled, type: Boolean, desc: 'Show link to create/view merge request when pushing from the command line'
       end
 
       params :optional_params do
@@ -218,6 +219,7 @@ module API
             :only_allow_merge_if_all_discussions_are_resolved,
             :only_allow_merge_if_pipeline_succeeds,
             :path,
+            :printing_merge_request_link_enabled,
             :public_builds,
             :request_access_enabled,
             :shared_runners_enabled,
diff --git a/lib/api/services.rb b/lib/api/services.rb
index 47bd9940f77181368bffcff147b23f7fdb96f1da..7488f95a9b7ed39d126fa8a97300f520227f6126 100644
--- a/lib/api/services.rb
+++ b/lib/api/services.rb
@@ -685,7 +685,7 @@ module API
 
     trigger_services.each do |service_slug, settings|
       helpers do
-        def chat_command_service(project, service_slug, params)
+        def slash_command_service(project, service_slug, params)
           project.services.active.where(template: false).find do |service|
             service.try(:token) == params[:token] && service.to_param == service_slug.underscore
           end
@@ -710,7 +710,7 @@ module API
           # This is not accurate, but done to prevent leakage of the project names
           not_found!('Service') unless project
 
-          service = chat_command_service(project, service_slug, params)
+          service = slash_command_service(project, service_slug, params)
           result = service.try(:trigger, params)
 
           if result
diff --git a/lib/api/tags.rb b/lib/api/tags.rb
index c7b1efe0bfa988d37844a6ef82dd62297c697268..633a858f8c73f17c2f4b9ac779af286653830ebd 100644
--- a/lib/api/tags.rb
+++ b/lib/api/tags.rb
@@ -44,8 +44,8 @@ module API
       post ':id/repository/tags' do
         authorize_push_project
 
-        result = ::Tags::CreateService.new(user_project, current_user).
-          execute(params[:tag_name], params[:ref], params[:message], params[:release_description])
+        result = ::Tags::CreateService.new(user_project, current_user)
+          .execute(params[:tag_name], params[:ref], params[:message], params[:release_description])
 
         if result[:status] == :success
           present result[:tag],
@@ -63,8 +63,8 @@ module API
       delete ":id/repository/tags/:tag_name", requirements: { tag_name: /.+/ } do
         authorize_push_project
 
-        result = ::Tags::DestroyService.new(user_project, current_user).
-          execute(params[:tag_name])
+        result = ::Tags::DestroyService.new(user_project, current_user)
+          .execute(params[:tag_name])
 
         if result[:status] != :success
           render_api_error!(result[:message], result[:return_code])
@@ -81,8 +81,8 @@ module API
       post ':id/repository/tags/:tag_name/release', requirements: { tag_name: /.+/ } do
         authorize_push_project
 
-        result = CreateReleaseService.new(user_project, current_user).
-          execute(params[:tag_name], params[:description])
+        result = CreateReleaseService.new(user_project, current_user)
+          .execute(params[:tag_name], params[:description])
 
         if result[:status] == :success
           present result[:release], with: Entities::Release
@@ -101,8 +101,8 @@ module API
       put ':id/repository/tags/:tag_name/release', requirements: { tag_name: /.+/ } do
         authorize_push_project
 
-        result = UpdateReleaseService.new(user_project, current_user).
-          execute(params[:tag_name], params[:description])
+        result = UpdateReleaseService.new(user_project, current_user)
+          .execute(params[:tag_name], params[:description])
 
         if result[:status] == :success
           present result[:release], with: Entities::Release
diff --git a/lib/api/users.rb b/lib/api/users.rb
index dda64715ee1edcf700941603ebeb283c786d5ed8..c10e3364382bac5be5301e0c1e883a86bb529e64 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -29,6 +29,7 @@ module API
           optional :can_create_group, type: Boolean, desc: 'Flag indicating the user can create groups'
           optional :skip_confirmation, type: Boolean, default: false, desc: 'Flag indicating the account is confirmed'
           optional :external, type: Boolean, desc: 'Flag indicating the user is an external user'
+          optional :avatar, type: File, desc: 'Avatar image for user'
           all_or_none_of :extern_uid, :provider
         end
       end
@@ -58,7 +59,7 @@ module API
 
         users = UsersFinder.new(current_user, params).execute
 
-        entity = current_user.admin? ? Entities::UserPublic : Entities::UserBasic
+        entity = current_user.admin? ? Entities::UserWithAdmin : Entities::UserBasic
         present paginate(users), with: entity
       end
 
@@ -102,13 +103,13 @@ module API
         if user.persisted?
           present user, with: Entities::UserPublic
         else
-          conflict!('Email has already been taken') if User.
-              where(email: user.email).
-              count > 0
+          conflict!('Email has already been taken') if User
+              .where(email: user.email)
+              .count > 0
 
-          conflict!('Username has already been taken') if User.
-              where(username: user.username).
-              count > 0
+          conflict!('Username has already been taken') if User
+              .where(username: user.username)
+              .count > 0
 
           render_validation_error!(user)
         end
@@ -132,12 +133,12 @@ module API
         not_found!('User') unless user
 
         conflict!('Email has already been taken') if params[:email] &&
-            User.where(email: params[:email]).
-                where.not(id: user.id).count > 0
+            User.where(email: params[:email])
+                .where.not(id: user.id).count > 0
 
         conflict!('Username has already been taken') if params[:username] &&
-            User.where(username: params[:username]).
-                where.not(id: user.id).count > 0
+            User.where(username: params[:username])
+                .where.not(id: user.id).count > 0
 
         user_params = declared_params(include_missing: false)
         identity_attrs = user_params.slice(:provider, :extern_uid)
@@ -516,9 +517,9 @@ module API
       get "activities" do
         authenticated_as_admin!
 
-        activities = User.
-          where(User.arel_table[:last_activity_on].gteq(params[:from])).
-          reorder(last_activity_on: :asc)
+        activities = User
+          .where(User.arel_table[:last_activity_on].gteq(params[:from]))
+          .reorder(last_activity_on: :asc)
 
         present paginate(activities), with: Entities::UserActivity
       end
diff --git a/lib/api/v3/branches.rb b/lib/api/v3/branches.rb
index 0a877b960f6684bfcf9a1e23c14f88ccd2817d1d..81b132498921113978a1c4d7ea20db7d43239d46 100644
--- a/lib/api/v3/branches.rb
+++ b/lib/api/v3/branches.rb
@@ -26,8 +26,8 @@ module API
         delete ":id/repository/branches/:branch", requirements: { branch: /.+/ } do
           authorize_push_project
 
-          result = DeleteBranchService.new(user_project, current_user).
-                   execute(params[:branch])
+          result = DeleteBranchService.new(user_project, current_user)
+                   .execute(params[:branch])
 
           if result[:status] == :success
             status(200)
@@ -55,8 +55,8 @@ module API
         end
         post ":id/repository/branches" do
           authorize_push_project
-          result = CreateBranchService.new(user_project, current_user).
-            execute(params[:branch_name], params[:ref])
+          result = CreateBranchService.new(user_project, current_user)
+            .execute(params[:branch_name], params[:ref])
 
           if result[:status] == :success
             present result[:branch],
diff --git a/lib/api/v3/entities.rb b/lib/api/v3/entities.rb
index 7c5065dee90fd2fd6e55eebbc3020bd3117ab2f8..c848f52723bd2d893d7d053f0e744aa3dc84d8df 100644
--- a/lib/api/v3/entities.rb
+++ b/lib/api/v3/entities.rb
@@ -245,9 +245,9 @@ module API
         expose :job_events, as: :build_events
         # Expose serialized properties
         expose :properties do |service, options|
-          field_names = service.fields.
-            select { |field| options[:include_passwords] || field[:type] != 'password' }.
-            map { |field| field[:name] }
+          field_names = service.fields
+            .select { |field| options[:include_passwords] || field[:type] != 'password' }
+            .map { |field| field[:name] }
           service.properties.slice(*field_names)
         end
       end
diff --git a/lib/api/v3/helpers.rb b/lib/api/v3/helpers.rb
index d9e76560d03b15463dbf7251449c92b4129cd275..4e63aa01c1a355c7434e99d227a5e42028ba4786 100644
--- a/lib/api/v3/helpers.rb
+++ b/lib/api/v3/helpers.rb
@@ -38,7 +38,10 @@ module API
           projects = projects.where(visibility_level: Gitlab::VisibilityLevel.level_value(params[:visibility]))
         end
 
-        projects = projects.where(archived: params[:archived])
+        unless params[:archived].nil?
+          projects = projects.where(archived: to_boolean(params[:archived]))
+        end
+
         projects.reorder(params[:order_by] => params[:sort])
       end
     end
diff --git a/lib/api/v3/notes.rb b/lib/api/v3/notes.rb
index 009ec5c6bbdb6026c7f1bc40909499a8a03e2a4b..23fe95e42e49113f673d7fde4dfcd63367d7c0b6 100644
--- a/lib/api/v3/notes.rb
+++ b/lib/api/v3/notes.rb
@@ -34,8 +34,8 @@ module API
                 # paginate() only works with a relation. This could lead to a
                 # mismatch between the pagination headers info and the actual notes
                 # array returned, but this is really a edge-case.
-                paginate(noteable.notes).
-                reject { |n| n.cross_reference_not_visible_for?(current_user) }
+                paginate(noteable.notes)
+                .reject { |n| n.cross_reference_not_visible_for?(current_user) }
               present notes, with: ::API::V3::Entities::Note
             else
               not_found!("Notes")
diff --git a/lib/api/v3/projects.rb b/lib/api/v3/projects.rb
index 20976b9dd08c83910fd2ee43573a5ead727b4ab9..eb090453b48ab0852e88cc675deae79941397e1b 100644
--- a/lib/api/v3/projects.rb
+++ b/lib/api/v3/projects.rb
@@ -69,7 +69,7 @@ module API
           end
 
           params :filter_params do
-            optional :archived, type: Boolean, default: false, desc: 'Limit by archived status'
+            optional :archived, type: Boolean, default: nil, desc: 'Limit by archived status'
             optional :visibility, type: String, values: %w[public internal private],
                                   desc: 'Limit by visibility'
             optional :search, type: String, desc: 'Return list of authorized projects matching the search criteria'
diff --git a/lib/api/v3/services.rb b/lib/api/v3/services.rb
index 118c6df6549d37027c84e9afc44b2007a2d1c632..2d13d6fabfd9937ea9edc352910239b9529345b4 100644
--- a/lib/api/v3/services.rb
+++ b/lib/api/v3/services.rb
@@ -608,7 +608,7 @@ module API
 
       trigger_services.each do |service_slug, settings|
         helpers do
-          def chat_command_service(project, service_slug, params)
+          def slash_command_service(project, service_slug, params)
             project.services.active.where(template: false).find do |service|
               service.try(:token) == params[:token] && service.to_param == service_slug.underscore
             end
@@ -633,7 +633,7 @@ module API
             # This is not accurate, but done to prevent leakage of the project names
             not_found!('Service') unless project
 
-            service = chat_command_service(project, service_slug, params)
+            service = slash_command_service(project, service_slug, params)
             result = service.try(:trigger, params)
 
             if result
diff --git a/lib/api/v3/tags.rb b/lib/api/v3/tags.rb
index c2541de2f5034faec75232c9d4509df148ac1041..7e5875cd030332f62ee549d01ab027e9e806ee76 100644
--- a/lib/api/v3/tags.rb
+++ b/lib/api/v3/tags.rb
@@ -22,8 +22,8 @@ module API
         delete ":id/repository/tags/:tag_name", requirements: { tag_name: /.+/ } do
           authorize_push_project
 
-          result = ::Tags::DestroyService.new(user_project, current_user).
-            execute(params[:tag_name])
+          result = ::Tags::DestroyService.new(user_project, current_user)
+            .execute(params[:tag_name])
 
           if result[:status] == :success
             status(200)
diff --git a/lib/api/v3/users.rb b/lib/api/v3/users.rb
index f4cda3b2ebacb70115bebae5fb55ec8fe6389410..37020019e07b4d185bcf66f32da5b37eccaed751 100644
--- a/lib/api/v3/users.rb
+++ b/lib/api/v3/users.rb
@@ -50,13 +50,13 @@ module API
           if user.persisted?
             present user, with: ::API::Entities::UserPublic
           else
-            conflict!('Email has already been taken') if User.
-                where(email: user.email).
-                count > 0
+            conflict!('Email has already been taken') if User
+                .where(email: user.email)
+                .count > 0
 
-            conflict!('Username has already been taken') if User.
-                where(username: user.username).
-                count > 0
+            conflict!('Username has already been taken') if User
+                .where(username: user.username)
+                .count > 0
 
             render_validation_error!(user)
           end
@@ -137,11 +137,11 @@ module API
           user = User.find_by(id: params[:id])
           not_found!('User') unless user
 
-          events = user.events.
-            merge(ProjectsFinder.new(current_user: current_user).execute).
-            references(:project).
-            with_associations.
-            recent
+          events = user.events
+            .merge(ProjectsFinder.new(current_user: current_user).execute)
+            .references(:project)
+            .with_associations
+            .recent
 
           present paginate(events), with: ::API::V3::Entities::Event
         end
diff --git a/lib/banzai/reference_extractor.rb b/lib/banzai/reference_extractor.rb
index 8e3b0c4db79361a84255bf9ee315cd82c7734a73..7e6357f8a008923224d11973b1ee05fd46085d9e 100644
--- a/lib/banzai/reference_extractor.rb
+++ b/lib/banzai/reference_extractor.rb
@@ -10,8 +10,8 @@ module Banzai
     end
 
     def references(type, project, current_user = nil)
-      processor = Banzai::ReferenceParser[type].
-        new(project, current_user)
+      processor = Banzai::ReferenceParser[type]
+        .new(project, current_user)
 
       processor.process(html_documents)
     end
diff --git a/lib/banzai/reference_parser/issue_parser.rb b/lib/banzai/reference_parser/issue_parser.rb
index 89ec715ddf6d91afbca086e4fa3f81f37da87a81..9fd4bd68d4396faaf7910c9a481cdc0cfcb61305 100644
--- a/lib/banzai/reference_parser/issue_parser.rb
+++ b/lib/banzai/reference_parser/issue_parser.rb
@@ -9,8 +9,8 @@ module Banzai
 
         issues = issues_for_nodes(nodes)
 
-        readable_issues = Ability.
-          issues_readable_by_user(issues.values, user).to_set
+        readable_issues = Ability
+          .issues_readable_by_user(issues.values, user).to_set
 
         nodes.select do |node|
           readable_issues.include?(issues[node])
diff --git a/lib/banzai/reference_parser/user_parser.rb b/lib/banzai/reference_parser/user_parser.rb
index 3efbd2fd631dd53fa20a1ae0b35d9755b6010f2c..4d336068861df64f9f241e11e4eccce66736c494 100644
--- a/lib/banzai/reference_parser/user_parser.rb
+++ b/lib/banzai/reference_parser/user_parser.rb
@@ -99,8 +99,8 @@ module Banzai
       def find_users_for_projects(ids)
         return [] if ids.empty?
 
-        collection_objects_for_ids(Project, ids).
-          flat_map { |p| p.team.members.to_a }
+        collection_objects_for_ids(Project, ids)
+          .flat_map { |p| p.team.members.to_a }
       end
 
       def can_read_reference?(user, ref_project, node)
diff --git a/lib/ci/charts.rb b/lib/ci/charts.rb
index 3decc3b1a269e609064157e46adec6c1281a4fcf..6063d6f45e8f46151e0cc23070ecf7226cd773e4 100644
--- a/lib/ci/charts.rb
+++ b/lib/ci/charts.rb
@@ -2,10 +2,10 @@ module Ci
   module Charts
     module DailyInterval
       def grouped_count(query)
-        query.
-          group("DATE(#{Ci::Build.table_name}.created_at)").
-          count(:created_at).
-          transform_keys { |date| date.strftime(@format) }
+        query
+          .group("DATE(#{Ci::Build.table_name}.created_at)")
+          .count(:created_at)
+          .transform_keys { |date| date.strftime(@format) }
       end
 
       def interval_step
@@ -16,14 +16,14 @@ module Ci
     module MonthlyInterval
       def grouped_count(query)
         if Gitlab::Database.postgresql?
-          query.
-            group("to_char(#{Ci::Build.table_name}.created_at, '01 Month YYYY')").
-            count(:created_at).
-            transform_keys(&:squish)
+          query
+            .group("to_char(#{Ci::Build.table_name}.created_at, '01 Month YYYY')")
+            .count(:created_at)
+            .transform_keys(&:squish)
         else
-          query.
-            group("DATE_FORMAT(#{Ci::Build.table_name}.created_at, '01 %M %Y')").
-            count(:created_at)
+          query
+            .group("DATE_FORMAT(#{Ci::Build.table_name}.created_at, '01 %M %Y')")
+            .count(:created_at)
         end
       end
 
@@ -46,8 +46,8 @@ module Ci
       end
 
       def collect
-        query = project.builds.
-          where("? > #{Ci::Build.table_name}.created_at AND #{Ci::Build.table_name}.created_at > ?", @to, @from)
+        query = project.builds
+          .where("? > #{Ci::Build.table_name}.created_at AND #{Ci::Build.table_name}.created_at > ?", @to, @from)
 
         totals_count  = grouped_count(query)
         success_count = grouped_count(query.success)
diff --git a/lib/github/import.rb b/lib/github/import.rb
index b20614b30602d64912eafc307d8c7beb0ea3a971..ff5d7db2705b0ee207c35abdde510bf72a1d68f6 100644
--- a/lib/github/import.rb
+++ b/lib/github/import.rb
@@ -172,7 +172,7 @@ module Github
           next unless merge_request.new_record? && pull_request.valid?
 
           begin
-            restore_branches(pull_request)
+            pull_request.restore_branches!
 
             author_id   = user_id(pull_request.author, project.creator_id)
             description = format_description(pull_request.description, pull_request.author)
@@ -208,7 +208,7 @@ module Github
           rescue => e
             error(:pull_request, pull_request.url, e.message)
           ensure
-            clean_up_restored_branches(pull_request)
+            pull_request.remove_restored_branches!
           end
         end
 
@@ -325,32 +325,6 @@ module Github
       end
     end
 
-    def restore_branches(pull_request)
-      restore_source_branch(pull_request) unless pull_request.source_branch_exists?
-      restore_target_branch(pull_request) unless pull_request.target_branch_exists?
-    end
-
-    def restore_source_branch(pull_request)
-      repository.create_branch(pull_request.source_branch_name, pull_request.source_branch_sha)
-    end
-
-    def restore_target_branch(pull_request)
-      repository.create_branch(pull_request.target_branch_name, pull_request.target_branch_sha)
-    end
-
-    def remove_branch(name)
-      repository.delete_branch(name)
-    rescue Rugged::ReferenceError
-      errors << { type: :branch, url: nil, error: "Could not clean up restored branch: #{name}" }
-    end
-
-    def clean_up_restored_branches(pull_request)
-      return if pull_request.opened?
-
-      remove_branch(pull_request.source_branch_name) unless pull_request.source_branch_exists?
-      remove_branch(pull_request.target_branch_name) unless pull_request.target_branch_exists?
-    end
-
     def label_ids(labels)
       labels.map { |attrs| cached[:label_ids][attrs.fetch('name')] }.compact
     end
diff --git a/lib/github/representation/branch.rb b/lib/github/representation/branch.rb
index d1dac6944f09da490917981891ee58bc69e00cdf..c6fa928d565f988a20a7ba9db08faa507f21f590 100644
--- a/lib/github/representation/branch.rb
+++ b/lib/github/representation/branch.rb
@@ -26,13 +26,25 @@ module Github
       end
 
       def exists?
-        branch_exists? && commit_exists?
+        @exists ||= branch_exists? && commit_exists?
       end
 
       def valid?
         sha.present? && ref.present?
       end
 
+      def restore!(name)
+        repository.create_branch(name, sha)
+      rescue Gitlab::Git::Repository::InvalidRef => e
+        Rails.logger.error("#{self.class.name}: Could not restore branch #{name}: #{e}")
+      end
+
+      def remove!(name)
+        repository.delete_branch(name)
+      rescue Rugged::ReferenceError => e
+        Rails.logger.error("#{self.class.name}: Could not remove branch #{name}: #{e}")
+      end
+
       private
 
       def branch_exists?
diff --git a/lib/github/representation/pull_request.rb b/lib/github/representation/pull_request.rb
index ac9c8283b4bc9fd5a72ccbdf53022ec71ca442ca..55461097e8a9ee05b46c29b556f3b4ab57366d3b 100644
--- a/lib/github/representation/pull_request.rb
+++ b/lib/github/representation/pull_request.rb
@@ -1,8 +1,6 @@
 module Github
   module Representation
     class PullRequest < Representation::Issuable
-      attr_reader :project
-
       delegate :user, :repo, :ref, :sha, to: :source_branch, prefix: true
       delegate :user, :exists?, :repo, :ref, :sha, :short_sha, to: :target_branch, prefix: true
 
@@ -10,10 +8,6 @@ module Github
         project
       end
 
-      def source_branch_exists?
-        !cross_project? && source_branch.exists?
-      end
-
       def source_branch_name
         @source_branch_name ||=
           if cross_project? || !source_branch_exists?
@@ -23,6 +17,12 @@ module Github
           end
       end
 
+      def source_branch_exists?
+        return @source_branch_exists if defined?(@source_branch_exists)
+
+        @source_branch_exists = !cross_project? && source_branch.exists?
+      end
+
       def target_project
         project
       end
@@ -31,6 +31,10 @@ module Github
         @target_branch_name ||= target_branch_exists? ? target_branch_ref : target_branch_name_prefixed
       end
 
+      def target_branch_exists?
+        @target_branch_exists ||= target_branch.exists?
+      end
+
       def state
         return 'merged' if raw['state'] == 'closed' && raw['merged_at'].present?
         return 'closed' if raw['state'] == 'closed'
@@ -46,6 +50,18 @@ module Github
         source_branch.valid? && target_branch.valid?
       end
 
+      def restore_branches!
+        restore_source_branch!
+        restore_target_branch!
+      end
+
+      def remove_restored_branches!
+        return if opened?
+
+        remove_source_branch!
+        remove_target_branch!
+      end
+
       private
 
       def project
@@ -73,6 +89,32 @@ module Github
 
         source_branch_repo.id != target_branch_repo.id
       end
+
+      def restore_source_branch!
+        return if source_branch_exists?
+
+        source_branch.restore!(source_branch_name)
+      end
+
+      def restore_target_branch!
+        return if target_branch_exists?
+
+        target_branch.restore!(target_branch_name)
+      end
+
+      def remove_source_branch!
+        # We should remove the source/target branches only if they were
+        # restored. Otherwise, we'll remove branches like 'master' that
+        # target_branch_exists? returns true. In other words, we need
+        # to clean up only the restored branches that (source|target)_branch_exists?
+        # returns false for the first time it has been called, because of
+        # this that is important to memoize these values.
+        source_branch.remove!(source_branch_name) unless source_branch_exists?
+      end
+
+      def remove_target_branch!
+        target_branch.remove!(target_branch_name) unless target_branch_exists?
+      end
     end
   end
 end
diff --git a/lib/gitlab/background_migration.rb b/lib/gitlab/background_migration.rb
index 914a3b72abd0b56c06cc24693e8f7ab8f7276488..d95ecd7b291278fa887595eaf0f8a4dbab4f0189 100644
--- a/lib/gitlab/background_migration.rb
+++ b/lib/gitlab/background_migration.rb
@@ -5,8 +5,8 @@ module Gitlab
     #
     # steal_class - The name of the class for which to steal jobs.
     def self.steal(steal_class)
-      queue = Sidekiq::Queue.
-        new(BackgroundMigrationWorker.sidekiq_options['queue'])
+      queue = Sidekiq::Queue
+        .new(BackgroundMigrationWorker.sidekiq_options['queue'])
 
       queue.each do |job|
         migration_class, migration_args = job.args
diff --git a/lib/gitlab/cache/ci/project_pipeline_status.rb b/lib/gitlab/cache/ci/project_pipeline_status.rb
index 4fc9a075edc864e4296a4c54ed52de112b3233e8..9c2e09943b02b95d089829759097a03fa70bb9f5 100644
--- a/lib/gitlab/cache/ci/project_pipeline_status.rb
+++ b/lib/gitlab/cache/ci/project_pipeline_status.rb
@@ -50,8 +50,8 @@ module Gitlab
             ref: pipeline.ref
           }
 
-          new(pipeline.project, pipeline_info: pipeline_info).
-            store_in_cache_if_needed
+          new(pipeline.project, pipeline_info: pipeline_info)
+            .store_in_cache_if_needed
         end
 
         def initialize(project, pipeline_info: {}, loaded_from_cache: nil)
diff --git a/lib/gitlab/ci/pipeline_duration.rb b/lib/gitlab/ci/pipeline_duration.rb
index a210e76acaa41baa7658853232b208149b14b311..3208cc2bef64aaac1fd75bb832e073f6f880a590 100644
--- a/lib/gitlab/ci/pipeline_duration.rb
+++ b/lib/gitlab/ci/pipeline_duration.rb
@@ -87,8 +87,8 @@ module Gitlab
 
       def from_pipeline(pipeline)
         status = %w[success failed running canceled]
-        builds = pipeline.builds.latest.
-          where(status: status).where.not(started_at: nil).order(:started_at)
+        builds = pipeline.builds.latest
+          .where(status: status).where.not(started_at: nil).order(:started_at)
 
         from_builds(builds)
       end
diff --git a/lib/gitlab/conflict/file_collection.rb b/lib/gitlab/conflict/file_collection.rb
index 6e73361cad13f9cf9383571077c79292b98ba373..1611eba31da9d1da8dcdb51311981e8b67665165 100644
--- a/lib/gitlab/conflict/file_collection.rb
+++ b/lib/gitlab/conflict/file_collection.rb
@@ -16,9 +16,9 @@ module Gitlab
           project = merge_request.source_project
 
           new(merge_request, project).tap do |file_collection|
-            project.
-              repository.
-              with_repo_branch_commit(merge_request.target_project.repository, merge_request.target_branch) do
+            project
+              .repository
+              .with_repo_branch_commit(merge_request.target_project.repository, merge_request.target_branch) do
 
               yield file_collection
             end
diff --git a/lib/gitlab/contributions_calendar.rb b/lib/gitlab/contributions_calendar.rb
index 060e013183fe9e2b24962ec00bd8dbbe150b03a8..bf557103cfd0892ae5c15b91627991b49c993c61 100644
--- a/lib/gitlab/contributions_calendar.rb
+++ b/lib/gitlab/contributions_calendar.rb
@@ -16,14 +16,14 @@ module Gitlab
       # Can't use Event.contributions here because we need to check 3 different
       # project_features for the (currently) 3 different contribution types
       date_from = 1.year.ago
-      repo_events = event_counts(date_from, :repository).
-        having(action: Event::PUSHED)
-      issue_events = event_counts(date_from, :issues).
-        having(action: [Event::CREATED, Event::CLOSED], target_type: "Issue")
-      mr_events = event_counts(date_from, :merge_requests).
-        having(action: [Event::MERGED, Event::CREATED, Event::CLOSED], target_type: "MergeRequest")
-      note_events = event_counts(date_from, :merge_requests).
-        having(action: [Event::COMMENTED], target_type: "Note")
+      repo_events = event_counts(date_from, :repository)
+        .having(action: Event::PUSHED)
+      issue_events = event_counts(date_from, :issues)
+        .having(action: [Event::CREATED, Event::CLOSED], target_type: "Issue")
+      mr_events = event_counts(date_from, :merge_requests)
+        .having(action: [Event::MERGED, Event::CREATED, Event::CLOSED], target_type: "MergeRequest")
+      note_events = event_counts(date_from, :merge_requests)
+        .having(action: [Event::COMMENTED], target_type: "Note")
 
       union = Gitlab::SQL::Union.new([repo_events, issue_events, mr_events, note_events])
       events = Event.find_by_sql(union.to_sql).map(&:attributes)
@@ -34,9 +34,9 @@ module Gitlab
     end
 
     def events_by_date(date)
-      events = Event.contributions.where(author_id: contributor.id).
-        where(created_at: date.beginning_of_day..date.end_of_day).
-        where(project_id: projects)
+      events = Event.contributions.where(author_id: contributor.id)
+        .where(created_at: date.beginning_of_day..date.end_of_day)
+        .where(project_id: projects)
 
       # Use visible_to_user? instead of the complicated logic in activity_dates
       # because we're only viewing the events for a single day.
@@ -60,20 +60,20 @@ module Gitlab
       # use IN(project_ids...) instead. It's the intersection of two users so
       # the list will be (relatively) short
       @contributed_project_ids ||= projects.uniq.pluck(:id)
-      authed_projects = Project.where(id: @contributed_project_ids).
-        with_feature_available_for_user(feature, current_user).
-        reorder(nil).
-        select(:id)
+      authed_projects = Project.where(id: @contributed_project_ids)
+        .with_feature_available_for_user(feature, current_user)
+        .reorder(nil)
+        .select(:id)
 
-      conditions = t[:created_at].gteq(date_from.beginning_of_day).
-        and(t[:created_at].lteq(Date.today.end_of_day)).
-        and(t[:author_id].eq(contributor.id))
+      conditions = t[:created_at].gteq(date_from.beginning_of_day)
+        .and(t[:created_at].lteq(Date.today.end_of_day))
+        .and(t[:author_id].eq(contributor.id))
 
-      Event.reorder(nil).
-        select(t[:project_id], t[:target_type], t[:action], 'date(created_at) AS date', 'count(id) as total_amount').
-        group(t[:project_id], t[:target_type], t[:action], 'date(created_at)').
-        where(conditions).
-        having(t[:project_id].in(Arel::Nodes::SqlLiteral.new(authed_projects.to_sql)))
+      Event.reorder(nil)
+        .select(t[:project_id], t[:target_type], t[:action], 'date(created_at) AS date', 'count(id) as total_amount')
+        .group(t[:project_id], t[:target_type], t[:action], 'date(created_at)')
+        .where(conditions)
+        .having(t[:project_id].in(Arel::Nodes::SqlLiteral.new(authed_projects.to_sql)))
     end
   end
 end
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index 48735fd197d68ef65c14ab9389a186ae4ec03e14..818b3d9c46b77f6eccde9fdeaba7f7668b9359cc 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -10,43 +10,49 @@ module Gitlab
 
     delegate :sidekiq_throttling_enabled?, to: :current_application_settings
 
-    def fake_application_settings
-      OpenStruct.new(::ApplicationSetting.defaults)
+    def fake_application_settings(defaults = ::ApplicationSetting.defaults)
+      FakeApplicationSettings.new(defaults)
     end
 
     private
 
     def ensure_application_settings!
-      unless ENV['IN_MEMORY_APPLICATION_SETTINGS'] == 'true'
-        settings = retrieve_settings_from_database?
-      end
+      return in_memory_application_settings if ENV['IN_MEMORY_APPLICATION_SETTINGS'] == 'true'
 
-      settings || in_memory_application_settings
+      cached_application_settings || uncached_application_settings
     end
 
-    def retrieve_settings_from_database?
-      settings = retrieve_settings_from_database_cache?
-      return settings if settings.present?
-
-      return fake_application_settings unless connect_to_db?
-
+    def cached_application_settings
       begin
-        db_settings = ::ApplicationSetting.current
-        # In case Redis isn't running or the Redis UNIX socket file is not available
+        ::ApplicationSetting.cached
       rescue ::Redis::BaseError, ::Errno::ENOENT
-        db_settings = ::ApplicationSetting.last
+        # In case Redis isn't running or the Redis UNIX socket file is not available
       end
-      db_settings || ::ApplicationSetting.create_from_defaults
     end
 
-    def retrieve_settings_from_database_cache?
+    def uncached_application_settings
+      return fake_application_settings unless connect_to_db?
+
+      # This loads from the database into the cache, so handle Redis errors
       begin
-        settings = ApplicationSetting.cached
+        db_settings = ::ApplicationSetting.current
       rescue ::Redis::BaseError, ::Errno::ENOENT
         # In case Redis isn't running or the Redis UNIX socket file is not available
-        settings = nil
       end
-      settings
+
+      # If there are pending migrations, it's possible there are columns that
+      # need to be added to the application settings. To prevent Rake tasks
+      # and other callers from failing, use any loaded settings and return
+      # defaults for missing columns.
+      if ActiveRecord::Migrator.needs_migration?
+        defaults = ::ApplicationSetting.defaults
+        defaults.merge!(db_settings.attributes.symbolize_keys) if db_settings.present?
+        return fake_application_settings(defaults)
+      end
+
+      return db_settings if db_settings.present?
+
+      ::ApplicationSetting.create_from_defaults || in_memory_application_settings
     end
 
     def in_memory_application_settings
@@ -62,8 +68,7 @@ module Gitlab
       active_db_connection = ActiveRecord::Base.connection.active? rescue false
 
       active_db_connection &&
-        ActiveRecord::Base.connection.table_exists?('application_settings') &&
-        !ActiveRecord::Migrator.needs_migration?
+        ActiveRecord::Base.connection.table_exists?('application_settings')
     rescue ActiveRecord::NoDatabaseError
       false
     end
diff --git a/lib/gitlab/cycle_analytics/base_query.rb b/lib/gitlab/cycle_analytics/base_query.rb
index d560dca45c8abbec50ecf5790b9fa0d9715e01e8..58729d3ced841ec5d956b5ef98609a8540b71b1f 100644
--- a/lib/gitlab/cycle_analytics/base_query.rb
+++ b/lib/gitlab/cycle_analytics/base_query.rb
@@ -12,17 +12,17 @@ module Gitlab
       end
 
       def stage_query
-        query = mr_closing_issues_table.join(issue_table).on(issue_table[:id].eq(mr_closing_issues_table[:issue_id])).
-          join(issue_metrics_table).on(issue_table[:id].eq(issue_metrics_table[:issue_id])).
-          where(issue_table[:project_id].eq(@project.id)).
-          where(issue_table[:deleted_at].eq(nil)).
-          where(issue_table[:created_at].gteq(@options[:from]))
+        query = mr_closing_issues_table.join(issue_table).on(issue_table[:id].eq(mr_closing_issues_table[:issue_id]))
+          .join(issue_metrics_table).on(issue_table[:id].eq(issue_metrics_table[:issue_id]))
+          .where(issue_table[:project_id].eq(@project.id))
+          .where(issue_table[:deleted_at].eq(nil))
+          .where(issue_table[:created_at].gteq(@options[:from]))
 
         # Load merge_requests
-        query = query.join(mr_table, Arel::Nodes::OuterJoin).
-          on(mr_table[:id].eq(mr_closing_issues_table[:merge_request_id])).
-          join(mr_metrics_table).
-          on(mr_table[:id].eq(mr_metrics_table[:merge_request_id]))
+        query = query.join(mr_table, Arel::Nodes::OuterJoin)
+          .on(mr_table[:id].eq(mr_closing_issues_table[:merge_request_id]))
+          .join(mr_metrics_table)
+          .on(mr_table[:id].eq(mr_metrics_table[:merge_request_id]))
 
         query
       end
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index d0bd129967143df816ae62e98e5f1033690688f8..0d5a7cf069465dc2ee432155e9b81c7702452f02 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -83,6 +83,22 @@ module Gitlab
       end
     end
 
+    def self.bulk_insert(table, rows)
+      return if rows.empty?
+
+      keys = rows.first.keys
+      columns = keys.map { |key| connection.quote_column_name(key) }
+
+      tuples = rows.map do |row|
+        row.values_at(*keys).map { |value| connection.quote(value) }
+      end
+
+      connection.execute <<-EOF.strip_heredoc
+        INSERT INTO #{table} (#{columns.join(', ')})
+        VALUES #{tuples.map { |tuple| "(#{tuple.join(', ')})" }.join(', ')}
+      EOF
+    end
+
     # pool_size - The size of the DB pool.
     # host - An optional host name to use instead of the default one.
     def self.create_connection_pool(pool_size, host = nil)
diff --git a/lib/gitlab/database/median.rb b/lib/gitlab/database/median.rb
index 23890e5f4935dc94b55562d76278a86ad3bf7c89..059054ac9ffda2ccca8b9c2a5daa32844f19f43c 100644
--- a/lib/gitlab/database/median.rb
+++ b/lib/gitlab/database/median.rb
@@ -29,10 +29,10 @@ module Gitlab
       end
 
       def mysql_median_datetime_sql(arel_table, query_so_far, column_sym)
-        query = arel_table.
-                from(arel_table.project(Arel.sql('*')).order(arel_table[column_sym]).as(arel_table.table_name)).
-                project(average([arel_table[column_sym]], 'median')).
-                where(
+        query = arel_table
+                .from(arel_table.project(Arel.sql('*')).order(arel_table[column_sym]).as(arel_table.table_name))
+                .project(average([arel_table[column_sym]], 'median'))
+                .where(
                   Arel::Nodes::Between.new(
                     Arel.sql("(select @row_id := @row_id + 1)"),
                     Arel::Nodes::And.new(
@@ -67,8 +67,8 @@ module Gitlab
         cte_table = Arel::Table.new("ordered_records")
         cte = Arel::Nodes::As.new(
           cte_table,
-          arel_table.
-            project(
+          arel_table
+            .project(
               arel_table[column_sym].as(column_sym.to_s),
               Arel::Nodes::Over.new(Arel::Nodes::NamedFunction.new("row_number", []),
                                     Arel::Nodes::Window.new.order(arel_table[column_sym])).as('row_id'),
@@ -79,8 +79,8 @@ module Gitlab
         # From the CTE, select either the middle row or the middle two rows (this is accomplished
         # by 'where cte.row_id between cte.ct / 2.0 AND cte.ct / 2.0 + 1'). Find the average of the
         # selected rows, and this is the median value.
-        cte_table.project(average([extract_epoch(cte_table[column_sym])], "median")).
-          where(
+        cte_table.project(average([extract_epoch(cte_table[column_sym])], "median"))
+          .where(
             Arel::Nodes::Between.new(
               cte_table[:row_id],
               Arel::Nodes::And.new(
@@ -88,9 +88,9 @@ module Gitlab
                  (cte_table[:ct] / Arel.sql('2.0') + 1)]
               )
             )
-          ).
-          with(query_so_far, cte).
-          to_sql
+          )
+          .with(query_so_far, cte)
+          .to_sql
       end
 
       private
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index cd85f961242c086c98e898206864c6c05b884b54..60cce9c6d9e5932d750ae2f970ad83abc962c0f6 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -233,25 +233,31 @@ module Gitlab
 
         # Update in batches of 5% until we run out of any rows to update.
         batch_size = ((total / 100.0) * 5.0).ceil
+        max_size = 1000
+
+        # The upper limit is 1000 to ensure we don't lock too many rows. For
+        # example, for "merge_requests" even 1% of the table is around 35 000
+        # rows for GitLab.com.
+        batch_size = max_size if batch_size > max_size
 
         start_arel = table.project(table[:id]).order(table[:id].asc).take(1)
         start_arel = yield table, start_arel if block_given?
         start_id = exec_query(start_arel.to_sql).to_hash.first['id'].to_i
 
         loop do
-          stop_arel = table.project(table[:id]).
-            where(table[:id].gteq(start_id)).
-            order(table[:id].asc).
-            take(1).
-            skip(batch_size)
+          stop_arel = table.project(table[:id])
+            .where(table[:id].gteq(start_id))
+            .order(table[:id].asc)
+            .take(1)
+            .skip(batch_size)
 
           stop_arel = yield table, stop_arel if block_given?
           stop_row = exec_query(stop_arel.to_sql).to_hash.first
 
-          update_arel = Arel::UpdateManager.new(ActiveRecord::Base).
-            table(table).
-            set([[table[column], value]]).
-            where(table[:id].gteq(start_id))
+          update_arel = Arel::UpdateManager.new(ActiveRecord::Base)
+            .table(table)
+            .set([[table[column], value]])
+            .where(table[:id].gteq(start_id))
 
           if stop_row
             stop_id = stop_row['id'].to_i
@@ -580,15 +586,15 @@ module Gitlab
         quoted_replacement = Arel::Nodes::Quoted.new(replacement.to_s)
 
         if Database.mysql?
-          locate = Arel::Nodes::NamedFunction.
-            new('locate', [quoted_pattern, column])
-          insert_in_place = Arel::Nodes::NamedFunction.
-            new('insert', [column, locate, pattern.size, quoted_replacement])
+          locate = Arel::Nodes::NamedFunction
+            .new('locate', [quoted_pattern, column])
+          insert_in_place = Arel::Nodes::NamedFunction
+            .new('insert', [column, locate, pattern.size, quoted_replacement])
 
           Arel::Nodes::SqlLiteral.new(insert_in_place.to_sql)
         else
-          replace = Arel::Nodes::NamedFunction.
-            new("regexp_replace", [column, quoted_pattern, quoted_replacement])
+          replace = Arel::Nodes::NamedFunction
+            .new("regexp_replace", [column, quoted_pattern, quoted_replacement])
           Arel::Nodes::SqlLiteral.new(replace.to_sql)
         end
       end
diff --git a/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base.rb b/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base.rb
index d60fd4bb5511f62e4a9c566b5927c487c22be556..d8163d7da111b7c39add4739816ee589c88b8b3a 100644
--- a/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base.rb
+++ b/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base.rb
@@ -27,8 +27,8 @@ module Gitlab
             new_full_path = join_routable_path(namespace_path, new_path)
 
             # skips callbacks & validations
-            routable.class.where(id: routable).
-              update_all(path: new_path)
+            routable.class.where(id: routable)
+              .update_all(path: new_path)
 
             rename_routes(old_full_path, new_full_path)
 
diff --git a/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces.rb b/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces.rb
index 2958ad4b8e5f63927653a4c94a6e5ecdb22dc573..da7e2cb2e858c0ae14862b0ebf08e941feed3216 100644
--- a/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces.rb
+++ b/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces.rb
@@ -18,8 +18,8 @@ module Gitlab
                          when :top_level
                            MigrationClasses::Namespace.where(parent_id: nil)
                          end
-            with_paths = MigrationClasses::Route.arel_table[:path].
-                           matches_any(path_patterns)
+            with_paths = MigrationClasses::Route.arel_table[:path]
+                           .matches_any(path_patterns)
             namespaces.joins(:route).where(with_paths)
           end
 
@@ -52,15 +52,15 @@ module Gitlab
           end
 
           def repo_paths_for_namespace(namespace)
-            projects_for_namespace(namespace).distinct.select(:repository_storage).
-              map(&:repository_storage_path)
+            projects_for_namespace(namespace).distinct.select(:repository_storage)
+              .map(&:repository_storage_path)
           end
 
           def projects_for_namespace(namespace)
             namespace_ids = child_ids_for_parent(namespace, ids: [namespace.id])
-            namespace_or_children = MigrationClasses::Project.
-                                      arel_table[:namespace_id].
-                                      in(namespace_ids)
+            namespace_or_children = MigrationClasses::Project
+                                      .arel_table[:namespace_id]
+                                      .in(namespace_ids)
             MigrationClasses::Project.where(namespace_or_children)
           end
 
diff --git a/lib/gitlab/diff/line.rb b/lib/gitlab/diff/line.rb
index bd52ae47e9ff0de69a472d59f61b9297cf0661fa..2d89ccfc354ac9f217813babe91a492cc15061c4 100644
--- a/lib/gitlab/diff/line.rb
+++ b/lib/gitlab/diff/line.rb
@@ -42,25 +42,25 @@ module Gitlab
       end
 
       def added?
-        type == 'new' || type == 'new-nonewline'
+        %w[new new-nonewline].include?(type)
       end
 
       def removed?
-        type == 'old' || type == 'old-nonewline'
-      end
-
-      def rich_text
-        @parent_file.highlight_lines! if @parent_file && !@rich_text
-
-        @rich_text
+        %w[old old-nonewline].include?(type)
       end
 
       def meta?
-        type == 'match'
+        %w[match new-nonewline old-nonewline].include?(type)
       end
 
       def discussable?
-        !['match', 'new-nonewline', 'old-nonewline'].include?(type)
+        !meta?
+      end
+
+      def rich_text
+        @parent_file.highlight_lines! if @parent_file && !@rich_text
+
+        @rich_text
       end
 
       def as_json(opts = nil)
diff --git a/lib/gitlab/diff/parallel_diff.rb b/lib/gitlab/diff/parallel_diff.rb
index 481536a380bc9118d5103141dbb198b54131a636..0cb26fa45c8acd8b7b39a1be268fb600a7449b2e 100644
--- a/lib/gitlab/diff/parallel_diff.rb
+++ b/lib/gitlab/diff/parallel_diff.rb
@@ -14,16 +14,7 @@ module Gitlab
         lines = []
         highlighted_diff_lines = diff_file.highlighted_diff_lines
         highlighted_diff_lines.each do |line|
-          if line.meta? || line.unchanged?
-            # line in the right panel is the same as in the left one
-            lines << {
-              left: line,
-              right: line
-            }
-
-            free_right_index = nil
-            i += 1
-          elsif line.removed?
+          if line.removed?
             lines << {
               left: line,
               right: nil
@@ -51,6 +42,15 @@ module Gitlab
               free_right_index = nil
               i += 1
             end
+          elsif line.meta? || line.unchanged?
+            # line in the right panel is the same as in the left one
+            lines << {
+              left: line,
+              right: line
+            }
+
+            free_right_index = nil
+            i += 1
           end
         end
 
diff --git a/lib/gitlab/downtime_check.rb b/lib/gitlab/downtime_check.rb
index ab9537ed7d701752ff9c020da8a45448c016a65a..941244694e2c028d6fb993417be9f68e215231ef 100644
--- a/lib/gitlab/downtime_check.rb
+++ b/lib/gitlab/downtime_check.rb
@@ -50,8 +50,8 @@ module Gitlab
 
     # Returns the class for the given migration file path.
     def class_for_migration_file(path)
-      File.basename(path, File.extname(path)).split('_', 2).last.camelize.
-        constantize
+      File.basename(path, File.extname(path)).split('_', 2).last.camelize
+        .constantize
     end
 
     # Returns true if the given migration can be performed without downtime.
diff --git a/lib/gitlab/email/html_parser.rb b/lib/gitlab/email/html_parser.rb
index a4ca62bfc411937e45afbf94c1342bc9a57f64c0..50559a489734a72f1355c5eb3a6d46e86a259d46 100644
--- a/lib/gitlab/email/html_parser.rb
+++ b/lib/gitlab/email/html_parser.rb
@@ -17,6 +17,13 @@ module Gitlab
       def filter_replies!
         document.xpath('//blockquote').each(&:remove)
         document.xpath('//table').each(&:remove)
+
+        # bogus links with no href are sometimes added by outlook,
+        # and can result in Html2Text adding extra square brackets
+        # to the text, so we unwrap them here.
+        document.xpath('//a[not(@href)]').each do |link|
+          link.replace(link.children)
+        end
       end
 
       def filtered_html
diff --git a/lib/gitlab/fake_application_settings.rb b/lib/gitlab/fake_application_settings.rb
new file mode 100644
index 0000000000000000000000000000000000000000..bb14a8cd9e7965679e4547a4ae16d2f7a40f54dc
--- /dev/null
+++ b/lib/gitlab/fake_application_settings.rb
@@ -0,0 +1,27 @@
+# This class extends an OpenStruct object by adding predicate methods to mimic
+# ActiveRecord access. We rely on the initial values being true or false to
+# determine whether to define a predicate method because for a newly-added
+# column that has not been migrated yet, there is no way to determine the
+# column type without parsing db/schema.rb.
+module Gitlab
+  class FakeApplicationSettings < OpenStruct
+    def initialize(options = {})
+      super
+
+      FakeApplicationSettings.define_predicate_methods(options)
+    end
+
+    # Mimic ActiveRecord predicate methods for boolean values
+    def self.define_predicate_methods(options)
+      options.each do |key, value|
+        next if key.to_s.end_with?('?')
+        next unless [true, false].include?(value)
+
+        define_method "#{key}?" do
+          actual_key = key.to_s.chomp('?')
+          self[actual_key]
+        end
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/git/blob.rb b/lib/gitlab/git/blob.rb
index 33a7624e3034b0e9925ca0e1511a4cd48e8d5bb4..a7aceab4c1413b2da36db4011b588ce1e1748889 100644
--- a/lib/gitlab/git/blob.rb
+++ b/lib/gitlab/git/blob.rb
@@ -14,6 +14,51 @@ module Gitlab
 
       class << self
         def find(repository, sha, path)
+          Gitlab::GitalyClient.migrate(:project_raw_show) do |is_enabled|
+            if is_enabled
+              find_by_gitaly(repository, sha, path)
+            else
+              find_by_rugged(repository, sha, path)
+            end
+          end
+        end
+
+        def find_by_gitaly(repository, sha, path)
+          path = path.sub(/\A\/*/, '')
+          path = '/' if path.empty?
+          name = File.basename(path)
+          entry = Gitlab::GitalyClient::Commit.new(repository).tree_entry(sha, path, MAX_DATA_DISPLAY_SIZE)
+          return unless entry
+
+          case entry.type
+          when :COMMIT
+            new(
+              id: entry.oid,
+              name: name,
+              size: 0,
+              data: '',
+              path: path,
+              commit_id: sha
+            )
+          when :BLOB
+            # EncodingDetector checks the first 1024 * 1024 bytes for NUL byte, libgit2 checks
+            # only the first 8000 (https://github.com/libgit2/libgit2/blob/2ed855a9e8f9af211e7274021c2264e600c0f86b/src/filter.h#L15),
+            # which is what we use below to keep a consistent behavior.
+            detect = CharlockHolmes::EncodingDetector.new(8000).detect(entry.data)
+            new(
+              id: entry.oid,
+              name: name,
+              size: entry.size,
+              data: entry.data.dup,
+              mode: entry.mode.to_s(8),
+              path: path,
+              commit_id: sha,
+              binary: detect && detect[:type] == :binary
+            )
+          end
+        end
+
+        def find_by_rugged(repository, sha, path)
           commit = repository.lookup(sha)
           root_tree = commit.tree
 
diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb
index bb04731f08cbd787a18794c7fab8a689ef76ada5..d5d149f1423a2bc498dad9d185cab91f96cd856e 100644
--- a/lib/gitlab/git/commit.rb
+++ b/lib/gitlab/git/commit.rb
@@ -4,7 +4,7 @@ module Gitlab
     class Commit
       include Gitlab::EncodingHelper
 
-      attr_accessor :raw_commit, :head, :refs
+      attr_accessor :raw_commit, :head
 
       SERIALIZE_KEYS = [
         :id, :message, :parent_ids,
diff --git a/lib/gitlab/git/diff.rb b/lib/gitlab/git/diff.rb
index 4b689f0e94f649e429f693a66960bf718486151c..f825568f1940218c4fb158411f924a7c45734c83 100644
--- a/lib/gitlab/git/diff.rb
+++ b/lib/gitlab/git/diff.rb
@@ -16,11 +16,11 @@ module Gitlab
       alias_method :renamed_file?, :renamed_file
 
       attr_accessor :expanded
+      attr_writer :too_large
 
       alias_method :expanded?, :expanded
 
-      # We need this accessor because of `to_hash` and `init_from_hash`
-      attr_accessor :too_large
+      SERIALIZE_KEYS = %i(diff new_path old_path a_mode b_mode new_file renamed_file deleted_file too_large).freeze
 
       class << self
         # The maximum size of a diff to display.
@@ -231,16 +231,10 @@ module Gitlab
         end
       end
 
-      def serialize_keys
-        @serialize_keys ||= %i(diff new_path old_path a_mode b_mode new_file renamed_file deleted_file too_large)
-      end
-
       def to_hash
         hash = {}
 
-        keys = serialize_keys
-
-        keys.each do |key|
+        SERIALIZE_KEYS.each do |key|
           hash[key] = send(key)
         end
 
@@ -267,6 +261,9 @@ module Gitlab
         end
       end
 
+      # This is used by `to_hash` and `init_from_hash`.
+      alias_method :too_large, :too_large?
+
       def too_large!
         @diff = ''
         @line_count = 0
@@ -315,7 +312,7 @@ module Gitlab
       def init_from_hash(hash)
         raw_diff = hash.symbolize_keys
 
-        serialize_keys.each do |key|
+        SERIALIZE_KEYS.each do |key|
           send(:"#{key}=", raw_diff[key.to_sym])
         end
       end
diff --git a/lib/gitlab/git/gitmodules_parser.rb b/lib/gitlab/git/gitmodules_parser.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f4e3b5e5129de20fbc8481d481301dcad65bdbc0
--- /dev/null
+++ b/lib/gitlab/git/gitmodules_parser.rb
@@ -0,0 +1,77 @@
+module Gitlab
+  module Git
+    class GitmodulesParser
+      def initialize(content)
+        @content = content
+      end
+
+      # Parses the contents of a .gitmodules file and returns a hash of
+      # submodule information, indexed by path.
+      def parse
+        reindex_by_path(get_submodules_by_name)
+      end
+
+      private
+
+      class State
+        def initialize
+          @result = {}
+          @current_submodule = nil
+        end
+
+        def start_section(section)
+          # In some .gitmodules files (e.g. nodegit's), a header
+          # with the same name appears multiple times; we want to
+          # accumulate the configs across these
+          @current_submodule = @result[section] || { 'name' => section }
+          @result[section] = @current_submodule
+        end
+
+        def set_attribute(attr, value)
+          @current_submodule[attr] = value
+        end
+
+        def section_started?
+          !@current_submodule.nil?
+        end
+
+        def submodules_by_name
+          @result
+        end
+      end
+
+      def get_submodules_by_name
+        iterator = State.new
+
+        @content.split("\n").each_with_object(iterator) do |text, iterator|
+          next if text =~ /^\s*#/
+
+          if text =~ /\A\[submodule "(?<name>[^"]+)"\]\z/
+            iterator.start_section($~[:name])
+          else
+            next unless iterator.section_started?
+
+            next unless text =~ /\A\s*(?<key>\w+)\s*=\s*(?<value>.*)\z/
+
+            value = $~[:value].chomp
+            iterator.set_attribute($~[:key], value)
+          end
+        end
+
+        iterator.submodules_by_name
+      end
+
+      def reindex_by_path(submodules_by_name)
+        # Convert from an indexed by name to an array indexed by path
+        # If a submodule doesn't have a path, it is considered bogus
+        # and is ignored
+        submodules_by_name.each_with_object({}) do |(name, data), results|
+          path = data.delete 'path'
+          next unless path
+
+          results[path] = data
+        end
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 85695d0a4df9fcf407bd441850e8a32717ea2cdb..c1f942f931a7bc582a87522b732a2cf9b097ef1f 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -617,9 +617,9 @@ module Gitlab
       #
       # Ex.
       #   {
-      #     "rack"  => {
+      #     "current_path/rack"  => {
+      #       "name" => "original_path/rack",
       #       "id" => "c67be4624545b4263184c4a0e8f887efd0a66320",
-      #       "path" => "rack",
       #       "url" => "git://github.com/chneukirchen/rack.git"
       #     },
       #     "encoding" => {
@@ -637,7 +637,8 @@ module Gitlab
           return {}
         end
 
-        parse_gitmodules(commit, content)
+        parser = GitmodulesParser.new(content)
+        fill_submodule_ids(commit, parser.parse)
       end
 
       # Return total commits count accessible from passed ref
@@ -998,42 +999,19 @@ module Gitlab
         end
       end
 
-      # Parses the contents of a .gitmodules file and returns a hash of
-      # submodule information.
-      def parse_gitmodules(commit, content)
-        modules = {}
-
-        name = nil
-        content.each_line do |line|
-          case line.strip
-          when /\A\[submodule "(?<name>[^"]+)"\]\z/ # Submodule header
-            name = $~[:name]
-            modules[name] = {}
-          when /\A(?<key>\w+)\s*=\s*(?<value>.*)\z/ # Key/value pair
-            key = $~[:key]
-            value = $~[:value].chomp
-
-            next unless name && modules[name]
-
-            modules[name][key] = value
-
-            if key == 'path'
-              begin
-                modules[name]['id'] = blob_content(commit, value)
-              rescue InvalidBlobName
-                # The current entry is invalid
-                modules.delete(name)
-                name = nil
-              end
-            end
-          when /\A#/ # Comment
-            next
-          else # Invalid line
-            name = nil
+      # Fill in the 'id' field of a submodule hash from its values
+      # as-of +commit+. Return a Hash consisting only of entries
+      # from the submodule hash for which the 'id' field is filled.
+      def fill_submodule_ids(commit, submodule_data)
+        submodule_data.each do |path, data|
+          id = begin
+            blob_content(commit, path)
+          rescue InvalidBlobName
+            nil
           end
+          data['id'] = id
         end
-
-        modules
+        submodule_data.select { |path, data| data['id'] }
       end
 
       # Returns true if +commit+ introduced changes to +path+, using commit
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index 0a19d24eb2005c5a7fc216c0732c7f0b20213c36..0b62911958dfb0e4642149864af85917761c81a0 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -22,12 +22,13 @@ module Gitlab
     PUSH_COMMANDS = %w{ git-receive-pack }.freeze
     ALL_COMMANDS = DOWNLOAD_COMMANDS + PUSH_COMMANDS
 
-    attr_reader :actor, :project, :protocol, :authentication_abilities
+    attr_reader :actor, :project, :protocol, :authentication_abilities, :redirected_path
 
-    def initialize(actor, project, protocol, authentication_abilities:)
+    def initialize(actor, project, protocol, authentication_abilities:, redirected_path: nil)
       @actor    = actor
       @project  = project
       @protocol = protocol
+      @redirected_path = redirected_path
       @authentication_abilities = authentication_abilities
     end
 
@@ -35,6 +36,7 @@ module Gitlab
       check_protocol!
       check_active_user!
       check_project_accessibility!
+      check_project_moved!
       check_command_disabled!(cmd)
       check_command_existence!(cmd)
       check_repository_existence!
@@ -87,6 +89,21 @@ module Gitlab
       end
     end
 
+    def check_project_moved!
+      if redirected_path
+        url = protocol == 'ssh' ? project.ssh_url_to_repo : project.http_url_to_repo
+        message = <<-MESSAGE.strip_heredoc
+          Project '#{redirected_path}' was moved to '#{project.full_path}'.
+
+          Please update your Git remote and try again:
+
+            git remote set-url origin #{url}
+        MESSAGE
+
+        raise NotFoundError, message
+      end
+    end
+
     def check_command_disabled!(cmd)
       if upload_pack?(cmd)
         check_upload_pack_disabled!
diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb
index 2343446bf223f39edede2aba17e5416fab5e8f87..f605c06dfc31527a5ce618245ac3a488744d2ee7 100644
--- a/lib/gitlab/gitaly_client.rb
+++ b/lib/gitlab/gitaly_client.rb
@@ -1,3 +1,5 @@
+require 'base64'
+
 require 'gitaly'
 
 module Gitlab
@@ -48,6 +50,26 @@ module Gitlab
       address
     end
 
+    # All Gitaly RPC call sites should use GitalyClient.call. This method
+    # makes sure that per-request authentication headers are set.
+    def self.call(storage, service, rpc, request)
+      metadata = request_metadata(storage)
+      metadata = yield(metadata) if block_given?
+      stub(service, storage).send(rpc, request, metadata)
+    end
+  
+    def self.request_metadata(storage)
+      encoded_token = Base64.strict_encode64(token(storage).to_s)
+      { metadata: { 'authorization' => "Bearer #{encoded_token}" } }
+    end
+
+    def self.token(storage)
+      params = Gitlab.config.repositories.storages[storage]
+      raise "storage not found: #{storage.inspect}" if params.nil?
+
+      params['gitaly_token'].presence || Gitlab.config.gitaly['token']
+    end
+
     def self.enabled?
       Gitlab.config.gitaly.enabled
     end
diff --git a/lib/gitlab/gitaly_client/commit.rb b/lib/gitlab/gitaly_client/commit.rb
index ba3da781dade7ed76e1da30600352a9b4544d8b9..b88776197974590ff8ab818dbfb903dc68c4fdf4 100644
--- a/lib/gitlab/gitaly_client/commit.rb
+++ b/lib/gitlab/gitaly_client/commit.rb
@@ -11,33 +11,51 @@ module Gitlab
       end
 
       def is_ancestor(ancestor_id, child_id)
-        stub = GitalyClient.stub(:commit, @repository.storage)
         request = Gitaly::CommitIsAncestorRequest.new(
           repository: @gitaly_repo,
           ancestor_id: ancestor_id,
           child_id: child_id
         )
 
-        stub.commit_is_ancestor(request).value
+        GitalyClient.call(@repository.storage, :commit, :commit_is_ancestor, request).value
       end
 
       def diff_from_parent(commit, options = {})
         request_params = commit_diff_request_params(commit, options)
         request_params[:ignore_whitespace_change] = options.fetch(:ignore_whitespace_change, false)
-
-        response = diff_service_stub.commit_diff(Gitaly::CommitDiffRequest.new(request_params))
+        request = Gitaly::CommitDiffRequest.new(request_params)
+        response = GitalyClient.call(@repository.storage, :diff, :commit_diff, request)
         Gitlab::Git::DiffCollection.new(GitalyClient::DiffStitcher.new(response), options)
       end
 
       def commit_deltas(commit)
-        request_params = commit_diff_request_params(commit)
-
-        response = diff_service_stub.commit_delta(Gitaly::CommitDeltaRequest.new(request_params))
+        request = Gitaly::CommitDeltaRequest.new(commit_diff_request_params(commit))
+        response = GitalyClient.call(@repository.storage, :diff, :commit_delta, request)
         response.flat_map do |msg|
           msg.deltas.map { |d| Gitlab::Git::Diff.new(d) }
         end
       end
 
+      def tree_entry(ref, path, limit = nil)
+        request = Gitaly::TreeEntryRequest.new(
+          repository: @gitaly_repo,
+          revision: ref,
+          path: path.dup.force_encoding(Encoding::ASCII_8BIT),
+          limit: limit.to_i
+        )
+
+        response = GitalyClient.call(@repository.storage, :commit, :tree_entry, request)
+        entry = response.first
+        return unless entry.oid.present?
+
+        if entry.type == :BLOB
+          rest_of_data = response.reduce("") { |memo, msg| memo << msg.data }
+          entry.data += rest_of_data
+        end
+
+        entry
+      end
+
       private
 
       def commit_diff_request_params(commit, options = {})
@@ -50,10 +68,6 @@ module Gitlab
           paths: options.fetch(:paths, [])
         }
       end
-
-      def diff_service_stub
-        GitalyClient.stub(:diff, @repository.storage)
-      end
     end
   end
 end
diff --git a/lib/gitlab/gitaly_client/notifications.rb b/lib/gitlab/gitaly_client/notifications.rb
index 719554eac52772acc12ed1fd3ad00f4526f7073a..78ed433e6b8658ad2faa38522b71bd3fc95183e4 100644
--- a/lib/gitlab/gitaly_client/notifications.rb
+++ b/lib/gitlab/gitaly_client/notifications.rb
@@ -1,17 +1,19 @@
 module Gitlab
   module GitalyClient
     class Notifications
-      attr_accessor :stub
-
       # 'repository' is a Gitlab::Git::Repository
       def initialize(repository)
         @gitaly_repo = repository.gitaly_repository
-        @stub = GitalyClient.stub(:notifications, repository.storage)
+        @storage = repository.storage
       end
 
       def post_receive
-        request = Gitaly::PostReceiveRequest.new(repository: @gitaly_repo)
-        @stub.post_receive(request)
+        GitalyClient.call(
+          @storage,
+          :notifications,
+          :post_receive,
+          Gitaly::PostReceiveRequest.new(repository: @gitaly_repo)
+        )
       end
     end
   end
diff --git a/lib/gitlab/gitaly_client/ref.rb b/lib/gitlab/gitaly_client/ref.rb
index 227fe45642ecca1dc96cf19e048a46c8a6615991..6d5f54dd9599092ff0eb363634c0c38cf8cf43e7 100644
--- a/lib/gitlab/gitaly_client/ref.rb
+++ b/lib/gitlab/gitaly_client/ref.rb
@@ -1,29 +1,28 @@
 module Gitlab
   module GitalyClient
     class Ref
-      attr_accessor :stub
-
       # 'repository' is a Gitlab::Git::Repository
       def initialize(repository)
         @gitaly_repo = repository.gitaly_repository
-        @stub = GitalyClient.stub(:ref, repository.storage)
+        @storage = repository.storage
       end
 
       def default_branch_name
         request = Gitaly::FindDefaultBranchNameRequest.new(repository: @gitaly_repo)
-        branch_name = stub.find_default_branch_name(request).name
-
-        Gitlab::Git.branch_name(branch_name)
+        response = GitalyClient.call(@storage, :ref, :find_default_branch_name, request)
+        Gitlab::Git.branch_name(response.name)
       end
 
       def branch_names
         request = Gitaly::FindAllBranchNamesRequest.new(repository: @gitaly_repo)
-        consume_refs_response(stub.find_all_branch_names(request), prefix: 'refs/heads/')
+        response = GitalyClient.call(@storage, :ref, :find_all_branch_names, request)
+        consume_refs_response(response, prefix: 'refs/heads/')
       end
 
       def tag_names
         request = Gitaly::FindAllTagNamesRequest.new(repository: @gitaly_repo)
-        consume_refs_response(stub.find_all_tag_names(request), prefix: 'refs/tags/')
+        response = GitalyClient.call(@storage, :ref, :find_all_tag_names, request)
+        consume_refs_response(response, prefix: 'refs/tags/')
       end
 
       def find_ref_name(commit_id, ref_prefix)
@@ -32,8 +31,7 @@ module Gitlab
           commit_id: commit_id,
           prefix: ref_prefix
         )
-
-        stub.find_ref_name(request).name
+        GitalyClient.call(@storage, :ref, :find_ref_name, request).name
       end
 
       def count_tag_names
@@ -47,7 +45,8 @@ module Gitlab
       def local_branches(sort_by: nil)
         request = Gitaly::FindLocalBranchesRequest.new(repository: @gitaly_repo)
         request.sort_by = sort_by_param(sort_by) if sort_by
-        consume_branches_response(stub.find_local_branches(request))
+        response = GitalyClient.call(@storage, :ref, :find_local_branches, request)
+        consume_branches_response(response)
       end
 
       private
diff --git a/lib/gitlab/group_hierarchy.rb b/lib/gitlab/group_hierarchy.rb
index e9d5d52cabb3cea76669c642648480574fe76832..5a31e56cb3077e2de9b10d775abf86731a3d1d70 100644
--- a/lib/gitlab/group_hierarchy.rb
+++ b/lib/gitlab/group_hierarchy.rb
@@ -3,33 +3,38 @@ module Gitlab
   #
   # This class uses recursive CTEs and as a result will only work on PostgreSQL.
   class GroupHierarchy
-    attr_reader :base, :model
-
-    # base - An instance of ActiveRecord::Relation for which to get parent or
-    #        child groups.
-    def initialize(base)
-      @base = base
-      @model = base.model
+    attr_reader :ancestors_base, :descendants_base, :model
+
+    # ancestors_base - An instance of ActiveRecord::Relation for which to
+    #                  get parent groups.
+    # descendants_base - An instance of ActiveRecord::Relation for which to
+    #                    get child groups. If omitted, ancestors_base is used.
+    def initialize(ancestors_base, descendants_base = ancestors_base)
+      raise ArgumentError.new("Model of ancestors_base does not match model of descendants_base") if ancestors_base.model != descendants_base.model
+
+      @ancestors_base = ancestors_base
+      @descendants_base = descendants_base
+      @model = ancestors_base.model
     end
 
-    # Returns a relation that includes the base set of groups and all their
-    # ancestors (recursively).
+    # Returns a relation that includes the ancestors_base set of groups
+    # and all their ancestors (recursively).
     def base_and_ancestors
-      return model.none unless Group.supports_nested_groups?
+      return ancestors_base unless Group.supports_nested_groups?
 
       base_and_ancestors_cte.apply_to(model.all)
     end
 
-    # Returns a relation that includes the base set of groups and all their
-    # descendants (recursively).
+    # Returns a relation that includes the descendants_base set of groups
+    # and all their descendants (recursively).
     def base_and_descendants
-      return model.none unless Group.supports_nested_groups?
+      return descendants_base unless Group.supports_nested_groups?
 
       base_and_descendants_cte.apply_to(model.all)
     end
 
-    # Returns a relation that includes the base groups, their ancestors, and the
-    # descendants of the base groups.
+    # Returns a relation that includes the base groups, their ancestors,
+    # and the descendants of the base groups.
     #
     # The resulting query will roughly look like the following:
     #
@@ -48,8 +53,10 @@ module Gitlab
     #
     # Using this approach allows us to further add criteria to the relation with
     # Rails thinking it's selecting data the usual way.
+    #
+    # If nested groups are not supported, ancestors_base is returned.
     def all_groups
-      return base unless Group.supports_nested_groups?
+      return ancestors_base unless Group.supports_nested_groups?
 
       ancestors = base_and_ancestors_cte
       descendants = base_and_descendants_cte
@@ -60,11 +67,11 @@ module Gitlab
       union = SQL::Union.new([model.unscoped.from(ancestors_table),
                               model.unscoped.from(descendants_table)])
 
-      model.
-        unscoped.
-        with.
-        recursive(ancestors.to_arel, descendants.to_arel).
-        from("(#{union.to_sql}) #{model.table_name}")
+      model
+        .unscoped
+        .with
+        .recursive(ancestors.to_arel, descendants.to_arel)
+        .from("(#{union.to_sql}) #{model.table_name}")
     end
 
     private
@@ -72,13 +79,13 @@ module Gitlab
     def base_and_ancestors_cte
       cte = SQL::RecursiveCTE.new(:base_and_ancestors)
 
-      cte << base.except(:order)
+      cte << ancestors_base.except(:order)
 
       # Recursively get all the ancestors of the base set.
-      cte << model.
-        from([groups_table, cte.table]).
-        where(groups_table[:id].eq(cte.table[:parent_id])).
-        except(:order)
+      cte << model
+        .from([groups_table, cte.table])
+        .where(groups_table[:id].eq(cte.table[:parent_id]))
+        .except(:order)
 
       cte
     end
@@ -86,13 +93,13 @@ module Gitlab
     def base_and_descendants_cte
       cte = SQL::RecursiveCTE.new(:base_and_descendants)
 
-      cte << base.except(:order)
+      cte << descendants_base.except(:order)
 
       # Recursively get all the descendants of the base set.
-      cte << model.
-        from([groups_table, cte.table]).
-        where(groups_table[:parent_id].eq(cte.table[:id])).
-        except(:order)
+      cte << model
+        .from([groups_table, cte.table])
+        .where(groups_table[:parent_id].eq(cte.table[:id]))
+        .except(:order)
 
       cte
     end
diff --git a/lib/gitlab/highlight.rb b/lib/gitlab/highlight.rb
index 6b24da030dff01665a39605b0e340d4ed179b5a6..5408a1a68384ae98f3770885d1bec05dfa69f0d4 100644
--- a/lib/gitlab/highlight.rb
+++ b/lib/gitlab/highlight.rb
@@ -1,8 +1,8 @@
 module Gitlab
   class Highlight
     def self.highlight(blob_name, blob_content, repository: nil, plain: false)
-      new(blob_name, blob_content, repository: repository).
-        highlight(blob_content, continue: false, plain: plain)
+      new(blob_name, blob_content, repository: repository)
+        .highlight(blob_content, continue: false, plain: plain)
     end
 
     attr_reader :blob_name
diff --git a/lib/gitlab/import_export.rb b/lib/gitlab/import_export.rb
index 27d5a9198b69d22a30257b42733798c920944010..3470a09eaf0338973b467bab406b25376b33e36f 100644
--- a/lib/gitlab/import_export.rb
+++ b/lib/gitlab/import_export.rb
@@ -3,7 +3,7 @@ module Gitlab
     extend self
 
     # For every version update, the version history in import_export.md has to be kept up to date.
-    VERSION = '0.1.7'.freeze
+    VERSION = '0.1.8'.freeze
     FILENAME_LIMIT = 50
 
     def export_path(relative_path:)
diff --git a/lib/gitlab/import_export/import_export.yml b/lib/gitlab/import_export/import_export.yml
index ff2b1d08c3c393957be114b184f5cf849862f404..72183e8aad4920f95e91ec386f962cebe01b6124 100644
--- a/lib/gitlab/import_export/import_export.yml
+++ b/lib/gitlab/import_export/import_export.yml
@@ -26,7 +26,8 @@ project_tree:
     - notes:
       - :author
       - :events
-    - :merge_request_diff
+    - merge_request_diff:
+      - :merge_request_diff_files
     - :events
     - :timelogs
     - label_links:
@@ -92,6 +93,8 @@ excluded_attributes:
     - :expired_at
   merge_request_diff:
     - :st_diffs
+  merge_request_diff_files:
+    - :diff
   issues:
     - :milestone_id
   merge_requests:
@@ -113,6 +116,8 @@ methods:
     - :type
   merge_request_diff:
     - :utf8_st_diffs
+  merge_request_diff_files:
+    - :utf8_diff
   merge_requests:
     - :diff_head_sha
   project:
diff --git a/lib/gitlab/import_export/json_hash_builder.rb b/lib/gitlab/import_export/json_hash_builder.rb
index 48c09dafcb6b76dc02e3c3f6dd845827d53559f2..b48f63bcd7ec54870b524f2dacd2bb44a7c6e372 100644
--- a/lib/gitlab/import_export/json_hash_builder.rb
+++ b/lib/gitlab/import_export/json_hash_builder.rb
@@ -83,7 +83,9 @@ module Gitlab
       # +value+ existing model to be included in the hash
       # +json_config_hash+ the original hash containing the root model
       def add_model_value(current_key, value, json_config_hash)
-        @attributes_finder.parse(value) { |hash| value = { value => hash } }
+        @attributes_finder.parse(value) do |hash|
+          value = { value => hash } unless value.is_a?(Hash)
+        end
 
         add_to_array(current_key, json_config_hash, value)
       end
diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb
index 695852526cb66ee148948ce0f0e809036f886aa3..205804590460c75b1cb706af3ee2d8adb34623fc 100644
--- a/lib/gitlab/import_export/relation_factory.rb
+++ b/lib/gitlab/import_export/relation_factory.rb
@@ -71,6 +71,7 @@ module Gitlab
 
         @relation_hash['data'].deep_symbolize_keys! if @relation_name == :events && @relation_hash['data']
         set_st_diff_commits if @relation_name == :merge_request_diff
+        set_diff if @relation_name == :merge_request_diff_files
       end
 
       def update_user_references
@@ -202,6 +203,10 @@ module Gitlab
         HashUtil.deep_symbolize_array_with_date!(@relation_hash['st_commits'])
       end
 
+      def set_diff
+        @relation_hash['diff'] = @relation_hash.delete('utf8_diff')
+      end
+
       def existing_or_new_object
         # Only find existing records to avoid mapping tables such as milestones
         # Otherwise always create the record, skipping the extra SELECT clause.
diff --git a/lib/gitlab/job_waiter.rb b/lib/gitlab/job_waiter.rb
index 8db91d25a4bc586b97072eff1f411fb235cc087c..208f0e1bbeaeb9854dc87688732a7e825e09c322 100644
--- a/lib/gitlab/job_waiter.rb
+++ b/lib/gitlab/job_waiter.rb
@@ -14,7 +14,7 @@ module Gitlab
     # timeout - The maximum amount of seconds to block the caller for. This
     #           ensures we don't indefinitely block a caller in case a job takes
     #           long to process, or is never processed.
-    def wait(timeout = 60)
+    def wait(timeout = 10)
       start = Time.current
 
       while (Time.current - start) <= timeout
diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb
index 5e299e26c54058ce94a7b48f8957edd67eac4691..39180dc17d992cd1f19434060a5908715726795a 100644
--- a/lib/gitlab/ldap/user.rb
+++ b/lib/gitlab/ldap/user.rb
@@ -10,9 +10,9 @@ module Gitlab
       class << self
         def find_by_uid_and_provider(uid, provider)
           # LDAP distinguished name is case-insensitive
-          identity = ::Identity.
-            where(provider: provider).
-            iwhere(extern_uid: uid).last
+          identity = ::Identity
+            .where(provider: provider)
+            .iwhere(extern_uid: uid).last
           identity && identity.user
         end
       end
diff --git a/lib/gitlab/metrics/influx_db.rb b/lib/gitlab/metrics/influx_db.rb
index 3a39791edbfb1f37ba792b4a2d14885d4988845a..d7c56463aac4488c15a773659f83d35fe205f52b 100644
--- a/lib/gitlab/metrics/influx_db.rb
+++ b/lib/gitlab/metrics/influx_db.rb
@@ -157,8 +157,8 @@ module Gitlab
                 host = settings[:host]
                 port = settings[:port]
 
-                InfluxDB::Client.
-                  new(udp: { host: host, port: port })
+                InfluxDB::Client
+                  .new(udp: { host: host, port: port })
               end
             end
           end
diff --git a/lib/gitlab/metrics/prometheus.rb b/lib/gitlab/metrics/prometheus.rb
index 606865093324fe7285fd4cbae746b220e7180eed..9d314a56e5890db90b3188dd268a9881fdf20a40 100644
--- a/lib/gitlab/metrics/prometheus.rb
+++ b/lib/gitlab/metrics/prometheus.rb
@@ -5,8 +5,16 @@ module Gitlab
     module Prometheus
       include Gitlab::CurrentSettings
 
+      def metrics_folder_present?
+        ENV.has_key?('prometheus_multiproc_dir') &&
+          ::Dir.exist?(ENV['prometheus_multiproc_dir']) &&
+          ::File.writable?(ENV['prometheus_multiproc_dir'])
+      end
+
       def prometheus_metrics_enabled?
-        @prometheus_metrics_enabled ||= current_application_settings[:prometheus_metrics_enabled] || false
+        return @prometheus_metrics_enabled if defined?(@prometheus_metrics_enabled)
+
+        @prometheus_metrics_enabled = prometheus_metrics_enabled_unmemoized
       end
 
       def registry
@@ -36,6 +44,12 @@ module Gitlab
           NullMetric.new
         end
       end
+
+      private
+
+      def prometheus_metrics_enabled_unmemoized
+        metrics_folder_present? && current_application_settings[:prometheus_metrics_enabled] || false
+      end
     end
   end
 end
diff --git a/lib/gitlab/metrics/system.rb b/lib/gitlab/metrics/system.rb
index 3aaebb3e9c3e64eda8c52eedc54f12292db64ae5..aba3e0df382e7ad58dfc6332fd77c40ae6b40c79 100644
--- a/lib/gitlab/metrics/system.rb
+++ b/lib/gitlab/metrics/system.rb
@@ -34,13 +34,13 @@ module Gitlab
       # THREAD_CPUTIME is not supported on OS X
       if Process.const_defined?(:CLOCK_THREAD_CPUTIME_ID)
         def self.cpu_time
-          Process.
-            clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :millisecond)
+          Process
+            .clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :millisecond)
         end
       else
         def self.cpu_time
-          Process.
-            clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :millisecond)
+          Process
+            .clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :millisecond)
         end
       end
 
diff --git a/lib/gitlab/other_markup.rb b/lib/gitlab/other_markup.rb
index 31a24460f0f7e591a260b56ca846884db04b5e5d..fc3f21233dd82442b5577f232aef72fc0a8c0830 100644
--- a/lib/gitlab/other_markup.rb
+++ b/lib/gitlab/other_markup.rb
@@ -6,8 +6,8 @@ module Gitlab
     # input         - the source text in a markup format
     #
     def self.render(file_name, input, context)
-      html = GitHub::Markup.render(file_name, input).
-        force_encoding(input.encoding)
+      html = GitHub::Markup.render(file_name, input)
+        .force_encoding(input.encoding)
       context[:pipeline] = :markup
 
       html = Banzai.render(html, context)
diff --git a/lib/gitlab/performance_bar/peek_query_tracker.rb b/lib/gitlab/performance_bar/peek_query_tracker.rb
index 7ab80f5ee0fc46ee24195c19e5f5f18fc3746d30..574ae8731a543c006509a3f81c7ce276e554eda1 100644
--- a/lib/gitlab/performance_bar/peek_query_tracker.rb
+++ b/lib/gitlab/performance_bar/peek_query_tracker.rb
@@ -3,8 +3,8 @@ module Gitlab
   module PerformanceBar
     module PeekQueryTracker
       def sorted_queries
-        PEEK_DB_CLIENT.query_details.
-          sort { |a, b| b[:duration] <=> a[:duration] }
+        PEEK_DB_CLIENT.query_details
+          .sort { |a, b| b[:duration] <=> a[:duration] }
       end
 
       def results
diff --git a/lib/gitlab/project_authorizations/with_nested_groups.rb b/lib/gitlab/project_authorizations/with_nested_groups.rb
index bb0df1e3dad25af485134213816026fe1e77739c..15b8beacf605574b581e434301ffa35c64fa2f02 100644
--- a/lib/gitlab/project_authorizations/with_nested_groups.rb
+++ b/lib/gitlab/project_authorizations/with_nested_groups.rb
@@ -28,34 +28,34 @@ module Gitlab
 
           # Projects that belong directly to any of the groups the user has
           # access to.
-          Namespace.
-            unscoped.
-            select([alias_as_column(projects[:id], 'project_id'),
-                    cte_alias[:access_level]]).
-            from(cte_alias).
-            joins(:projects),
+          Namespace
+            .unscoped
+            .select([alias_as_column(projects[:id], 'project_id'),
+                     cte_alias[:access_level]])
+            .from(cte_alias)
+            .joins(:projects),
 
           # Projects shared with any of the namespaces the user has access to.
-          Namespace.
-            unscoped.
-            select([links[:project_id],
-                    least(cte_alias[:access_level],
-                          links[:group_access],
-                          'access_level')]).
-            from(cte_alias).
-            joins('INNER JOIN project_group_links ON project_group_links.group_id = namespaces.id').
-            joins('INNER JOIN projects ON projects.id = project_group_links.project_id').
-            joins('INNER JOIN namespaces p_ns ON p_ns.id = projects.namespace_id').
-            where('p_ns.share_with_group_lock IS FALSE')
+          Namespace
+            .unscoped
+            .select([links[:project_id],
+                     least(cte_alias[:access_level],
+                           links[:group_access],
+                           'access_level')])
+            .from(cte_alias)
+            .joins('INNER JOIN project_group_links ON project_group_links.group_id = namespaces.id')
+            .joins('INNER JOIN projects ON projects.id = project_group_links.project_id')
+            .joins('INNER JOIN namespaces p_ns ON p_ns.id = projects.namespace_id')
+            .where('p_ns.share_with_group_lock IS FALSE')
         ]
 
         union = Gitlab::SQL::Union.new(relations)
 
-        ProjectAuthorization.
-          unscoped.
-          with.
-          recursive(cte.to_arel).
-          select_from_union(union)
+        ProjectAuthorization
+          .unscoped
+          .with
+          .recursive(cte.to_arel)
+          .select_from_union(union)
       end
 
       private
@@ -68,17 +68,17 @@ module Gitlab
         namespaces = Namespace.arel_table
 
         # Namespaces the user is a member of.
-        cte << user.groups.
-          select([namespaces[:id], members[:access_level]]).
-          except(:order)
+        cte << user.groups
+          .select([namespaces[:id], members[:access_level]])
+          .except(:order)
 
         # Sub groups of any groups the user is a member of.
         cte << Group.select([namespaces[:id],
                              greatest(members[:access_level],
-                                      cte.table[:access_level], 'access_level')]).
-          joins(join_cte(cte)).
-          joins(join_members).
-          except(:order)
+                                      cte.table[:access_level], 'access_level')])
+          .joins(join_cte(cte))
+          .joins(join_members)
+          .except(:order)
 
         cte
       end
@@ -88,11 +88,11 @@ module Gitlab
         members = Member.arel_table
         namespaces = Namespace.arel_table
 
-        cond = members[:source_id].
-          eq(namespaces[:id]).
-          and(members[:source_type].eq('Namespace')).
-          and(members[:requested_at].eq(nil)).
-          and(members[:user_id].eq(user.id))
+        cond = members[:source_id]
+          .eq(namespaces[:id])
+          .and(members[:source_type].eq('Namespace'))
+          .and(members[:requested_at].eq(nil))
+          .and(members[:user_id].eq(user.id))
 
         Arel::Nodes::OuterJoin.new(members, Arel::Nodes::On.new(cond))
       end
diff --git a/lib/gitlab/project_authorizations/without_nested_groups.rb b/lib/gitlab/project_authorizations/without_nested_groups.rb
index 627e8c5fba26f8129418ced650a803972bf7e606..ad87540e6c2a3d53c0dc82ce12ffc6a94774e819 100644
--- a/lib/gitlab/project_authorizations/without_nested_groups.rb
+++ b/lib/gitlab/project_authorizations/without_nested_groups.rb
@@ -26,9 +26,9 @@ module Gitlab
 
         union = Gitlab::SQL::Union.new(relations)
 
-        ProjectAuthorization.
-          unscoped.
-          select_from_union(union)
+        ProjectAuthorization
+          .unscoped
+          .select_from_union(union)
       end
     end
   end
diff --git a/lib/gitlab/slash_commands/command_definition.rb b/lib/gitlab/quick_actions/command_definition.rb
similarity index 98%
rename from lib/gitlab/slash_commands/command_definition.rb
rename to lib/gitlab/quick_actions/command_definition.rb
index caab88560145fd8b71c1a978bf921cca4236773b..3937d9c153a2a536f78fcbb9a53f378c9d2af0ee 100644
--- a/lib/gitlab/slash_commands/command_definition.rb
+++ b/lib/gitlab/quick_actions/command_definition.rb
@@ -1,5 +1,5 @@
 module Gitlab
-  module SlashCommands
+  module QuickActions
     class CommandDefinition
       attr_accessor :name, :aliases, :description, :explanation, :params,
         :condition_block, :parse_params_block, :action_block
diff --git a/lib/gitlab/slash_commands/dsl.rb b/lib/gitlab/quick_actions/dsl.rb
similarity index 96%
rename from lib/gitlab/slash_commands/dsl.rb
rename to lib/gitlab/quick_actions/dsl.rb
index 1b5b4566d8123c864b448e4dd38e5fd4f9301620..a4a97236ffc147897cf907d20a34b75c0911f207 100644
--- a/lib/gitlab/slash_commands/dsl.rb
+++ b/lib/gitlab/quick_actions/dsl.rb
@@ -1,5 +1,5 @@
 module Gitlab
-  module SlashCommands
+  module QuickActions
     module Dsl
       extend ActiveSupport::Concern
 
@@ -14,7 +14,7 @@ module Gitlab
       end
 
       class_methods do
-        # Allows to give a description to the next slash command.
+        # Allows to give a description to the next quick action.
         # This description is shown in the autocomplete menu.
         # It accepts a block that will be evaluated with the context given to
         # `CommandDefintion#to_h`.
@@ -31,7 +31,7 @@ module Gitlab
           @description = block_given? ? block : text
         end
 
-        # Allows to define params for the next slash command.
+        # Allows to define params for the next quick action.
         # These params are shown in the autocomplete menu.
         #
         # Example:
diff --git a/lib/gitlab/slash_commands/extractor.rb b/lib/gitlab/quick_actions/extractor.rb
similarity index 94%
rename from lib/gitlab/slash_commands/extractor.rb
rename to lib/gitlab/quick_actions/extractor.rb
index 6dbb467d70de6f819c69489797bd9774909bebc5..09576be715688f17fd87b28cec4f3ef907ef567a 100644
--- a/lib/gitlab/slash_commands/extractor.rb
+++ b/lib/gitlab/quick_actions/extractor.rb
@@ -1,10 +1,10 @@
 module Gitlab
-  module SlashCommands
+  module QuickActions
     # This class takes an array of commands that should be extracted from a
     # given text.
     #
     # ```
-    # extractor = Gitlab::SlashCommands::Extractor.new([:open, :assign, :labels])
+    # extractor = Gitlab::QuickActions::Extractor.new([:open, :assign, :labels])
     # ```
     class Extractor
       attr_reader :command_definitions
@@ -24,7 +24,7 @@ module Gitlab
       #
       # Usage:
       # ```
-      # extractor = Gitlab::SlashCommands::Extractor.new([:open, :assign, :labels])
+      # extractor = Gitlab::QuickActions::Extractor.new([:open, :assign, :labels])
       # msg = %(hello\n/labels ~foo ~"bar baz"\nworld)
       # commands = extractor.extract_commands(msg) #=> [['labels', '~foo ~"bar baz"']]
       # msg #=> "hello\nworld"
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index e4d2a9924707c748c8c373fe8dbb0df6558bc336..b706434217d7bbeec74e959e568d461801e8d6e1 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -43,7 +43,7 @@ module Gitlab
     end
 
     def environment_name_regex_message
-      "can contain only letters, digits, '-', '_', '/', '$', '{', '}', '.' and spaces"
+      "can contain only letters, digits, '-', '_', '/', '$', '{', '}', '.', and spaces"
     end
 
     def kubernetes_namespace_regex
diff --git a/lib/gitlab/repo_path.rb b/lib/gitlab/repo_path.rb
index 878e03f61d70d41653a7772aa729cc62f7d76043..3591fa9145ef7cdb20ba774b042a5552685438c3 100644
--- a/lib/gitlab/repo_path.rb
+++ b/lib/gitlab/repo_path.rb
@@ -3,16 +3,18 @@ module Gitlab
     NotFoundError = Class.new(StandardError)
 
     def self.parse(repo_path)
+      wiki = false
       project_path = strip_storage_path(repo_path.sub(/\.git\z/, ''), fail_on_not_found: false)
-      project = Project.find_by_full_path(project_path)
-      if project_path.end_with?('.wiki') && !project
-        project = Project.find_by_full_path(project_path.chomp('.wiki'))
+      project, was_redirected = find_project(project_path)
+
+      if project_path.end_with?('.wiki') && project.nil?
+        project, was_redirected = find_project(project_path.chomp('.wiki'))
         wiki = true
-      else
-        wiki = false
       end
 
-      [project, wiki]
+      redirected_path = project_path if was_redirected
+
+      [project, wiki, redirected_path]
     end
 
     def self.strip_storage_path(repo_path, fail_on_not_found: true)
@@ -30,5 +32,12 @@ module Gitlab
 
       result.sub(/\A\/*/, '')
     end
+
+    def self.find_project(project_path)
+      project = Project.find_by_full_path(project_path, follow_redirects: true)
+      was_redirected = project && project.full_path.casecmp(project_path) != 0
+
+      [project, was_redirected]
+    end
   end
 end
diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb
index b1d6ea665b70c854911499c1b05854eab69366e6..22554236c38339b520659f0e45d9c36e83d0575b 100644
--- a/lib/gitlab/shell.rb
+++ b/lib/gitlab/shell.rb
@@ -30,8 +30,8 @@ module Gitlab
       end
 
       def version_required
-        @version_required ||= File.read(Rails.root.
-                                        join('GITLAB_SHELL_VERSION')).strip
+        @version_required ||= File.read(Rails.root
+                                        .join('GITLAB_SHELL_VERSION')).strip
       end
 
       def strip_key(key)
diff --git a/lib/gitlab/sherlock/line_profiler.rb b/lib/gitlab/sherlock/line_profiler.rb
index aa1468bff6bc66de1af5f493bd2fd887630c6578..b5f9d04004725e3e75c5f309a9d0d1ed3b0e4e43 100644
--- a/lib/gitlab/sherlock/line_profiler.rb
+++ b/lib/gitlab/sherlock/line_profiler.rb
@@ -77,8 +77,8 @@ module Gitlab
             line_samples << LineSample.new(duration, events)
           end
 
-          samples << FileSample.
-            new(file, line_samples, total_duration, total_events)
+          samples << FileSample
+            .new(file, line_samples, total_duration, total_events)
         end
 
         samples
diff --git a/lib/gitlab/sherlock/query.rb b/lib/gitlab/sherlock/query.rb
index 99e56e923eb08a191d3583e2baf5ab815e6d2d58..948bf5e6528730b2dca8f649c1ce972b1cc2a7e6 100644
--- a/lib/gitlab/sherlock/query.rb
+++ b/lib/gitlab/sherlock/query.rb
@@ -105,10 +105,10 @@ module Gitlab
       end
 
       def format_sql(query)
-        query.each_line.
-          map { |line| line.strip }.
-          join("\n").
-          gsub(PREFIX_NEWLINE) { "\n#{$1} " }
+        query.each_line
+          .map { |line| line.strip }
+          .join("\n")
+          .gsub(PREFIX_NEWLINE) { "\n#{$1} " }
       end
     end
   end
diff --git a/lib/gitlab/chat_commands/base_command.rb b/lib/gitlab/slash_commands/base_command.rb
similarity index 97%
rename from lib/gitlab/chat_commands/base_command.rb
rename to lib/gitlab/slash_commands/base_command.rb
index 25da8474e95a0f7c43c507b3f18be238ef849c63..cc3c9a5055527764b4a2ca8c650341ace4f9adb5 100644
--- a/lib/gitlab/chat_commands/base_command.rb
+++ b/lib/gitlab/slash_commands/base_command.rb
@@ -1,5 +1,5 @@
 module Gitlab
-  module ChatCommands
+  module SlashCommands
     class BaseCommand
       QUERY_LIMIT = 5
 
diff --git a/lib/gitlab/chat_commands/command.rb b/lib/gitlab/slash_commands/command.rb
similarity index 65%
rename from lib/gitlab/chat_commands/command.rb
rename to lib/gitlab/slash_commands/command.rb
index 3e0c30c33b7414d48b1163a355c54792f8702ae5..a78408b0519d1104e3c42f1c27d53e1a1f9256b6 100644
--- a/lib/gitlab/chat_commands/command.rb
+++ b/lib/gitlab/slash_commands/command.rb
@@ -1,11 +1,11 @@
 module Gitlab
-  module ChatCommands
+  module SlashCommands
     class Command < BaseCommand
       COMMANDS = [
-        Gitlab::ChatCommands::IssueShow,
-        Gitlab::ChatCommands::IssueNew,
-        Gitlab::ChatCommands::IssueSearch,
-        Gitlab::ChatCommands::Deploy
+        Gitlab::SlashCommands::IssueShow,
+        Gitlab::SlashCommands::IssueNew,
+        Gitlab::SlashCommands::IssueSearch,
+        Gitlab::SlashCommands::Deploy
       ].freeze
 
       def execute
@@ -15,10 +15,10 @@ module Gitlab
           if command.allowed?(project, current_user)
             command.new(project, current_user, params).execute(match)
           else
-            Gitlab::ChatCommands::Presenters::Access.new.access_denied
+            Gitlab::SlashCommands::Presenters::Access.new.access_denied
           end
         else
-          Gitlab::ChatCommands::Help.new(project, current_user, params).execute(available_commands, params[:text])
+          Gitlab::SlashCommands::Help.new(project, current_user, params).execute(available_commands, params[:text])
         end
       end
 
diff --git a/lib/gitlab/chat_commands/deploy.rb b/lib/gitlab/slash_commands/deploy.rb
similarity index 79%
rename from lib/gitlab/chat_commands/deploy.rb
rename to lib/gitlab/slash_commands/deploy.rb
index 458d90f84e8f066535b4b2790f92d82e7cd73b2c..e71eb15d60406d496d086638ca2d38f77c3ac17b 100644
--- a/lib/gitlab/chat_commands/deploy.rb
+++ b/lib/gitlab/slash_commands/deploy.rb
@@ -1,5 +1,5 @@
 module Gitlab
-  module ChatCommands
+  module SlashCommands
     class Deploy < BaseCommand
       def self.match(text)
         /\Adeploy\s+(?<from>\S+.*)\s+to+\s+(?<to>\S+.*)\z/.match(text)
@@ -24,12 +24,12 @@ module Gitlab
         actions = find_actions(from, to)
 
         if actions.none?
-          Gitlab::ChatCommands::Presenters::Deploy.new(nil).no_actions
+          Gitlab::SlashCommands::Presenters::Deploy.new(nil).no_actions
         elsif actions.one?
           action = play!(from, to, actions.first)
-          Gitlab::ChatCommands::Presenters::Deploy.new(action).present(from, to)
+          Gitlab::SlashCommands::Presenters::Deploy.new(action).present(from, to)
         else
-          Gitlab::ChatCommands::Presenters::Deploy.new(actions).too_many_actions
+          Gitlab::SlashCommands::Presenters::Deploy.new(actions).too_many_actions
         end
       end
 
diff --git a/lib/gitlab/chat_commands/help.rb b/lib/gitlab/slash_commands/help.rb
similarity index 82%
rename from lib/gitlab/chat_commands/help.rb
rename to lib/gitlab/slash_commands/help.rb
index 6c0e4d304a4f79f6f9943b8d61a3dd953895512d..81f3707e03e4a735ed1dcc664c68f9f05bf84f57 100644
--- a/lib/gitlab/chat_commands/help.rb
+++ b/lib/gitlab/slash_commands/help.rb
@@ -1,5 +1,5 @@
 module Gitlab
-  module ChatCommands
+  module SlashCommands
     class Help < BaseCommand
       # This class has to be used last, as it always matches. It has to match
       # because other commands were not triggered and we want to show the help
@@ -17,7 +17,7 @@ module Gitlab
       end
 
       def execute(commands, text)
-        Gitlab::ChatCommands::Presenters::Help.new(commands).present(trigger, text)
+        Gitlab::SlashCommands::Presenters::Help.new(commands).present(trigger, text)
       end
 
       def trigger
diff --git a/lib/gitlab/chat_commands/issue_command.rb b/lib/gitlab/slash_commands/issue_command.rb
similarity index 92%
rename from lib/gitlab/chat_commands/issue_command.rb
rename to lib/gitlab/slash_commands/issue_command.rb
index 84de3e44c70c8c3ea28f26ddc7f7748783db6304..87ea19b880619eabbc57cd7521f071280d5b52ef 100644
--- a/lib/gitlab/chat_commands/issue_command.rb
+++ b/lib/gitlab/slash_commands/issue_command.rb
@@ -1,5 +1,5 @@
 module Gitlab
-  module ChatCommands
+  module SlashCommands
     class IssueCommand < BaseCommand
       def self.available?(project)
         project.issues_enabled? && project.default_issues_tracker?
diff --git a/lib/gitlab/chat_commands/issue_new.rb b/lib/gitlab/slash_commands/issue_new.rb
similarity index 92%
rename from lib/gitlab/chat_commands/issue_new.rb
rename to lib/gitlab/slash_commands/issue_new.rb
index 016054ecd465e73aea59d3954663c20a67a11678..25f965e843d76bf229ca06e50aba70cbfdb9e37b 100644
--- a/lib/gitlab/chat_commands/issue_new.rb
+++ b/lib/gitlab/slash_commands/issue_new.rb
@@ -1,5 +1,5 @@
 module Gitlab
-  module ChatCommands
+  module SlashCommands
     class IssueNew < IssueCommand
       def self.match(text)
         # we can not match \n with the dot by passing the m modifier as than
@@ -35,7 +35,7 @@ module Gitlab
       end
 
       def presenter(issue)
-        Gitlab::ChatCommands::Presenters::IssueNew.new(issue)
+        Gitlab::SlashCommands::Presenters::IssueNew.new(issue)
       end
     end
   end
diff --git a/lib/gitlab/chat_commands/issue_search.rb b/lib/gitlab/slash_commands/issue_search.rb
similarity index 95%
rename from lib/gitlab/chat_commands/issue_search.rb
rename to lib/gitlab/slash_commands/issue_search.rb
index 3491b53093ecf6db66fd9f19483520b213bfd51c..acba84b54b4ce5476debbe1fc2fd2b2922063fb1 100644
--- a/lib/gitlab/chat_commands/issue_search.rb
+++ b/lib/gitlab/slash_commands/issue_search.rb
@@ -1,5 +1,5 @@
 module Gitlab
-  module ChatCommands
+  module SlashCommands
     class IssueSearch < IssueCommand
       def self.match(text)
         /\Aissue\s+search\s+(?<query>.*)/.match(text)
diff --git a/lib/gitlab/chat_commands/issue_show.rb b/lib/gitlab/slash_commands/issue_show.rb
similarity index 69%
rename from lib/gitlab/chat_commands/issue_show.rb
rename to lib/gitlab/slash_commands/issue_show.rb
index d6013f4d10cb0fdef8deb5b29bb0b46b8ddecffa..ffa5184e5cb6872ff4078caa01e32df23efb848e 100644
--- a/lib/gitlab/chat_commands/issue_show.rb
+++ b/lib/gitlab/slash_commands/issue_show.rb
@@ -1,5 +1,5 @@
 module Gitlab
-  module ChatCommands
+  module SlashCommands
     class IssueShow < IssueCommand
       def self.match(text)
         /\Aissue\s+show\s+#{Issue.reference_prefix}?(?<iid>\d+)/.match(text)
@@ -13,9 +13,9 @@ module Gitlab
         issue = find_by_iid(match[:iid])
 
         if issue
-          Gitlab::ChatCommands::Presenters::IssueShow.new(issue).present
+          Gitlab::SlashCommands::Presenters::IssueShow.new(issue).present
         else
-          Gitlab::ChatCommands::Presenters::Access.new.not_found
+          Gitlab::SlashCommands::Presenters::Access.new.not_found
         end
       end
     end
diff --git a/lib/gitlab/chat_commands/presenters/access.rb b/lib/gitlab/slash_commands/presenters/access.rb
similarity index 98%
rename from lib/gitlab/chat_commands/presenters/access.rb
rename to lib/gitlab/slash_commands/presenters/access.rb
index 92f4fa17f78d32a66b04a9bd116382304a11c1c3..1a817eb735b913f3cf1dc6e515aaeca006c7fad5 100644
--- a/lib/gitlab/chat_commands/presenters/access.rb
+++ b/lib/gitlab/slash_commands/presenters/access.rb
@@ -1,5 +1,5 @@
 module Gitlab
-  module ChatCommands
+  module SlashCommands
     module Presenters
       class Access < Presenters::Base
         def access_denied
diff --git a/lib/gitlab/chat_commands/presenters/base.rb b/lib/gitlab/slash_commands/presenters/base.rb
similarity index 98%
rename from lib/gitlab/chat_commands/presenters/base.rb
rename to lib/gitlab/slash_commands/presenters/base.rb
index 05994bee79decf609dd2f3f258cfe3abb0e1d73d..27696436574e2230252d3474a86ab8559e10fada 100644
--- a/lib/gitlab/chat_commands/presenters/base.rb
+++ b/lib/gitlab/slash_commands/presenters/base.rb
@@ -1,5 +1,5 @@
 module Gitlab
-  module ChatCommands
+  module SlashCommands
     module Presenters
       class Base
         include Gitlab::Routing.url_helpers
diff --git a/lib/gitlab/chat_commands/presenters/deploy.rb b/lib/gitlab/slash_commands/presenters/deploy.rb
similarity index 95%
rename from lib/gitlab/chat_commands/presenters/deploy.rb
rename to lib/gitlab/slash_commands/presenters/deploy.rb
index 863d0bf99ca2fc9f3216d3c2a668b9efeda41031..b8dc77bd37bc0403836946531f774c150629d7b7 100644
--- a/lib/gitlab/chat_commands/presenters/deploy.rb
+++ b/lib/gitlab/slash_commands/presenters/deploy.rb
@@ -1,5 +1,5 @@
 module Gitlab
-  module ChatCommands
+  module SlashCommands
     module Presenters
       class Deploy < Presenters::Base
         def present(from, to)
diff --git a/lib/gitlab/chat_commands/presenters/help.rb b/lib/gitlab/slash_commands/presenters/help.rb
similarity index 96%
rename from lib/gitlab/chat_commands/presenters/help.rb
rename to lib/gitlab/slash_commands/presenters/help.rb
index cd47b7f4c6ab2fa4559f2868e3abd7a7d16c0039..ea611a4d629b81ac24c5fecbb94d8f7203fc7508 100644
--- a/lib/gitlab/chat_commands/presenters/help.rb
+++ b/lib/gitlab/slash_commands/presenters/help.rb
@@ -1,5 +1,5 @@
 module Gitlab
-  module ChatCommands
+  module SlashCommands
     module Presenters
       class Help < Presenters::Base
         def present(trigger, text)
diff --git a/lib/gitlab/chat_commands/presenters/issue_base.rb b/lib/gitlab/slash_commands/presenters/issue_base.rb
similarity index 97%
rename from lib/gitlab/chat_commands/presenters/issue_base.rb
rename to lib/gitlab/slash_commands/presenters/issue_base.rb
index 25bc82994baa5095a7df469422dd0ea4a32daf93..341f2aabdd0968ab1d15b2f86b6d79839217d0fe 100644
--- a/lib/gitlab/chat_commands/presenters/issue_base.rb
+++ b/lib/gitlab/slash_commands/presenters/issue_base.rb
@@ -1,5 +1,5 @@
 module Gitlab
-  module ChatCommands
+  module SlashCommands
     module Presenters
       module IssueBase
         def color(issuable)
diff --git a/lib/gitlab/chat_commands/presenters/issue_new.rb b/lib/gitlab/slash_commands/presenters/issue_new.rb
similarity index 98%
rename from lib/gitlab/chat_commands/presenters/issue_new.rb
rename to lib/gitlab/slash_commands/presenters/issue_new.rb
index 3674ba25641e4dcda131059bf2ef14eb3679ae19..86490a39cc183e96789822ea97d97cd8d0d8a6dc 100644
--- a/lib/gitlab/chat_commands/presenters/issue_new.rb
+++ b/lib/gitlab/slash_commands/presenters/issue_new.rb
@@ -1,5 +1,5 @@
 module Gitlab
-  module ChatCommands
+  module SlashCommands
     module Presenters
       class IssueNew < Presenters::Base
         include Presenters::IssueBase
diff --git a/lib/gitlab/chat_commands/presenters/issue_search.rb b/lib/gitlab/slash_commands/presenters/issue_search.rb
similarity index 98%
rename from lib/gitlab/chat_commands/presenters/issue_search.rb
rename to lib/gitlab/slash_commands/presenters/issue_search.rb
index 73788cf96626a27d1b21dc6ba48f64e3b59ae37a..4e27d668685e1ae0c79e38bd69bdfcfb8205b20e 100644
--- a/lib/gitlab/chat_commands/presenters/issue_search.rb
+++ b/lib/gitlab/slash_commands/presenters/issue_search.rb
@@ -1,5 +1,5 @@
 module Gitlab
-  module ChatCommands
+  module SlashCommands
     module Presenters
       class IssueSearch < Presenters::Base
         include Presenters::IssueBase
diff --git a/lib/gitlab/chat_commands/presenters/issue_show.rb b/lib/gitlab/slash_commands/presenters/issue_show.rb
similarity index 98%
rename from lib/gitlab/chat_commands/presenters/issue_show.rb
rename to lib/gitlab/slash_commands/presenters/issue_show.rb
index bd784ad241eb36fd4a662c5e99c339207dd0df35..c99316df66701ee51cdfa4db4ea1df61c4dd764d 100644
--- a/lib/gitlab/chat_commands/presenters/issue_show.rb
+++ b/lib/gitlab/slash_commands/presenters/issue_show.rb
@@ -1,5 +1,5 @@
 module Gitlab
-  module ChatCommands
+  module SlashCommands
     module Presenters
       class IssueShow < Presenters::Base
         include Presenters::IssueBase
diff --git a/lib/gitlab/chat_commands/result.rb b/lib/gitlab/slash_commands/result.rb
similarity index 73%
rename from lib/gitlab/chat_commands/result.rb
rename to lib/gitlab/slash_commands/result.rb
index 324d7ef43a3bc30d5165770d326fe7447699b195..7021b4b01b2b20254fdd7902a1a5cae151872a5c 100644
--- a/lib/gitlab/chat_commands/result.rb
+++ b/lib/gitlab/slash_commands/result.rb
@@ -1,5 +1,5 @@
 module Gitlab
-  module ChatCommands
+  module SlashCommands
     Result = Struct.new(:type, :message)
   end
 end
diff --git a/lib/gitlab/sql/recursive_cte.rb b/lib/gitlab/sql/recursive_cte.rb
index 5b1b03820a328ff59cd7a60b4bfe855efcee5178..16ec002f1397128f7efb66d758a857ed9a841d86 100644
--- a/lib/gitlab/sql/recursive_cte.rb
+++ b/lib/gitlab/sql/recursive_cte.rb
@@ -52,10 +52,10 @@ module Gitlab
       # Applies the CTE to the given relation, returning a new one that will
       # query from it.
       def apply_to(relation)
-        relation.except(:where).
-          with.
-          recursive(to_arel).
-          from(alias_to(relation.model.arel_table))
+        relation.except(:where)
+          .with
+          .recursive(to_arel)
+          .from(alias_to(relation.model.arel_table))
       end
     end
   end
diff --git a/lib/gitlab/visibility_level.rb b/lib/gitlab/visibility_level.rb
index 2b53798e70f10be86fbcb7860a7ab7cff7a94f76..36e5b5041a600aec507752b4a1a4a652f567fd19 100644
--- a/lib/gitlab/visibility_level.rb
+++ b/lib/gitlab/visibility_level.rb
@@ -13,18 +13,8 @@ module Gitlab
       scope :public_and_internal_only,  -> { where(visibility_level: [PUBLIC, INTERNAL] ) }
       scope :non_public_only,           -> { where.not(visibility_level: PUBLIC) }
 
-      scope :public_to_user, -> (user) do
-        if user
-          if user.admin?
-            all
-          elsif !user.external?
-            public_and_internal_only
-          else
-            public_only
-          end
-        else
-          public_only
-        end
+      scope :public_to_user, -> (user = nil) do
+        where(visibility_level: VisibilityLevel.levels_for_user(user))
       end
     end
 
@@ -35,6 +25,18 @@ module Gitlab
     class << self
       delegate :values, to: :options
 
+      def levels_for_user(user = nil)
+        return [PUBLIC] unless user
+
+        if user.admin?
+          [PRIVATE, INTERNAL, PUBLIC]
+        elsif user.external?
+          [PUBLIC]
+        else
+          [INTERNAL, PUBLIC]
+        end
+      end
+
       def string_values
         string_options.keys
       end
diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb
index 7f27317775c0d6b130c946355e8b2bf721ae7de5..f96ee69096d4018d4bb7cb14cb81d394048484b9 100644
--- a/lib/gitlab/workhorse.rb
+++ b/lib/gitlab/workhorse.rb
@@ -26,7 +26,10 @@ module Gitlab
         }
 
         if Gitlab.config.gitaly.enabled
-          address = Gitlab::GitalyClient.address(project.repository_storage)
+          server = {
+            address: Gitlab::GitalyClient.address(project.repository_storage),
+            token: Gitlab::GitalyClient.token(project.repository_storage)
+          }
           params[:Repository] = repository.gitaly_repository.to_h
 
           feature_enabled = case action.to_s
@@ -39,8 +42,10 @@ module Gitlab
                             else
                               raise "Unsupported action: #{action}"
                             end
-
-          params[:GitalyAddress] = address if feature_enabled
+          if feature_enabled
+            params[:GitalyAddress] = server[:address] # This field will be deprecated
+            params[:GitalyServer] = server
+          end
         end
 
         params
diff --git a/lib/tasks/gitlab/gitaly.rake b/lib/tasks/gitlab/gitaly.rake
index e88111c37251606b506b0803f07019d9a47e31e7..a8db5701d0b95dc05adc1e67ab173b066dd16138 100644
--- a/lib/tasks/gitlab/gitaly.rake
+++ b/lib/tasks/gitlab/gitaly.rake
@@ -58,8 +58,9 @@ namespace :gitlab do
 
         storages << { name: key, path: val['path'] }
       end
-
-      TOML.dump(socket_path: address.sub(%r{\Aunix:}, ''), storage: storages)
+      config = { socket_path: address.sub(%r{\Aunix:}, ''), storage: storages }
+      config[:auth] = { token: 'secret' } if Rails.env.test?
+      TOML.dump(config)
     end
 
     def create_gitaly_configuration
diff --git a/lib/tasks/migrate/add_limits_mysql.rake b/lib/tasks/migrate/add_limits_mysql.rake
index 761f275d42ac1f8173af9ff25f72c0a1b72c7f51..151f42a222294234170087630dc2431f995b37bc 100644
--- a/lib/tasks/migrate/add_limits_mysql.rake
+++ b/lib/tasks/migrate/add_limits_mysql.rake
@@ -1,9 +1,11 @@
 require Rails.root.join('db/migrate/limits_to_mysql')
 require Rails.root.join('db/migrate/markdown_cache_limits_to_mysql')
+require Rails.root.join('db/migrate/merge_request_diff_file_limits_to_mysql')
 
 desc "GitLab | Add limits to strings in mysql database"
 task add_limits_mysql: :environment do
   puts "Adding limits to schema.rb for mysql"
   LimitsToMysql.new.up
   MarkdownCacheLimitsToMysql.new.up
+  MergeRequestDiffFileLimitsToMysql.new.up
 end
diff --git a/locale/bg/gitlab.po b/locale/bg/gitlab.po
index e6caf83252d75fa4f4f36103c40c303942a14c34..43a5de65c437452c17c4d3f1f80fafc8ccce621d 100644
--- a/locale/bg/gitlab.po
+++ b/locale/bg/gitlab.po
@@ -250,6 +250,9 @@ msgstr "Искате ли да видите данните? Помолете а
 msgid "We don't have enough data to show this stage."
 msgstr "Няма достатъчно данни за този етап."
 
+msgid "You have reached your project limit"
+msgstr ""
+
 msgid "You need permission."
 msgstr "Нуждаете се от разрешение."
 
diff --git a/locale/de/gitlab.po b/locale/de/gitlab.po
index 9a660571db9876504f7f5e54df16e125574733ef..ea864091b1090cb1c73b0efbdc819f05a4838cb8 100644
--- a/locale/de/gitlab.po
+++ b/locale/de/gitlab.po
@@ -291,6 +291,9 @@ msgstr "Um diese Daten einsehen zu können, wenden Sie sich bitte an Ihren Admin
 msgid "We don't have enough data to show this stage."
 msgstr "Es liegen nicht genügend Daten vor, um diese Phase anzuzeigen."
 
+msgid "You have reached your project limit"
+msgstr ""
+
 msgid "You need permission."
 msgstr "Sie benötigen Zugriffsrechte."
 
diff --git a/locale/en/gitlab.po b/locale/en/gitlab.po
index 4e44731fc5a381260c7b84c08f01d3f810382e0f..afb8fb3176fdad86704b72c9a14814e9f974cd4c 100644
--- a/locale/en/gitlab.po
+++ b/locale/en/gitlab.po
@@ -17,23 +17,217 @@ msgstr ""
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
 "\n"
 
+msgid "%{commit_author_link} committed %{commit_timeago}"
+msgstr ""
+
+msgid "About auto deploy"
+msgstr ""
+
+msgid "Active"
+msgstr ""
+
+msgid "Activity"
+msgstr ""
+
+msgid "Add Changelog"
+msgstr ""
+
+msgid "Add Contribution guide"
+msgstr ""
+
+msgid "Add License"
+msgstr ""
+
+msgid "Add an SSH key to your profile to pull or push via SSH."
+msgstr ""
+
+msgid "Add new directory"
+msgstr ""
+
+msgid "Archived project! Repository is read-only"
+msgstr ""
+
 msgid "Are you sure you want to delete this pipeline schedule?"
 msgstr ""
 
+msgid "Attach a file by drag &amp; drop or %{upload_link}"
+msgstr ""
+
+msgid "Branch"
+msgid_plural "Branches"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}"
+msgstr ""
+
+msgid "Branches"
+msgstr ""
+
+msgid "Browse files"
+msgstr ""
+
 msgid "ByAuthor|by"
 msgstr ""
 
+msgid "CI configuration"
+msgstr ""
+
 msgid "Cancel"
 msgstr ""
 
+msgid "ChangeTypeActionLabel|Pick into branch"
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Revert in branch"
+msgstr ""
+
+msgid "ChangeTypeAction|Cherry-pick"
+msgstr ""
+
+msgid "ChangeTypeAction|Revert"
+msgstr ""
+
+msgid "Changelog"
+msgstr ""
+
+msgid "Charts"
+msgstr ""
+
+msgid "Cherry-pick this commit"
+msgstr ""
+
+msgid "Cherry-pick this merge request"
+msgstr ""
+
+msgid "CiStatusLabel|canceled"
+msgstr ""
+
+msgid "CiStatusLabel|created"
+msgstr ""
+
+msgid "CiStatusLabel|failed"
+msgstr ""
+
+msgid "CiStatusLabel|manual action"
+msgstr ""
+
+msgid "CiStatusLabel|passed"
+msgstr ""
+
+msgid "CiStatusLabel|passed with warnings"
+msgstr ""
+
+msgid "CiStatusLabel|pending"
+msgstr ""
+
+msgid "CiStatusLabel|skipped"
+msgstr ""
+
+msgid "CiStatusLabel|waiting for manual action"
+msgstr ""
+
+msgid "CiStatusText|blocked"
+msgstr ""
+
+msgid "CiStatusText|canceled"
+msgstr ""
+
+msgid "CiStatusText|created"
+msgstr ""
+
+msgid "CiStatusText|failed"
+msgstr ""
+
+msgid "CiStatusText|manual"
+msgstr ""
+
+msgid "CiStatusText|passed"
+msgstr ""
+
+msgid "CiStatusText|pending"
+msgstr ""
+
+msgid "CiStatusText|skipped"
+msgstr ""
+
+msgid "CiStatus|running"
+msgstr ""
+
 msgid "Commit"
 msgid_plural "Commits"
 msgstr[0] ""
 msgstr[1] ""
 
+msgid "Commit message"
+msgstr ""
+
+msgid "CommitBoxTitle|Commit"
+msgstr ""
+
+msgid "CommitMessage|Add %{file_name}"
+msgstr ""
+
+msgid "Commits"
+msgstr ""
+
+msgid "Commits|History"
+msgstr ""
+
+msgid "Committed by"
+msgstr ""
+
+msgid "Compare"
+msgstr ""
+
+msgid "Contribution guide"
+msgstr ""
+
+msgid "Contributors"
+msgstr ""
+
+msgid "Copy URL to clipboard"
+msgstr ""
+
+msgid "Copy commit SHA to clipboard"
+msgstr ""
+
+msgid "Create New Directory"
+msgstr ""
+
+msgid "Create directory"
+msgstr ""
+
+msgid "Create empty bare repository"
+msgstr ""
+
+msgid "Create merge request"
+msgstr ""
+
+msgid "Create new..."
+msgstr ""
+
+msgid "CreateNewFork|Fork"
+msgstr ""
+
+msgid "CreateTag|Tag"
+msgstr ""
+
 msgid "Cron Timezone"
 msgstr ""
 
+msgid "Cron syntax"
+msgstr ""
+
+msgid "Custom notification events"
+msgstr ""
+
+msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
+msgstr ""
+
+msgid "Cycle Analytics"
+msgstr ""
+
 msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
 msgstr ""
 
@@ -58,6 +252,9 @@ msgstr ""
 msgid "CycleAnalyticsStage|Test"
 msgstr ""
 
+msgid "Define a custom pattern with cron syntax"
+msgstr ""
+
 msgid "Delete"
 msgstr ""
 
@@ -69,19 +266,67 @@ msgstr[1] ""
 msgid "Description"
 msgstr ""
 
+msgid "Directory name"
+msgstr ""
+
+msgid "Don't show again"
+msgstr ""
+
+msgid "Download"
+msgstr ""
+
+msgid "Download tar"
+msgstr ""
+
+msgid "Download tar.bz2"
+msgstr ""
+
+msgid "Download tar.gz"
+msgstr ""
+
+msgid "Download zip"
+msgstr ""
+
+msgid "DownloadArtifacts|Download"
+msgstr ""
+
+msgid "DownloadCommit|Email Patches"
+msgstr ""
+
+msgid "DownloadCommit|Plain Diff"
+msgstr ""
+
+msgid "DownloadSource|Download"
+msgstr ""
+
 msgid "Edit"
 msgstr ""
 
 msgid "Edit Pipeline Schedule %{id}"
 msgstr ""
 
+msgid "Every day (at 4:00am)"
+msgstr ""
+
+msgid "Every month (on the 1st at 4:00am)"
+msgstr ""
+
+msgid "Every week (Sundays at 4:00am)"
+msgstr ""
+
 msgid "Failed to change the owner"
 msgstr ""
 
 msgid "Failed to remove the pipeline schedule"
 msgstr ""
 
-msgid "Filter"
+msgid "Files"
+msgstr ""
+
+msgid "Find by path"
+msgstr ""
+
+msgid "Find file"
 msgstr ""
 
 msgid "FirstPushedBy|First"
@@ -90,18 +335,47 @@ msgstr ""
 msgid "FirstPushedBy|pushed by"
 msgstr ""
 
+msgid "Fork"
+msgid_plural "Forks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ForkedFromProjectPath|Forked from"
+msgstr ""
+
 msgid "From issue creation until deploy to production"
 msgstr ""
 
 msgid "From merge request merge until deploy to production"
 msgstr ""
 
+msgid "Go to your fork"
+msgstr ""
+
+msgid "GoToYourFork|Fork"
+msgstr ""
+
+msgid "Home"
+msgstr ""
+
+msgid "Housekeeping successfully started"
+msgstr ""
+
+msgid "Import repository"
+msgstr ""
+
 msgid "Interval Pattern"
 msgstr ""
 
 msgid "Introducing Cycle Analytics"
 msgstr ""
 
+msgid "LFSStatus|Disabled"
+msgstr ""
+
+msgid "LFSStatus|Enabled"
+msgstr ""
+
 msgid "Last %d day"
 msgid_plural "Last %d days"
 msgstr[0] ""
@@ -110,6 +384,24 @@ msgstr[1] ""
 msgid "Last Pipeline"
 msgstr ""
 
+msgid "Last Update"
+msgstr ""
+
+msgid "Last commit"
+msgstr ""
+
+msgid "Learn more in the"
+msgstr ""
+
+msgid "Learn more in the|pipeline schedules documentation"
+msgstr ""
+
+msgid "Leave group"
+msgstr ""
+
+msgid "Leave project"
+msgstr ""
+
 msgid "Limited to showing %d event at most"
 msgid_plural "Limited to showing %d events at most"
 msgstr[0] ""
@@ -118,6 +410,9 @@ msgstr[1] ""
 msgid "Median"
 msgstr ""
 
+msgid "MissingSSHKeyWarningLink|add an SSH key"
+msgstr ""
+
 msgid "New Issue"
 msgid_plural "New Issues"
 msgstr[0] ""
@@ -126,6 +421,33 @@ msgstr[1] ""
 msgid "New Pipeline Schedule"
 msgstr ""
 
+msgid "New branch"
+msgstr ""
+
+msgid "New directory"
+msgstr ""
+
+msgid "New file"
+msgstr ""
+
+msgid "New issue"
+msgstr ""
+
+msgid "New merge request"
+msgstr ""
+
+msgid "New schedule"
+msgstr ""
+
+msgid "New snippet"
+msgstr ""
+
+msgid "New tag"
+msgstr ""
+
+msgid "No repository"
+msgstr ""
+
 msgid "No schedules"
 msgstr ""
 
@@ -135,12 +457,75 @@ msgstr ""
 msgid "Not enough data"
 msgstr ""
 
+msgid "Notification events"
+msgstr ""
+
+msgid "NotificationEvent|Close issue"
+msgstr ""
+
+msgid "NotificationEvent|Close merge request"
+msgstr ""
+
+msgid "NotificationEvent|Failed pipeline"
+msgstr ""
+
+msgid "NotificationEvent|Merge merge request"
+msgstr ""
+
+msgid "NotificationEvent|New issue"
+msgstr ""
+
+msgid "NotificationEvent|New merge request"
+msgstr ""
+
+msgid "NotificationEvent|New note"
+msgstr ""
+
+msgid "NotificationEvent|Reassign issue"
+msgstr ""
+
+msgid "NotificationEvent|Reassign merge request"
+msgstr ""
+
+msgid "NotificationEvent|Reopen issue"
+msgstr ""
+
+msgid "NotificationEvent|Successful pipeline"
+msgstr ""
+
+msgid "NotificationLevel|Custom"
+msgstr ""
+
+msgid "NotificationLevel|Disabled"
+msgstr ""
+
+msgid "NotificationLevel|Global"
+msgstr ""
+
+msgid "NotificationLevel|On mention"
+msgstr ""
+
+msgid "NotificationLevel|Participate"
+msgstr ""
+
+msgid "NotificationLevel|Watch"
+msgstr ""
+
+msgid "OfSearchInADropdown|Filter"
+msgstr ""
+
 msgid "OpenedNDaysAgo|Opened"
 msgstr ""
 
+msgid "Options"
+msgstr ""
+
 msgid "Owner"
 msgstr ""
 
+msgid "Pipeline"
+msgstr ""
+
 msgid "Pipeline Health"
 msgstr ""
 
@@ -177,12 +562,78 @@ msgstr ""
 msgid "PipelineSchedules|Target"
 msgstr ""
 
+msgid "PipelineSheduleIntervalPattern|Custom"
+msgstr ""
+
+msgid "Pipeline|with stage"
+msgstr ""
+
+msgid "Pipeline|with stages"
+msgstr ""
+
+msgid "Project '%{project_name}' queued for deletion."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully created."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully updated."
+msgstr ""
+
+msgid "Project '%{project_name}' will be deleted."
+msgstr ""
+
+msgid "Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Project export could not be deleted."
+msgstr ""
+
+msgid "Project export has been deleted."
+msgstr ""
+
+msgid "Project export link has expired. Please generate a new export from your project settings."
+msgstr ""
+
+msgid "Project export started. A download link will be sent by email."
+msgstr ""
+
+msgid "Project home"
+msgstr ""
+
+msgid "ProjectFeature|Disabled"
+msgstr ""
+
+msgid "ProjectFeature|Everyone with access"
+msgstr ""
+
+msgid "ProjectFeature|Only team members"
+msgstr ""
+
+msgid "ProjectFileTree|Name"
+msgstr ""
+
+msgid "ProjectLastActivity|Never"
+msgstr ""
+
 msgid "ProjectLifecycle|Stage"
 msgstr ""
 
+msgid "ProjectNetworkGraph|Graph"
+msgstr ""
+
 msgid "Read more"
 msgstr ""
 
+msgid "Readme"
+msgstr ""
+
+msgid "RefSwitcher|Branches"
+msgstr ""
+
+msgid "RefSwitcher|Tags"
+msgstr ""
+
 msgid "Related Commits"
 msgstr ""
 
@@ -201,23 +652,85 @@ msgstr ""
 msgid "Related Merged Requests"
 msgstr ""
 
+msgid "Remind later"
+msgstr ""
+
+msgid "Remove project"
+msgstr ""
+
+msgid "Request Access"
+msgstr ""
+
+msgid "Revert this commit"
+msgstr ""
+
+msgid "Revert this merge request"
+msgstr ""
+
 msgid "Save pipeline schedule"
 msgstr ""
 
 msgid "Schedule a new pipeline"
 msgstr ""
 
+msgid "Scheduling Pipelines"
+msgstr ""
+
+msgid "Search branches and tags"
+msgstr ""
+
+msgid "Select Archive Format"
+msgstr ""
+
 msgid "Select a timezone"
 msgstr ""
 
 msgid "Select target branch"
 msgstr ""
 
+msgid "Set a password on your account to pull or push via %{protocol}"
+msgstr ""
+
+msgid "Set up CI"
+msgstr ""
+
+msgid "Set up Koding"
+msgstr ""
+
+msgid "Set up auto deploy"
+msgstr ""
+
+msgid "SetPasswordToCloneLink|set a password"
+msgstr ""
+
 msgid "Showing %d event"
 msgid_plural "Showing %d events"
 msgstr[0] ""
 msgstr[1] ""
 
+msgid "Source code"
+msgstr ""
+
+msgid "StarProject|Star"
+msgstr ""
+
+msgid "Start a  %{new_merge_request} with these changes"
+msgstr ""
+
+msgid "Start a <strong>new merge request</strong> with these changes"
+msgstr ""
+
+msgid "Switch branch/tag"
+msgstr ""
+
+msgid "Tag"
+msgid_plural "Tags"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Tags"
+msgstr ""
+
 msgid "Target Branch"
 msgstr ""
 
@@ -227,18 +740,33 @@ msgstr ""
 msgid "The collection of events added to the data gathered for that stage."
 msgstr ""
 
+msgid "The fork relationship has been removed."
+msgstr ""
+
 msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
 msgstr ""
 
 msgid "The phase of the development lifecycle."
 msgstr ""
 
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
 msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
 msgstr ""
 
 msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
 msgstr ""
 
+msgid "The project can be accessed by any logged in user."
+msgstr ""
+
+msgid "The project can be accessed without any authentication."
+msgstr ""
+
+msgid "The repository for this project does not exist."
+msgstr ""
+
 msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
 msgstr ""
 
@@ -254,6 +782,9 @@ msgstr ""
 msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
 msgstr ""
 
+msgid "This means you can not push code until you create an empty repository or import existing one."
+msgstr ""
+
 msgid "Time before an issue gets scheduled"
 msgstr ""
 
@@ -266,6 +797,129 @@ msgstr ""
 msgid "Time until first merge request"
 msgstr ""
 
+msgid "Timeago|%s days ago"
+msgstr ""
+
+msgid "Timeago|%s days remaining"
+msgstr ""
+
+msgid "Timeago|%s hours remaining"
+msgstr ""
+
+msgid "Timeago|%s minutes ago"
+msgstr ""
+
+msgid "Timeago|%s minutes remaining"
+msgstr ""
+
+msgid "Timeago|%s months ago"
+msgstr ""
+
+msgid "Timeago|%s months remaining"
+msgstr ""
+
+msgid "Timeago|%s seconds remaining"
+msgstr ""
+
+msgid "Timeago|%s weeks ago"
+msgstr ""
+
+msgid "Timeago|%s weeks remaining"
+msgstr ""
+
+msgid "Timeago|%s years ago"
+msgstr ""
+
+msgid "Timeago|%s years remaining"
+msgstr ""
+
+msgid "Timeago|1 day remaining"
+msgstr ""
+
+msgid "Timeago|1 hour remaining"
+msgstr ""
+
+msgid "Timeago|1 minute remaining"
+msgstr ""
+
+msgid "Timeago|1 month remaining"
+msgstr ""
+
+msgid "Timeago|1 week remaining"
+msgstr ""
+
+msgid "Timeago|1 year remaining"
+msgstr ""
+
+msgid "Timeago|Past due"
+msgstr ""
+
+msgid "Timeago|a day ago"
+msgstr ""
+
+msgid "Timeago|a month ago"
+msgstr ""
+
+msgid "Timeago|a week ago"
+msgstr ""
+
+msgid "Timeago|a while"
+msgstr ""
+
+msgid "Timeago|a year ago"
+msgstr ""
+
+msgid "Timeago|about %s hours ago"
+msgstr ""
+
+msgid "Timeago|about a minute ago"
+msgstr ""
+
+msgid "Timeago|about an hour ago"
+msgstr ""
+
+msgid "Timeago|in %s days"
+msgstr ""
+
+msgid "Timeago|in %s hours"
+msgstr ""
+
+msgid "Timeago|in %s minutes"
+msgstr ""
+
+msgid "Timeago|in %s months"
+msgstr ""
+
+msgid "Timeago|in %s seconds"
+msgstr ""
+
+msgid "Timeago|in %s weeks"
+msgstr ""
+
+msgid "Timeago|in %s years"
+msgstr ""
+
+msgid "Timeago|in 1 day"
+msgstr ""
+
+msgid "Timeago|in 1 hour"
+msgstr ""
+
+msgid "Timeago|in 1 minute"
+msgstr ""
+
+msgid "Timeago|in 1 month"
+msgstr ""
+
+msgid "Timeago|in 1 week"
+msgstr ""
+
+msgid "Timeago|in 1 year"
+msgstr ""
+
+msgid "Timeago|less than a minute ago"
+msgstr ""
+
 msgid "Time|hr"
 msgid_plural "Time|hrs"
 msgstr[0] ""
@@ -285,16 +939,96 @@ msgstr ""
 msgid "Total test time for all commits/merges"
 msgstr ""
 
+msgid "Unstar"
+msgstr ""
+
+msgid "Upload New File"
+msgstr ""
+
+msgid "Upload file"
+msgstr ""
+
+msgid "Use your global notification setting"
+msgstr ""
+
+msgid "VisibilityLevel|Internal"
+msgstr ""
+
+msgid "VisibilityLevel|Private"
+msgstr ""
+
+msgid "VisibilityLevel|Public"
+msgstr ""
+
 msgid "Want to see the data? Please ask an administrator for access."
 msgstr ""
 
 msgid "We don't have enough data to show this stage."
 msgstr ""
 
+msgid "Withdraw Access Request"
+msgstr ""
+
+msgid ""
+"You are going to remove %{project_name_with_namespace}.\n"
+"Removed project CANNOT be restored!\n"
+"Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You can only add files when you are on a branch"
+msgstr ""
+
+msgid "You have reached your project limit"
+msgstr ""
+
+msgid "You must sign in to star a project"
+msgstr ""
+
 msgid "You need permission."
 msgstr ""
 
+msgid "You will not get any notifications via email"
+msgstr ""
+
+msgid "You will only receive notifications for the events you choose"
+msgstr ""
+
+msgid "You will only receive notifications for threads you have participated in"
+msgstr ""
+
+msgid "You will receive notifications for any activity"
+msgstr ""
+
+msgid "You will receive notifications only for comments in which you were @mentioned"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile"
+msgstr ""
+
+msgid "Your name"
+msgstr ""
+
 msgid "day"
 msgid_plural "days"
 msgstr[0] ""
 msgstr[1] ""
+
+msgid "new merge request"
+msgstr ""
+
+msgid "notification emails"
+msgstr ""
+
+msgid "parent"
+msgid_plural "parents"
+msgstr[0] ""
+msgstr[1] ""
diff --git a/locale/es/gitlab.po b/locale/es/gitlab.po
index 78d28d69885f3b2b7aa81eb73709c684891d88d9..f661cbddf5f2985e9fc92c78603e101022a5ff20 100644
--- a/locale/es/gitlab.po
+++ b/locale/es/gitlab.po
@@ -7,7 +7,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gitlab 1.0.0\n"
 "Report-Msgid-Bugs-To: \n"
-"PO-Revision-Date: 2017-06-07 12:29-0500\n"
+"PO-Revision-Date: 2017-06-19 15:22-0500\n"
 "Language-Team: Spanish\n"
 "Language: es\n"
 "MIME-Version: 1.0\n"
@@ -17,9 +17,15 @@ msgstr ""
 "Last-Translator: Bob Van Landuyt <bob@gitlab.com>\n"
 "X-Generator: Poedit 2.0.2\n"
 
+msgid "%{commit_author_link} committed %{commit_timeago}"
+msgstr "%{commit_author_link} cambió %{commit_timeago}"
+
 msgid "About auto deploy"
 msgstr "Acerca del auto despliegue"
 
+msgid "Active"
+msgstr "Activo"
+
 msgid "Activity"
 msgstr "Actividad"
 
@@ -39,7 +45,13 @@ msgid "Add new directory"
 msgstr "Agregar nuevo directorio"
 
 msgid "Archived project! Repository is read-only"
-msgstr "¡Proyecto archivado! El repositorio es de sólo lectura"
+msgstr "¡Proyecto archivado! El repositorio es de solo lectura"
+
+msgid "Are you sure you want to delete this pipeline schedule?"
+msgstr "¿Estás seguro que deseas eliminar esta programación del pipeline?"
+
+msgid "Attach a file by drag &amp; drop or %{upload_link}"
+msgstr "Adjunte un archivo arrastrando &amp; soltando o %{upload_link}"
 
 msgid "Branch"
 msgid_plural "Branches"
@@ -49,21 +61,51 @@ msgstr[1] "Ramas"
 msgid "Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}"
 msgstr "La rama <strong>%{branch_name}</strong> fue creada. Para configurar el auto despliegue, escoge una plantilla Yaml para GitLab CI y envía tus cambios. %{link_to_autodeploy_doc}"
 
+msgid "BranchSwitcherPlaceholder|Search branches"
+msgstr "Buscar ramas"
+
+msgid "BranchSwitcherTitle|Switch branch"
+msgstr "Cambiar rama"
+
 msgid "Branches"
 msgstr "Ramas"
 
+msgid "Browse files"
+msgstr "Examinar los archivos"
+
 msgid "ByAuthor|by"
 msgstr "por"
 
 msgid "CI configuration"
 msgstr "Configuración de CI"
 
+msgid "Cancel"
+msgstr "Cancelar"
+
+msgid "ChangeTypeActionLabel|Pick into branch"
+msgstr "Escoger en la rama"
+
+msgid "ChangeTypeActionLabel|Revert in branch"
+msgstr "Revertir en la rama"
+
+msgid "ChangeTypeAction|Cherry-pick"
+msgstr "Cherry-pick"
+
+msgid "ChangeTypeAction|Revert"
+msgstr "Revertir"
+
 msgid "Changelog"
 msgstr "Changelog"
 
 msgid "Charts"
 msgstr "Gráficos"
 
+msgid "Cherry-pick this commit"
+msgstr "Escoger este cambio"
+
+msgid "Cherry-pick this merge request"
+msgstr "Escoger esta solicitud de fusión"
+
 msgid "CiStatusLabel|canceled"
 msgstr "cancelado"
 
@@ -71,7 +113,7 @@ msgid "CiStatusLabel|created"
 msgstr "creado"
 
 msgid "CiStatusLabel|failed"
-msgstr "fallado"
+msgstr "fallido"
 
 msgid "CiStatusLabel|manual action"
 msgstr "acción manual"
@@ -123,6 +165,12 @@ msgid_plural "Commits"
 msgstr[0] "Cambio"
 msgstr[1] "Cambios"
 
+msgid "Commit message"
+msgstr "Mensaje del cambio"
+
+msgid "CommitBoxTitle|Commit"
+msgstr "Cambio"
+
 msgid "CommitMessage|Add %{file_name}"
 msgstr "Agregar %{file_name}"
 
@@ -132,6 +180,9 @@ msgstr "Cambios"
 msgid "Commits|History"
 msgstr "Historial"
 
+msgid "Committed by"
+msgstr "Enviado por"
+
 msgid "Compare"
 msgstr "Comparar"
 
@@ -159,9 +210,21 @@ msgstr "Crear repositorio vacío"
 msgid "Create merge request"
 msgstr "Crear solicitud de fusión"
 
+msgid "Create new..."
+msgstr "Crear nuevo..."
+
 msgid "CreateNewFork|Fork"
 msgstr "Bifurcar"
 
+msgid "CreateTag|Tag"
+msgstr "Etiqueta"
+
+msgid "Cron Timezone"
+msgstr "Zona horaria del Cron"
+
+msgid "Cron syntax"
+msgstr "Sintaxis de Cron"
+
 msgid "Custom notification events"
 msgstr "Eventos de notificaciones personalizadas"
 
@@ -195,17 +258,29 @@ msgstr "Puesta en escena"
 msgid "CycleAnalyticsStage|Test"
 msgstr "Pruebas"
 
+msgid "Define a custom pattern with cron syntax"
+msgstr "Definir un patrón personalizado con la sintaxis de cron"
+
+msgid "Delete"
+msgstr "Eliminar"
+
 msgid "Deploy"
 msgid_plural "Deploys"
 msgstr[0] "Despliegue"
 msgstr[1] "Despliegues"
 
+msgid "Description"
+msgstr "Descripción"
+
 msgid "Directory name"
 msgstr "Nombre del directorio"
 
 msgid "Don't show again"
 msgstr "No mostrar de nuevo"
 
+msgid "Download"
+msgstr "Descargar"
+
 msgid "Download tar"
 msgstr "Descargar tar"
 
@@ -221,9 +296,36 @@ msgstr "Descargar zip"
 msgid "DownloadArtifacts|Download"
 msgstr "Descargar"
 
+msgid "DownloadCommit|Email Patches"
+msgstr "Parches por correo electrónico"
+
+msgid "DownloadCommit|Plain Diff"
+msgstr "Diferencias en texto plano"
+
 msgid "DownloadSource|Download"
 msgstr "Descargar"
 
+msgid "Edit"
+msgstr "Editar"
+
+msgid "Edit Pipeline Schedule %{id}"
+msgstr "Editar Programación del Pipeline %{id}"
+
+msgid "Every day (at 4:00am)"
+msgstr "Todos los días (a las 4:00 am)"
+
+msgid "Every month (on the 1st at 4:00am)"
+msgstr "Todos los meses (el día 1 a las 4:00 am)"
+
+msgid "Every week (Sundays at 4:00am)"
+msgstr "Todas las semanas (domingos a las 4:00 am)"
+
+msgid "Failed to change the owner"
+msgstr "Error al cambiar el propietario"
+
+msgid "Failed to remove the pipeline schedule"
+msgstr "Error al eliminar la programación del pipeline"
+
 msgid "Files"
 msgstr "Archivos"
 
@@ -239,12 +341,14 @@ msgstr "Primer"
 msgid "FirstPushedBy|pushed by"
 msgstr "enviado por"
 
+msgid "Fork"
+msgid_plural "Forks"
+msgstr[0] "Bifurcación"
+msgstr[1] "Bifurcaciones"
+
 msgid "ForkedFromProjectPath|Forked from"
 msgstr "Bifurcado de"
 
-msgid "Forks"
-msgstr "Bifurcaciones"
-
 msgid "From issue creation until deploy to production"
 msgstr "Desde la creación de la incidencia hasta el despliegue a producción"
 
@@ -266,6 +370,9 @@ msgstr "Servicio de limpieza iniciado con éxito"
 msgid "Import repository"
 msgstr "Importar repositorio"
 
+msgid "Interval Pattern"
+msgstr "Patrón de intervalo"
+
 msgid "Introducing Cycle Analytics"
 msgstr "Introducción a Cycle Analytics"
 
@@ -280,12 +387,21 @@ msgid_plural "Last %d days"
 msgstr[0] "Último %d día"
 msgstr[1] "Últimos %d días"
 
+msgid "Last Pipeline"
+msgstr "Último Pipeline"
+
 msgid "Last Update"
 msgstr "Última actualización"
 
 msgid "Last commit"
 msgstr "Último cambio"
 
+msgid "Learn more in the"
+msgstr "Más información en la"
+
+msgid "Learn more in the|pipeline schedules documentation"
+msgstr "documentación sobre la programación de pipelines"
+
 msgid "Leave group"
 msgstr "Abandonar grupo"
 
@@ -308,6 +424,9 @@ msgid_plural "New Issues"
 msgstr[0] "Nueva incidencia"
 msgstr[1] "Nuevas incidencias"
 
+msgid "New Pipeline Schedule"
+msgstr "Nueva Programación del Pipeline"
+
 msgid "New branch"
 msgstr "Nueva rama"
 
@@ -323,6 +442,9 @@ msgstr "Nueva incidencia"
 msgid "New merge request"
 msgstr "Nueva solicitud de fusión"
 
+msgid "New schedule"
+msgstr "Nueva programación"
+
 msgid "New snippet"
 msgstr "Nuevo fragmento de código"
 
@@ -332,6 +454,9 @@ msgstr "Nueva etiqueta"
 msgid "No repository"
 msgstr "No hay repositorio"
 
+msgid "No schedules"
+msgstr "No hay programaciones"
+
 msgid "Not available"
 msgstr "No disponible"
 
@@ -392,12 +517,66 @@ msgstr "Participación"
 msgid "NotificationLevel|Watch"
 msgstr "Vigilancia"
 
+msgid "OfSearchInADropdown|Filter"
+msgstr "Filtrar"
+
 msgid "OpenedNDaysAgo|Opened"
 msgstr "Abierto"
 
+msgid "Options"
+msgstr "Opciones"
+
+msgid "Owner"
+msgstr "Propietario"
+
+msgid "Pipeline"
+msgstr "Pipeline"
+
 msgid "Pipeline Health"
 msgstr "Estado del Pipeline"
 
+msgid "Pipeline Schedule"
+msgstr "Programación del Pipeline"
+
+msgid "Pipeline Schedules"
+msgstr "Programaciones de los Pipelines"
+
+msgid "PipelineSchedules|Activated"
+msgstr "Activado"
+
+msgid "PipelineSchedules|Active"
+msgstr "Activos"
+
+msgid "PipelineSchedules|All"
+msgstr "Todos"
+
+msgid "PipelineSchedules|Inactive"
+msgstr "Inactivos"
+
+msgid "PipelineSchedules|Next Run"
+msgstr "Próxima Ejecución"
+
+msgid "PipelineSchedules|None"
+msgstr "Ninguno"
+
+msgid "PipelineSchedules|Provide a short description for this pipeline"
+msgstr "Proporcione una breve descripción para este pipeline"
+
+msgid "PipelineSchedules|Take ownership"
+msgstr "Tomar posesión"
+
+msgid "PipelineSchedules|Target"
+msgstr "Destino"
+
+msgid "PipelineSheduleIntervalPattern|Custom"
+msgstr "Personalizado"
+
+msgid "Pipeline|with stage"
+msgstr "con etapa"
+
+msgid "Pipeline|with stages"
+msgstr "con etapas"
+
 msgid "Project '%{project_name}' queued for deletion."
 msgstr "Proyecto ‘%{project_name}’ en cola para eliminación."
 
@@ -453,7 +632,7 @@ msgid "Read more"
 msgstr "Leer más"
 
 msgid "Readme"
-msgstr "Readme"
+msgstr "Léeme"
 
 msgid "RefSwitcher|Branches"
 msgstr "Ramas"
@@ -488,14 +667,35 @@ msgstr "Eliminar proyecto"
 msgid "Request Access"
 msgstr "Solicitar acceso"
 
+msgid "Revert this commit"
+msgstr "Revertir este cambio"
+
+msgid "Revert this merge request"
+msgstr "Revertir esta solicitud de fusión"
+
+msgid "Save pipeline schedule"
+msgstr "Guardar programación del pipeline"
+
+msgid "Schedule a new pipeline"
+msgstr "Programar un nuevo pipeline"
+
+msgid "Scheduling Pipelines"
+msgstr "Programación de Pipelines"
+
 msgid "Search branches and tags"
 msgstr "Buscar ramas y etiquetas"
 
 msgid "Select Archive Format"
 msgstr "Seleccionar formato de archivo"
 
+msgid "Select a timezone"
+msgstr "Selecciona una zona horaria"
+
+msgid "Select target branch"
+msgstr "Selecciona una rama de destino"
+
 msgid "Set a password on your account to pull or push via %{protocol}"
-msgstr "Establezca una contraseña en su cuenta para actualizar o enviar a través de% {protocol}"
+msgstr "Establezca una contraseña en su cuenta para actualizar o enviar a través de %{protocol}"
 
 msgid "Set up CI"
 msgstr "Configurar CI"
@@ -520,6 +720,9 @@ msgstr "Código fuente"
 msgid "StarProject|Star"
 msgstr "Destacar"
 
+msgid "Start a %{new_merge_request} with these changes"
+msgstr "Iniciar una %{new_merge_request} con estos cambios"
+
 msgid "Switch branch/tag"
 msgstr "Cambiar rama/etiqueta"
 
@@ -531,6 +734,9 @@ msgstr[1] "Etiquetas"
 msgid "Tags"
 msgstr "Etiquetas"
 
+msgid "Target Branch"
+msgstr "Rama de destino"
+
 msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
 msgstr "La etapa de desarrollo muestra el tiempo desde el primer cambio hasta la creación de la solicitud de fusión. Los datos serán automáticamente incorporados aquí una vez creada tu primera solicitud de fusión."
 
@@ -546,6 +752,9 @@ msgstr "La etapa de incidencia muestra el tiempo que toma desde la creación de
 msgid "The phase of the development lifecycle."
 msgstr "La etapa del ciclo de vida de desarrollo."
 
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr "La programación de pipelines ejecuta pipelines en el futuro, repetidamente, para ramas o etiquetas específicas. Los pipelines programados heredarán acceso limitado al proyecto basado en su usuario asociado."
+
 msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
 msgstr "La etapa de planificación muestra el tiempo desde el paso anterior hasta el envío de tu primera confirmación. Este tiempo se añadirá automáticamente una vez que usted envíe el primer cambio."
 
@@ -652,16 +861,16 @@ msgid "Timeago|a day ago"
 msgstr "hace un día"
 
 msgid "Timeago|a month ago"
-msgstr "hace 1 mes"
+msgstr "hace un mes"
 
 msgid "Timeago|a week ago"
-msgstr "hace 1 semana"
+msgstr "hace una semana"
 
 msgid "Timeago|a while"
 msgstr "hace un momento"
 
 msgid "Timeago|a year ago"
-msgstr "hace 1 año"
+msgstr "hace un año"
 
 msgid "Timeago|about %s hours ago"
 msgstr "hace alrededor de %s horas"
@@ -742,6 +951,9 @@ msgstr "Subir nuevo archivo"
 msgid "Upload file"
 msgstr "Subir archivo"
 
+msgid "UploadLink|click to upload"
+msgstr "Hacer clic para subir"
+
 msgid "Use your global notification setting"
 msgstr "Utiliza tu configuración de notificación global"
 
@@ -772,18 +984,21 @@ msgstr ""
 "¡El proyecto eliminado NO puede ser restaurado!\n"
 "¿Estás TOTALMENTE seguro?"
 
-msgid "You are going to remove the fork relationship to source project %{forked_from_project}.  Are you ABSOLUTELY sure?"
+msgid "You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?"
 msgstr "Vas a eliminar el enlace de la bifurcación con el proyecto original %{forked_from_project}. ¿Estás TOTALMENTE seguro?"
 
 msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
 msgstr "Vas a transferir %{project_name_with_namespace} a otro propietario. ¿Estás TOTALMENTE seguro?"
 
 msgid "You can only add files when you are on a branch"
-msgstr "Sólo puede agregar archivos cuando estas en una rama"
+msgstr "Solo puedes agregar archivos cuando estás en una rama"
 
 msgid "You must sign in to star a project"
 msgstr "Debes iniciar sesión para destacar un proyecto"
 
+msgid "You have reached your project limit"
+msgstr ""
+
 msgid "You need permission."
 msgstr "Necesitas permisos."
 
@@ -797,10 +1012,10 @@ msgid "You will only receive notifications for threads you have participated in"
 msgstr "Solo recibirás notificaciones de los temas en los que has participado"
 
 msgid "You will receive notifications for any activity"
-msgstr "Recibirás notificaciones para cualquier actividad"
+msgstr "Recibirás notificaciones por cualquier actividad"
 
 msgid "You will receive notifications only for comments in which you were @mentioned"
-msgstr "Recibirás notificaciones sólo para los comentarios en los que se te mencionó"
+msgstr "Recibirás notificaciones solo para los comentarios en los que se te mencionó"
 
 msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
 msgstr "No podrás actualizar o enviar código al proyecto a través de %{protocol} hasta que %{set_password_link} en tu cuenta"
@@ -811,13 +1026,18 @@ msgstr "No podrás actualizar o enviar código al proyecto a través de SSH hast
 msgid "Your name"
 msgstr "Tu nombre"
 
-msgid "committed"
-msgstr "cambió"
-
 msgid "day"
 msgid_plural "days"
 msgstr[0] "día"
 msgstr[1] "días"
 
+msgid "new merge request"
+msgstr "nueva solicitud de fusión"
+
 msgid "notification emails"
 msgstr "correos electrónicos de notificación"
+
+msgid "parent"
+msgid_plural "parents"
+msgstr[0] "padre"
+msgstr[1] "padres"
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 050f6c446c139992baa45288110f2195b06f8225..a2e32b478d33687f67fc72718ec4bc61ee270121 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -8,8 +8,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: gitlab 1.0.0\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-06-07 21:22+0200\n"
-"PO-Revision-Date: 2017-06-07 21:22+0200\n"
+"POT-Creation-Date: 2017-06-19 15:13-0500\n"
+"PO-Revision-Date: 2017-06-19 15:13-0500\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
 "Language: \n"
@@ -18,23 +18,223 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
 
+msgid "%{commit_author_link} committed %{commit_timeago}"
+msgstr ""
+
+msgid "About auto deploy"
+msgstr ""
+
+msgid "Active"
+msgstr ""
+
+msgid "Activity"
+msgstr ""
+
+msgid "Add Changelog"
+msgstr ""
+
+msgid "Add Contribution guide"
+msgstr ""
+
+msgid "Add License"
+msgstr ""
+
+msgid "Add an SSH key to your profile to pull or push via SSH."
+msgstr ""
+
+msgid "Add new directory"
+msgstr ""
+
+msgid "Archived project! Repository is read-only"
+msgstr ""
+
 msgid "Are you sure you want to delete this pipeline schedule?"
 msgstr ""
 
+msgid "Attach a file by drag &amp; drop or %{upload_link}"
+msgstr ""
+
+msgid "Branch"
+msgid_plural "Branches"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, choose a GitLab CI Yaml template and commit your changes. %{link_to_autodeploy_doc}"
+msgstr ""
+
+msgid "BranchSwitcherPlaceholder|Search branches"
+msgstr ""
+
+msgid "BranchSwitcherTitle|Switch branch"
+msgstr ""
+
+msgid "Branches"
+msgstr ""
+
+msgid "Browse files"
+msgstr ""
+
 msgid "ByAuthor|by"
 msgstr ""
 
+msgid "CI configuration"
+msgstr ""
+
 msgid "Cancel"
 msgstr ""
 
+msgid "ChangeTypeActionLabel|Pick into branch"
+msgstr ""
+
+msgid "ChangeTypeActionLabel|Revert in branch"
+msgstr ""
+
+msgid "ChangeTypeAction|Cherry-pick"
+msgstr ""
+
+msgid "ChangeTypeAction|Revert"
+msgstr ""
+
+msgid "Changelog"
+msgstr ""
+
+msgid "Charts"
+msgstr ""
+
+msgid "Cherry-pick this commit"
+msgstr ""
+
+msgid "Cherry-pick this merge request"
+msgstr ""
+
+msgid "CiStatusLabel|canceled"
+msgstr ""
+
+msgid "CiStatusLabel|created"
+msgstr ""
+
+msgid "CiStatusLabel|failed"
+msgstr ""
+
+msgid "CiStatusLabel|manual action"
+msgstr ""
+
+msgid "CiStatusLabel|passed"
+msgstr ""
+
+msgid "CiStatusLabel|passed with warnings"
+msgstr ""
+
+msgid "CiStatusLabel|pending"
+msgstr ""
+
+msgid "CiStatusLabel|skipped"
+msgstr ""
+
+msgid "CiStatusLabel|waiting for manual action"
+msgstr ""
+
+msgid "CiStatusText|blocked"
+msgstr ""
+
+msgid "CiStatusText|canceled"
+msgstr ""
+
+msgid "CiStatusText|created"
+msgstr ""
+
+msgid "CiStatusText|failed"
+msgstr ""
+
+msgid "CiStatusText|manual"
+msgstr ""
+
+msgid "CiStatusText|passed"
+msgstr ""
+
+msgid "CiStatusText|pending"
+msgstr ""
+
+msgid "CiStatusText|skipped"
+msgstr ""
+
+msgid "CiStatus|running"
+msgstr ""
+
 msgid "Commit"
 msgid_plural "Commits"
 msgstr[0] ""
 msgstr[1] ""
 
+msgid "Commit message"
+msgstr ""
+
+msgid "CommitBoxTitle|Commit"
+msgstr ""
+
+msgid "CommitMessage|Add %{file_name}"
+msgstr ""
+
+msgid "Commits"
+msgstr ""
+
+msgid "Commits|History"
+msgstr ""
+
+msgid "Committed by"
+msgstr ""
+
+msgid "Compare"
+msgstr ""
+
+msgid "Contribution guide"
+msgstr ""
+
+msgid "Contributors"
+msgstr ""
+
+msgid "Copy URL to clipboard"
+msgstr ""
+
+msgid "Copy commit SHA to clipboard"
+msgstr ""
+
+msgid "Create New Directory"
+msgstr ""
+
+msgid "Create directory"
+msgstr ""
+
+msgid "Create empty bare repository"
+msgstr ""
+
+msgid "Create merge request"
+msgstr ""
+
+msgid "Create new..."
+msgstr ""
+
+msgid "CreateNewFork|Fork"
+msgstr ""
+
+msgid "CreateTag|Tag"
+msgstr ""
+
 msgid "Cron Timezone"
 msgstr ""
 
+msgid "Cron syntax"
+msgstr ""
+
+msgid "Custom notification events"
+msgstr ""
+
+msgid "Custom notification levels are the same as participating levels. With custom notification levels you will also receive notifications for select events. To find out more, check out %{notification_link}."
+msgstr ""
+
+msgid "Cycle Analytics"
+msgstr ""
+
 msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
 msgstr ""
 
@@ -59,6 +259,9 @@ msgstr ""
 msgid "CycleAnalyticsStage|Test"
 msgstr ""
 
+msgid "Define a custom pattern with cron syntax"
+msgstr ""
+
 msgid "Delete"
 msgstr ""
 
@@ -70,19 +273,67 @@ msgstr[1] ""
 msgid "Description"
 msgstr ""
 
+msgid "Directory name"
+msgstr ""
+
+msgid "Don't show again"
+msgstr ""
+
+msgid "Download"
+msgstr ""
+
+msgid "Download tar"
+msgstr ""
+
+msgid "Download tar.bz2"
+msgstr ""
+
+msgid "Download tar.gz"
+msgstr ""
+
+msgid "Download zip"
+msgstr ""
+
+msgid "DownloadArtifacts|Download"
+msgstr ""
+
+msgid "DownloadCommit|Email Patches"
+msgstr ""
+
+msgid "DownloadCommit|Plain Diff"
+msgstr ""
+
+msgid "DownloadSource|Download"
+msgstr ""
+
 msgid "Edit"
 msgstr ""
 
 msgid "Edit Pipeline Schedule %{id}"
 msgstr ""
 
+msgid "Every day (at 4:00am)"
+msgstr ""
+
+msgid "Every month (on the 1st at 4:00am)"
+msgstr ""
+
+msgid "Every week (Sundays at 4:00am)"
+msgstr ""
+
 msgid "Failed to change the owner"
 msgstr ""
 
 msgid "Failed to remove the pipeline schedule"
 msgstr ""
 
-msgid "Filter"
+msgid "Files"
+msgstr ""
+
+msgid "Find by path"
+msgstr ""
+
+msgid "Find file"
 msgstr ""
 
 msgid "FirstPushedBy|First"
@@ -91,18 +342,47 @@ msgstr ""
 msgid "FirstPushedBy|pushed by"
 msgstr ""
 
+msgid "Fork"
+msgid_plural "Forks"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ForkedFromProjectPath|Forked from"
+msgstr ""
+
 msgid "From issue creation until deploy to production"
 msgstr ""
 
 msgid "From merge request merge until deploy to production"
 msgstr ""
 
+msgid "Go to your fork"
+msgstr ""
+
+msgid "GoToYourFork|Fork"
+msgstr ""
+
+msgid "Home"
+msgstr ""
+
+msgid "Housekeeping successfully started"
+msgstr ""
+
+msgid "Import repository"
+msgstr ""
+
 msgid "Interval Pattern"
 msgstr ""
 
 msgid "Introducing Cycle Analytics"
 msgstr ""
 
+msgid "LFSStatus|Disabled"
+msgstr ""
+
+msgid "LFSStatus|Enabled"
+msgstr ""
+
 msgid "Last %d day"
 msgid_plural "Last %d days"
 msgstr[0] ""
@@ -111,6 +391,24 @@ msgstr[1] ""
 msgid "Last Pipeline"
 msgstr ""
 
+msgid "Last Update"
+msgstr ""
+
+msgid "Last commit"
+msgstr ""
+
+msgid "Learn more in the"
+msgstr ""
+
+msgid "Learn more in the|pipeline schedules documentation"
+msgstr ""
+
+msgid "Leave group"
+msgstr ""
+
+msgid "Leave project"
+msgstr ""
+
 msgid "Limited to showing %d event at most"
 msgid_plural "Limited to showing %d events at most"
 msgstr[0] ""
@@ -119,6 +417,9 @@ msgstr[1] ""
 msgid "Median"
 msgstr ""
 
+msgid "MissingSSHKeyWarningLink|add an SSH key"
+msgstr ""
+
 msgid "New Issue"
 msgid_plural "New Issues"
 msgstr[0] ""
@@ -127,6 +428,33 @@ msgstr[1] ""
 msgid "New Pipeline Schedule"
 msgstr ""
 
+msgid "New branch"
+msgstr ""
+
+msgid "New directory"
+msgstr ""
+
+msgid "New file"
+msgstr ""
+
+msgid "New issue"
+msgstr ""
+
+msgid "New merge request"
+msgstr ""
+
+msgid "New schedule"
+msgstr ""
+
+msgid "New snippet"
+msgstr ""
+
+msgid "New tag"
+msgstr ""
+
+msgid "No repository"
+msgstr ""
+
 msgid "No schedules"
 msgstr ""
 
@@ -136,12 +464,75 @@ msgstr ""
 msgid "Not enough data"
 msgstr ""
 
+msgid "Notification events"
+msgstr ""
+
+msgid "NotificationEvent|Close issue"
+msgstr ""
+
+msgid "NotificationEvent|Close merge request"
+msgstr ""
+
+msgid "NotificationEvent|Failed pipeline"
+msgstr ""
+
+msgid "NotificationEvent|Merge merge request"
+msgstr ""
+
+msgid "NotificationEvent|New issue"
+msgstr ""
+
+msgid "NotificationEvent|New merge request"
+msgstr ""
+
+msgid "NotificationEvent|New note"
+msgstr ""
+
+msgid "NotificationEvent|Reassign issue"
+msgstr ""
+
+msgid "NotificationEvent|Reassign merge request"
+msgstr ""
+
+msgid "NotificationEvent|Reopen issue"
+msgstr ""
+
+msgid "NotificationEvent|Successful pipeline"
+msgstr ""
+
+msgid "NotificationLevel|Custom"
+msgstr ""
+
+msgid "NotificationLevel|Disabled"
+msgstr ""
+
+msgid "NotificationLevel|Global"
+msgstr ""
+
+msgid "NotificationLevel|On mention"
+msgstr ""
+
+msgid "NotificationLevel|Participate"
+msgstr ""
+
+msgid "NotificationLevel|Watch"
+msgstr ""
+
+msgid "OfSearchInADropdown|Filter"
+msgstr ""
+
 msgid "OpenedNDaysAgo|Opened"
 msgstr ""
 
+msgid "Options"
+msgstr ""
+
 msgid "Owner"
 msgstr ""
 
+msgid "Pipeline"
+msgstr ""
+
 msgid "Pipeline Health"
 msgstr ""
 
@@ -178,12 +569,78 @@ msgstr ""
 msgid "PipelineSchedules|Target"
 msgstr ""
 
+msgid "PipelineSheduleIntervalPattern|Custom"
+msgstr ""
+
+msgid "Pipeline|with stage"
+msgstr ""
+
+msgid "Pipeline|with stages"
+msgstr ""
+
+msgid "Project '%{project_name}' queued for deletion."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully created."
+msgstr ""
+
+msgid "Project '%{project_name}' was successfully updated."
+msgstr ""
+
+msgid "Project '%{project_name}' will be deleted."
+msgstr ""
+
+msgid "Project access must be granted explicitly to each user."
+msgstr ""
+
+msgid "Project export could not be deleted."
+msgstr ""
+
+msgid "Project export has been deleted."
+msgstr ""
+
+msgid "Project export link has expired. Please generate a new export from your project settings."
+msgstr ""
+
+msgid "Project export started. A download link will be sent by email."
+msgstr ""
+
+msgid "Project home"
+msgstr ""
+
+msgid "ProjectFeature|Disabled"
+msgstr ""
+
+msgid "ProjectFeature|Everyone with access"
+msgstr ""
+
+msgid "ProjectFeature|Only team members"
+msgstr ""
+
+msgid "ProjectFileTree|Name"
+msgstr ""
+
+msgid "ProjectLastActivity|Never"
+msgstr ""
+
 msgid "ProjectLifecycle|Stage"
 msgstr ""
 
+msgid "ProjectNetworkGraph|Graph"
+msgstr ""
+
 msgid "Read more"
 msgstr ""
 
+msgid "Readme"
+msgstr ""
+
+msgid "RefSwitcher|Branches"
+msgstr ""
+
+msgid "RefSwitcher|Tags"
+msgstr ""
+
 msgid "Related Commits"
 msgstr ""
 
@@ -202,23 +659,82 @@ msgstr ""
 msgid "Related Merged Requests"
 msgstr ""
 
+msgid "Remind later"
+msgstr ""
+
+msgid "Remove project"
+msgstr ""
+
+msgid "Request Access"
+msgstr ""
+
+msgid "Revert this commit"
+msgstr ""
+
+msgid "Revert this merge request"
+msgstr ""
+
 msgid "Save pipeline schedule"
 msgstr ""
 
 msgid "Schedule a new pipeline"
 msgstr ""
 
+msgid "Scheduling Pipelines"
+msgstr ""
+
+msgid "Search branches and tags"
+msgstr ""
+
+msgid "Select Archive Format"
+msgstr ""
+
 msgid "Select a timezone"
 msgstr ""
 
 msgid "Select target branch"
 msgstr ""
 
+msgid "Set a password on your account to pull or push via %{protocol}"
+msgstr ""
+
+msgid "Set up CI"
+msgstr ""
+
+msgid "Set up Koding"
+msgstr ""
+
+msgid "Set up auto deploy"
+msgstr ""
+
+msgid "SetPasswordToCloneLink|set a password"
+msgstr ""
+
 msgid "Showing %d event"
 msgid_plural "Showing %d events"
 msgstr[0] ""
 msgstr[1] ""
 
+msgid "Source code"
+msgstr ""
+
+msgid "StarProject|Star"
+msgstr ""
+
+msgid "Start a %{new_merge_request} with these changes"
+msgstr ""
+
+msgid "Switch branch/tag"
+msgstr ""
+
+msgid "Tag"
+msgid_plural "Tags"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Tags"
+msgstr ""
+
 msgid "Target Branch"
 msgstr ""
 
@@ -228,18 +744,33 @@ msgstr ""
 msgid "The collection of events added to the data gathered for that stage."
 msgstr ""
 
+msgid "The fork relationship has been removed."
+msgstr ""
+
 msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
 msgstr ""
 
 msgid "The phase of the development lifecycle."
 msgstr ""
 
+msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
+msgstr ""
+
 msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
 msgstr ""
 
 msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
 msgstr ""
 
+msgid "The project can be accessed by any logged in user."
+msgstr ""
+
+msgid "The project can be accessed without any authentication."
+msgstr ""
+
+msgid "The repository for this project does not exist."
+msgstr ""
+
 msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
 msgstr ""
 
@@ -255,6 +786,9 @@ msgstr ""
 msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
 msgstr ""
 
+msgid "This means you can not push code until you create an empty repository or import existing one."
+msgstr ""
+
 msgid "Time before an issue gets scheduled"
 msgstr ""
 
@@ -267,6 +801,129 @@ msgstr ""
 msgid "Time until first merge request"
 msgstr ""
 
+msgid "Timeago|%s days ago"
+msgstr ""
+
+msgid "Timeago|%s days remaining"
+msgstr ""
+
+msgid "Timeago|%s hours remaining"
+msgstr ""
+
+msgid "Timeago|%s minutes ago"
+msgstr ""
+
+msgid "Timeago|%s minutes remaining"
+msgstr ""
+
+msgid "Timeago|%s months ago"
+msgstr ""
+
+msgid "Timeago|%s months remaining"
+msgstr ""
+
+msgid "Timeago|%s seconds remaining"
+msgstr ""
+
+msgid "Timeago|%s weeks ago"
+msgstr ""
+
+msgid "Timeago|%s weeks remaining"
+msgstr ""
+
+msgid "Timeago|%s years ago"
+msgstr ""
+
+msgid "Timeago|%s years remaining"
+msgstr ""
+
+msgid "Timeago|1 day remaining"
+msgstr ""
+
+msgid "Timeago|1 hour remaining"
+msgstr ""
+
+msgid "Timeago|1 minute remaining"
+msgstr ""
+
+msgid "Timeago|1 month remaining"
+msgstr ""
+
+msgid "Timeago|1 week remaining"
+msgstr ""
+
+msgid "Timeago|1 year remaining"
+msgstr ""
+
+msgid "Timeago|Past due"
+msgstr ""
+
+msgid "Timeago|a day ago"
+msgstr ""
+
+msgid "Timeago|a month ago"
+msgstr ""
+
+msgid "Timeago|a week ago"
+msgstr ""
+
+msgid "Timeago|a while"
+msgstr ""
+
+msgid "Timeago|a year ago"
+msgstr ""
+
+msgid "Timeago|about %s hours ago"
+msgstr ""
+
+msgid "Timeago|about a minute ago"
+msgstr ""
+
+msgid "Timeago|about an hour ago"
+msgstr ""
+
+msgid "Timeago|in %s days"
+msgstr ""
+
+msgid "Timeago|in %s hours"
+msgstr ""
+
+msgid "Timeago|in %s minutes"
+msgstr ""
+
+msgid "Timeago|in %s months"
+msgstr ""
+
+msgid "Timeago|in %s seconds"
+msgstr ""
+
+msgid "Timeago|in %s weeks"
+msgstr ""
+
+msgid "Timeago|in %s years"
+msgstr ""
+
+msgid "Timeago|in 1 day"
+msgstr ""
+
+msgid "Timeago|in 1 hour"
+msgstr ""
+
+msgid "Timeago|in 1 minute"
+msgstr ""
+
+msgid "Timeago|in 1 month"
+msgstr ""
+
+msgid "Timeago|in 1 week"
+msgstr ""
+
+msgid "Timeago|in 1 year"
+msgstr ""
+
+msgid "Timeago|less than a minute ago"
+msgstr ""
+
 msgid "Time|hr"
 msgid_plural "Time|hrs"
 msgstr[0] ""
@@ -286,16 +943,99 @@ msgstr ""
 msgid "Total test time for all commits/merges"
 msgstr ""
 
+msgid "Unstar"
+msgstr ""
+
+msgid "Upload New File"
+msgstr ""
+
+msgid "Upload file"
+msgstr ""
+
+msgid "UploadLink|click to upload"
+msgstr ""
+
+msgid "Use your global notification setting"
+msgstr ""
+
+msgid "VisibilityLevel|Internal"
+msgstr ""
+
+msgid "VisibilityLevel|Private"
+msgstr ""
+
+msgid "VisibilityLevel|Public"
+msgstr ""
+
 msgid "Want to see the data? Please ask an administrator for access."
 msgstr ""
 
 msgid "We don't have enough data to show this stage."
 msgstr ""
 
+msgid "Withdraw Access Request"
+msgstr ""
+
+msgid ""
+"You are going to remove %{project_name_with_namespace}.\n"
+"Removed project CANNOT be restored!\n"
+"Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to remove the fork relationship to source project %{forked_from_project}. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
+msgstr ""
+
+msgid "You can only add files when you are on a branch"
+msgstr ""
+
+msgid "You have reached your project limit"
+msgstr ""
+
+msgid "You must sign in to star a project"
+msgstr ""
+
 msgid "You need permission."
 msgstr ""
 
+msgid "You will not get any notifications via email"
+msgstr ""
+
+msgid "You will only receive notifications for the events you choose"
+msgstr ""
+
+msgid "You will only receive notifications for threads you have participated in"
+msgstr ""
+
+msgid "You will receive notifications for any activity"
+msgstr ""
+
+msgid "You will receive notifications only for comments in which you were @mentioned"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
+msgstr ""
+
+msgid "You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile"
+msgstr ""
+
+msgid "Your name"
+msgstr ""
+
 msgid "day"
 msgid_plural "days"
 msgstr[0] ""
 msgstr[1] ""
+
+msgid "new merge request"
+msgstr ""
+
+msgid "notification emails"
+msgstr ""
+
+msgid "parent"
+msgid_plural "parents"
+msgstr[0] ""
+msgstr[1] ""
diff --git a/locale/pt_BR/gitlab.po b/locale/pt_BR/gitlab.po
index 5ad41f92b64f67c5eb88a19648d9d516fec679da..fe6d51c36ac2ca3fb1aafff28d2d0c6871cce345 100644
--- a/locale/pt_BR/gitlab.po
+++ b/locale/pt_BR/gitlab.po
@@ -250,6 +250,9 @@ msgstr "Precisa visualizar os dados? Solicite acesso ao administrador."
 msgid "We don't have enough data to show this stage."
 msgstr "Não temos dados suficientes para mostrar esta fase."
 
+msgid "You have reached your project limit"
+msgstr ""
+
 msgid "You need permission."
 msgstr "Você precisa de permissão."
 
diff --git a/locale/zh_CN/gitlab.po b/locale/zh_CN/gitlab.po
index 114344602075ea218932523f21315377589c8921..8ba95093b8213c8643770bf3aad004cbc66391a9 100644
--- a/locale/zh_CN/gitlab.po
+++ b/locale/zh_CN/gitlab.po
@@ -1,39 +1,241 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the gitlab package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-#
+# Huang Tao <htve@outlook.com>, 2017. #zanata
+# Xiaogang Wen <xiaogang@gitlab.com>, 2017.
 msgid ""
 msgstr ""
 "Project-Id-Version: gitlab 1.0.0\n"
 "Report-Msgid-Bugs-To: \n"
-"PO-Revision-Date: 2017-05-04 19:24-0500\n"
-"Last-Translator: HuangTao <htve@outlook.com>, 2017\n"
-"Language-Team: Chinese (China) (https://www.transifex.com/gitlab-zh/teams/7517"
-"7/zh_CN/)\n"
+"POT-Creation-Date: 2017-06-15 21:59-0500\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Language: zh_CN\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
+"PO-Revision-Date: 2017-06-19 09:57-0400\n"
+"Last-Translator: Huang Tao <htve@outlook.com>\n"
+"Language-Team: Chinese (China) (https://translate.zanata.org/project/view/GitLab)\n"
+"Language: zh-CN\n"
+"X-Generator: Zanata 3.9.6\n"
+"Plural-Forms: nplurals=1; plural=0\n"
+
+msgid "%{commit_author_link} committed %{commit_timeago}"
+msgstr "由 %{commit_author_link} 提交于 %{commit_timeago}"
+
+msgid "About auto deploy"
+msgstr "关于自动部署"
+
+msgid "Active"
+msgstr "启用"
+
+msgid "Activity"
+msgstr "活动"
+
+msgid "Add Changelog"
+msgstr "添加更新日志"
+
+msgid "Add Contribution guide"
+msgstr "添加贡献指南"
+
+msgid "Add License"
+msgstr "添加许可证"
+
+msgid "Add an SSH key to your profile to pull or push via SSH."
+msgstr "新建一个用于推送或拉取的 SSH 秘钥到账号中。"
+
+msgid "Add new directory"
+msgstr "添加目录"
+
+msgid "Archived project! Repository is read-only"
+msgstr "项目已归档!存储库为只读状态"
 
 msgid "Are you sure you want to delete this pipeline schedule?"
+msgstr "确定要删除此流水线计划吗?"
+
+msgid "Attach a file by drag &amp; drop or %{upload_link}"
+msgstr "拖放文件到此处或者 %{upload_link}"
+
+msgid "Branch"
+msgid_plural "Branches"
+msgstr[0] "分支"
+
+msgid ""
+"Branch <strong>%{branch_name}</strong> was created. To set up auto deploy, "
+"choose a GitLab CI Yaml template and commit your changes. "
+"%{link_to_autodeploy_doc}"
 msgstr ""
+"已创建分支 <strong>%{branch_name}</strong> 。如需设置自动部署, 请选择合适的 GitLab CI Yaml "
+"模板并提交更改。%{link_to_autodeploy_doc}"
+
+msgid "Branches"
+msgstr "分支"
+
+msgid "Browse files"
+msgstr "浏览文件"
 
 msgid "ByAuthor|by"
 msgstr "作者:"
 
+msgid "CI configuration"
+msgstr "CI 配置"
+
 msgid "Cancel"
-msgstr ""
+msgstr "取消"
+
+msgid "ChangeTypeActionLabel|Pick into branch"
+msgstr "选择分支"
+
+msgid "ChangeTypeActionLabel|Revert in branch"
+msgstr "还原分支"
+
+msgid "ChangeTypeAction|Cherry-pick"
+msgstr "优选"
+
+msgid "ChangeTypeAction|Revert"
+msgstr "还原"
+
+msgid "Changelog"
+msgstr "更新日志"
+
+msgid "Charts"
+msgstr "统计图"
+
+msgid "Cherry-pick this commit"
+msgstr "优选此提交"
+
+msgid "Cherry-pick this merge request"
+msgstr "优选此合并请求"
+
+msgid "CiStatusLabel|canceled"
+msgstr "已取消"
+
+msgid "CiStatusLabel|created"
+msgstr "已创建"
+
+msgid "CiStatusLabel|failed"
+msgstr "已失败"
+
+msgid "CiStatusLabel|manual action"
+msgstr "手动操作"
+
+msgid "CiStatusLabel|passed"
+msgstr "已通过"
+
+msgid "CiStatusLabel|passed with warnings"
+msgstr "已通过但有警告"
+
+msgid "CiStatusLabel|pending"
+msgstr "等待中"
+
+msgid "CiStatusLabel|skipped"
+msgstr "已跳过"
+
+msgid "CiStatusLabel|waiting for manual action"
+msgstr "等待手动操作"
+
+msgid "CiStatusText|blocked"
+msgstr "已阻塞"
+
+msgid "CiStatusText|canceled"
+msgstr "已取消"
+
+msgid "CiStatusText|created"
+msgstr "已创建"
+
+msgid "CiStatusText|failed"
+msgstr "已失败"
+
+msgid "CiStatusText|manual"
+msgstr "手动操作"
+
+msgid "CiStatusText|passed"
+msgstr "已通过"
+
+msgid "CiStatusText|pending"
+msgstr "等待中"
+
+msgid "CiStatusText|skipped"
+msgstr "已跳过"
+
+msgid "CiStatus|running"
+msgstr "运行中"
 
 msgid "Commit"
 msgid_plural "Commits"
 msgstr[0] "提交"
 
+msgid "Commit message"
+msgstr "提交信息"
+
+msgid "CommitBoxTitle|Commit"
+msgstr "提交"
+
+msgid "CommitMessage|Add %{file_name}"
+msgstr "添加 %{file_name}"
+
+msgid "Commits"
+msgstr "提交"
+
+msgid "Commits|History"
+msgstr "历史"
+
+msgid "Committed by"
+msgstr "提交者:"
+
+msgid "Compare"
+msgstr "比较"
+
+msgid "Contribution guide"
+msgstr "贡献指南"
+
+msgid "Contributors"
+msgstr "贡献者"
+
+msgid "Copy URL to clipboard"
+msgstr "复制 URL 到剪贴板"
+
+msgid "Copy commit SHA to clipboard"
+msgstr "复制提交 SHA 的值到剪贴板"
+
+msgid "Create New Directory"
+msgstr "创建新目录"
+
+msgid "Create directory"
+msgstr "创建目录"
+
+msgid "Create empty bare repository"
+msgstr "创建空的存储库"
+
+msgid "Create merge request"
+msgstr "创建合并请求"
+
+msgid "Create new..."
+msgstr "创建..."
+
+msgid "CreateNewFork|Fork"
+msgstr "派生"
+
+msgid "CreateTag|Tag"
+msgstr "标签"
+
 msgid "Cron Timezone"
+msgstr "Cron 时区"
+
+msgid "Cron syntax"
+msgstr "Cron 语法"
+
+msgid "Custom notification events"
+msgstr "自定义通知事件"
+
+msgid ""
+"Custom notification levels are the same as participating levels. With custom "
+"notification levels you will also receive notifications for select events. "
+"To find out more, check out %{notification_link}."
 msgstr ""
+"自定义通知级别继承自参与级别。使用自定义通知级别,您会收到参与级别及选定事件的通知。想了解更多信息,请查看 %{notification_link}."
+
+msgid "Cycle Analytics"
+msgstr "周期分析"
 
-msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
+msgid ""
+"Cycle Analytics gives an overview of how much time it takes to go from idea "
+"to production in your project."
 msgstr "周期分析概述了项目从想法到产品实现的各阶段所需的时间。"
 
 msgid "CycleAnalyticsStage|Code"
@@ -57,30 +259,81 @@ msgstr "预发布"
 msgid "CycleAnalyticsStage|Test"
 msgstr "测试"
 
+msgid "Define a custom pattern with cron syntax"
+msgstr "使用 Cron 语法定义自定义模式"
+
 msgid "Delete"
-msgstr ""
+msgstr "删除"
 
 msgid "Deploy"
 msgid_plural "Deploys"
 msgstr[0] "部署"
 
 msgid "Description"
-msgstr ""
+msgstr "描述"
+
+msgid "Directory name"
+msgstr "目录名称"
+
+msgid "Don't show again"
+msgstr "不再显示"
+
+msgid "Download"
+msgstr "下载"
+
+msgid "Download tar"
+msgstr "下载 tar"
+
+msgid "Download tar.bz2"
+msgstr "下载 tar.bz2"
+
+msgid "Download tar.gz"
+msgstr "下载 tar.gz"
+
+msgid "Download zip"
+msgstr "下载 zip"
+
+msgid "DownloadArtifacts|Download"
+msgstr "下载"
+
+msgid "DownloadCommit|Email Patches"
+msgstr "电子邮件补丁"
+
+msgid "DownloadCommit|Plain Diff"
+msgstr "差异文件"
+
+msgid "DownloadSource|Download"
+msgstr "下载"
 
 msgid "Edit"
-msgstr ""
+msgstr "编辑"
 
 msgid "Edit Pipeline Schedule %{id}"
-msgstr ""
+msgstr "编辑 %{id} 流水线计划"
+
+msgid "Every day (at 4:00am)"
+msgstr "每日执行(凌晨 4 点)"
+
+msgid "Every month (on the 1st at 4:00am)"
+msgstr "每月执行(每月 1 日凌晨 4 点)"
+
+msgid "Every week (Sundays at 4:00am)"
+msgstr "每周执行(周日凌晨 4 点)"
 
 msgid "Failed to change the owner"
-msgstr ""
+msgstr "无法变更所有者"
 
 msgid "Failed to remove the pipeline schedule"
-msgstr ""
+msgstr "无法删除流水线计划"
 
-msgid "Filter"
-msgstr ""
+msgid "Files"
+msgstr "文件"
+
+msgid "Find by path"
+msgstr "按路径查找"
+
+msgid "Find file"
+msgstr "查找文件"
 
 msgid "FirstPushedBy|First"
 msgstr "首次推送"
@@ -88,24 +341,70 @@ msgstr "首次推送"
 msgid "FirstPushedBy|pushed by"
 msgstr "推送者:"
 
+msgid "Fork"
+msgid_plural "Forks"
+msgstr[0] "派生"
+
+msgid "ForkedFromProjectPath|Forked from"
+msgstr "派生自"
+
 msgid "From issue creation until deploy to production"
 msgstr "从创建议题到部署至生产环境"
 
 msgid "From merge request merge until deploy to production"
 msgstr "从合并请求被合并后到部署至生产环境"
 
+msgid "Go to your fork"
+msgstr "跳转到派生项目"
+
+msgid "GoToYourFork|Fork"
+msgstr "跳转到派生项目"
+
+msgid "Home"
+msgstr "首页"
+
+msgid "Housekeeping successfully started"
+msgstr "已开始维护"
+
+msgid "Import repository"
+msgstr "导入存储库"
+
 msgid "Interval Pattern"
-msgstr ""
+msgstr "循环周期"
 
 msgid "Introducing Cycle Analytics"
 msgstr "周期分析简介"
 
+msgid "LFSStatus|Disabled"
+msgstr "停用"
+
+msgid "LFSStatus|Enabled"
+msgstr "启用"
+
 msgid "Last %d day"
 msgid_plural "Last %d days"
-msgstr[0] "最后 %d 天"
+msgstr[0] "最近 %d 天"
 
 msgid "Last Pipeline"
-msgstr ""
+msgstr "最新流水线"
+
+msgid "Last Update"
+msgstr "最后更新"
+
+msgid "Last commit"
+msgstr "最后提交"
+
+msgid "Learn more in the"
+msgstr "了解更多"
+
+msgid "Learn more in the|pipeline schedules documentation"
+msgstr "流水线计划文档"
+
+msgid "Leave group"
+msgstr "退出群组"
+
+msgid "Leave project"
+msgstr "退出项目"
 
 msgid "Limited to showing %d event at most"
 msgid_plural "Limited to showing %d events at most"
@@ -114,15 +413,45 @@ msgstr[0] "最多显示 %d 个事件"
 msgid "Median"
 msgstr "中位数"
 
+msgid "MissingSSHKeyWarningLink|add an SSH key"
+msgstr "新建 SSH 公钥"
+
 msgid "New Issue"
 msgid_plural "New Issues"
-msgstr[0] "新议题"
+msgstr[0] "新建议题"
 
 msgid "New Pipeline Schedule"
-msgstr ""
+msgstr "创建流水线计划"
+
+msgid "New branch"
+msgstr "新建分支"
+
+msgid "New directory"
+msgstr "新建目录"
+
+msgid "New file"
+msgstr "新建文件"
+
+msgid "New issue"
+msgstr "新建议题"
+
+msgid "New merge request"
+msgstr "新建合并请求"
+
+msgid "New schedule"
+msgstr "新建计划"
+
+msgid "New snippet"
+msgstr "新建代码片段"
+
+msgid "New tag"
+msgstr "新建标签"
+
+msgid "No repository"
+msgstr "没有存储库"
 
 msgid "No schedules"
-msgstr ""
+msgstr "没有计划"
 
 msgid "Not available"
 msgstr "数据不足"
@@ -130,54 +459,185 @@ msgstr "数据不足"
 msgid "Not enough data"
 msgstr "数据不足"
 
+msgid "Notification events"
+msgstr "通知事件"
+
+msgid "NotificationEvent|Close issue"
+msgstr "关闭议题"
+
+msgid "NotificationEvent|Close merge request"
+msgstr "关闭合并请求"
+
+msgid "NotificationEvent|Failed pipeline"
+msgstr "流水线失败"
+
+msgid "NotificationEvent|Merge merge request"
+msgstr "合并请求被合并"
+
+msgid "NotificationEvent|New issue"
+msgstr "新建议题"
+
+msgid "NotificationEvent|New merge request"
+msgstr "新建合并请求"
+
+msgid "NotificationEvent|New note"
+msgstr "新建评论"
+
+msgid "NotificationEvent|Reassign issue"
+msgstr "重新指派议题"
+
+msgid "NotificationEvent|Reassign merge request"
+msgstr "重新指派合并请求"
+
+msgid "NotificationEvent|Reopen issue"
+msgstr "重启议题"
+
+msgid "NotificationEvent|Successful pipeline"
+msgstr "流水线成功完成"
+
+msgid "NotificationLevel|Custom"
+msgstr "自定义"
+
+msgid "NotificationLevel|Disabled"
+msgstr "停用"
+
+msgid "NotificationLevel|Global"
+msgstr "全局"
+
+msgid "NotificationLevel|On mention"
+msgstr "提及"
+
+msgid "NotificationLevel|Participate"
+msgstr "参与"
+
+msgid "NotificationLevel|Watch"
+msgstr "关注"
+
+msgid "OfSearchInADropdown|Filter"
+msgstr "筛选"
+
 msgid "OpenedNDaysAgo|Opened"
 msgstr "开始于"
 
+msgid "Options"
+msgstr "操作"
+
 msgid "Owner"
-msgstr ""
+msgstr "所有者"
+
+msgid "Pipeline"
+msgstr "流水线"
 
 msgid "Pipeline Health"
 msgstr "流水线健康指标"
 
 msgid "Pipeline Schedule"
-msgstr ""
+msgstr "流水线计划"
 
 msgid "Pipeline Schedules"
-msgstr ""
+msgstr "流水线计划"
 
 msgid "PipelineSchedules|Activated"
-msgstr ""
+msgstr "是否启用"
 
 msgid "PipelineSchedules|Active"
-msgstr ""
+msgstr "已启用"
 
 msgid "PipelineSchedules|All"
-msgstr ""
+msgstr "所有"
 
 msgid "PipelineSchedules|Inactive"
-msgstr ""
+msgstr "未启用"
 
 msgid "PipelineSchedules|Next Run"
-msgstr ""
+msgstr "下次运行时间"
 
 msgid "PipelineSchedules|None"
-msgstr ""
+msgstr "æ— "
 
 msgid "PipelineSchedules|Provide a short description for this pipeline"
-msgstr ""
+msgstr "为此流水线提供简短描述"
 
 msgid "PipelineSchedules|Take ownership"
-msgstr ""
+msgstr "取得所有者"
 
 msgid "PipelineSchedules|Target"
-msgstr ""
+msgstr "目标"
+
+msgid "PipelineSheduleIntervalPattern|Custom"
+msgstr "自定义"
+
+msgid "Pipeline|with stage"
+msgstr "于阶段"
+
+msgid "Pipeline|with stages"
+msgstr "于阶段"
+
+msgid "Project '%{project_name}' queued for deletion."
+msgstr "项目 '%{project_name}' 已进入删除队列。"
+
+msgid "Project '%{project_name}' was successfully created."
+msgstr "项目 '%{project_name}' 已创建成功。"
+
+msgid "Project '%{project_name}' was successfully updated."
+msgstr "项目 '%{project_name}' 已更新完成。"
+
+msgid "Project '%{project_name}' will be deleted."
+msgstr "项目 '%{project_name}' 将被删除。"
+
+msgid "Project access must be granted explicitly to each user."
+msgstr "项目访问权限必须明确授权给每个用户。"
+
+msgid "Project export could not be deleted."
+msgstr "无法删除项目导出。"
+
+msgid "Project export has been deleted."
+msgstr "项目导出已被删除。"
+
+msgid ""
+"Project export link has expired. Please generate a new export from your "
+"project settings."
+msgstr "项目导出链接已过期。请从项目设置中重新生成项目导出。"
+
+msgid "Project export started. A download link will be sent by email."
+msgstr "项目导出已开始。下载链接将通过电子邮件发送。"
+
+msgid "Project home"
+msgstr "项目首页"
+
+msgid "ProjectFeature|Disabled"
+msgstr "停用"
+
+msgid "ProjectFeature|Everyone with access"
+msgstr "任何对项目有访问权的人"
+
+msgid "ProjectFeature|Only team members"
+msgstr "只限团队成员"
+
+msgid "ProjectFileTree|Name"
+msgstr "名称"
+
+msgid "ProjectLastActivity|Never"
+msgstr "从未"
 
 msgid "ProjectLifecycle|Stage"
-msgstr "项目生命周期"
+msgstr "阶段"
+
+msgid "ProjectNetworkGraph|Graph"
+msgstr "分支图"
 
 msgid "Read more"
 msgstr "了解更多"
 
+msgid "Readme"
+msgstr "自述文件"
+
+msgid "RefSwitcher|Branches"
+msgstr "分支"
+
+msgid "RefSwitcher|Tags"
+msgstr "标签"
+
 msgid "Related Commits"
 msgstr "相关的提交"
 
@@ -196,58 +656,163 @@ msgstr "相关的合并请求"
 msgid "Related Merged Requests"
 msgstr "相关已合并的合并请求"
 
+msgid "Remind later"
+msgstr "稍后提醒"
+
+msgid "Remove project"
+msgstr "删除项目"
+
+msgid "Request Access"
+msgstr "申请权限"
+
+msgid "Revert this commit"
+msgstr "还原此提交"
+
+msgid "Revert this merge request"
+msgstr "还原此合并请求"
+
 msgid "Save pipeline schedule"
-msgstr ""
+msgstr "保存流水线计划"
 
 msgid "Schedule a new pipeline"
-msgstr ""
+msgstr "新建流水线计划"
+
+msgid "Scheduling Pipelines"
+msgstr "流水线计划"
+
+msgid "Search branches and tags"
+msgstr "搜索分支和标签"
+
+msgid "Select Archive Format"
+msgstr "选择下载格式"
 
 msgid "Select a timezone"
-msgstr ""
+msgstr "选择时区"
 
 msgid "Select target branch"
-msgstr ""
+msgstr "选择目标分支"
+
+msgid "Set a password on your account to pull or push via %{protocol}"
+msgstr "为账号创建一个用于推送或拉取的 %{protocol} 密码。"
+
+msgid "Set up CI"
+msgstr "设置 CI"
+
+msgid "Set up Koding"
+msgstr "设置 Koding"
+
+msgid "Set up auto deploy"
+msgstr "设置自动部署"
+
+msgid "SetPasswordToCloneLink|set a password"
+msgstr "设置密码"
 
 msgid "Showing %d event"
 msgid_plural "Showing %d events"
 msgstr[0] "显示 %d 个事件"
 
+msgid "Source code"
+msgstr "源代码"
+
+msgid "StarProject|Star"
+msgstr "星标"
+
+msgid "Start a %{new_merge_request} with these changes"
+msgstr "由此更改 %{new_merge_request}"
+
+msgid "Switch branch/tag"
+msgstr "切换分支/标签"
+
+msgid "Tag"
+msgid_plural "Tags"
+msgstr[0] "标签"
+
+msgid "Tags"
+msgstr "标签"
+
 msgid "Target Branch"
-msgstr ""
+msgstr "目标分支"
 
-msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
+msgid ""
+"The coding stage shows the time from the first commit to creating the merge "
+"request. The data will automatically be added here once you create your "
+"first merge request."
 msgstr "编码阶段概述了从第一次提交到创建合并请求的时间。创建第一个合并请求后,数据将自动添加到此处。"
 
 msgid "The collection of events added to the data gathered for that stage."
-msgstr "与该阶段相关的事件。"
+msgstr "与该阶段相关的事件集合。"
 
-msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
-msgstr "议题阶段概述了从创建议题到将议题设置里程碑或将议题添加到议题看板的时间。开始创建议题以查看此阶段的数据。"
+msgid "The fork relationship has been removed."
+msgstr "派生关系已被删除。"
+
+msgid ""
+"The issue stage shows the time it takes from creating an issue to assigning "
+"the issue to a milestone, or add the issue to a list on your Issue Board. "
+"Begin creating issues to see data for this stage."
+msgstr "议题阶段概述了从创建议题到将议题添加到里程碑或议题看板所花费的时间。创建第一个议题后,数据将自动添加到此处.。"
 
 msgid "The phase of the development lifecycle."
 msgstr "项目生命周期中的各个阶段。"
 
-msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
-msgstr "计划阶段概述了从议题添加到日程后到推送首次提交的时间。当首次推送提交后,数据将自动添加到此处。"
+msgid ""
+"The pipelines schedule runs pipelines in the future, repeatedly, for "
+"specific branches or tags. Those scheduled pipelines will inherit limited "
+"project access based on their associated user."
+msgstr "流水线计划会周期性重复运行指定分支或标签的流水线。这些流水线将根据其关联用户继承有限的项目访问权限。"
 
-msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
+msgid ""
+"The planning stage shows the time from the previous step to pushing your "
+"first commit. This time will be added automatically once you push your first "
+"commit."
+msgstr "计划阶段概述了从议题添加到日程到推送首次提交的时间。当首次推送提交后,数据将自动添加到此处。"
+
+msgid ""
+"The production stage shows the total time it takes between creating an issue "
+"and deploying the code to production. The data will be automatically added "
+"once you have completed the full idea to production cycle."
 msgstr "生产阶段概述了从创建一个议题到将代码部署到生产环境的总时间。当完成想法到部署生产的循环,数据将自动添加到此处。"
 
-msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
+msgid "The project can be accessed by any logged in user."
+msgstr "该项目允许已登录的用户访问。"
+
+msgid "The project can be accessed without any authentication."
+msgstr "该项目允许任何人访问。"
+
+msgid "The repository for this project does not exist."
+msgstr "此项目的存储库不存在。"
+
+msgid ""
+"The review stage shows the time from creating the merge request to merging "
+"it. The data will automatically be added after you merge your first merge "
+"request."
 msgstr "评审阶段概述了从创建合并请求到被合并的时间。当创建第一个合并请求后,数据将自动添加到此处。"
 
-msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
+msgid ""
+"The staging stage shows the time between merging the MR and deploying code "
+"to the production environment. The data will be automatically added once you "
+"deploy to production for the first time."
 msgstr "预发布阶段概述了从合并请求被合并到部署至生产环境的总时间。首次部署到生产环境后,数据将自动添加到此处。"
 
-msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
-msgstr "测试阶段概述了GitLab CI为相关合并请求运行每个流水线所需的时间。当第一个流水线运行完成后,数据将自动添加到此处。"
+msgid ""
+"The testing stage shows the time GitLab CI takes to run every pipeline for "
+"the related merge request. The data will automatically be added after your "
+"first pipeline finishes running."
+msgstr "测试阶段概述了 GitLab CI 为相关合并请求运行每个流水线所需的时间。当第一个流水线运行完成后,数据将自动添加到此处。"
 
 msgid "The time taken by each data entry gathered by that stage."
 msgstr "该阶段每条数据所花的时间"
 
-msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
+msgid ""
+"The value lying at the midpoint of a series of observed values. E.g., "
+"between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 ="
+" 6."
 msgstr "中位数是一个数列中最中间的值。例如在 3、5、9 之间,中位数是 5。在 3、5、7、8 之间,中位数是 (5 + 7)/ 2 = 6。"
 
+msgid ""
+"This means you can not push code until you create an empty repository or "
+"import existing one."
+msgstr "在创建一个空的存储库或导入现有存储库之前,将无法推送代码。"
+
 msgid "Time before an issue gets scheduled"
 msgstr "议题被列入日程表的时间"
 
@@ -260,6 +825,129 @@ msgstr "从创建合并请求到被合并或关闭的时间"
 msgid "Time until first merge request"
 msgstr "创建第一个合并请求之前的时间"
 
+msgid "Timeago|%s days ago"
+msgstr " %s 天前"
+
+msgid "Timeago|%s days remaining"
+msgstr "剩余 %s 天"
+
+msgid "Timeago|%s hours remaining"
+msgstr "剩余 %s 小时"
+
+msgid "Timeago|%s minutes ago"
+msgstr " %s 分钟前"
+
+msgid "Timeago|%s minutes remaining"
+msgstr "剩余 %s 分钟"
+
+msgid "Timeago|%s months ago"
+msgstr " %s 个月前"
+
+msgid "Timeago|%s months remaining"
+msgstr "剩余 %s 月"
+
+msgid "Timeago|%s seconds remaining"
+msgstr "剩余 %s 秒"
+
+msgid "Timeago|%s weeks ago"
+msgstr " %s 星期前"
+
+msgid "Timeago|%s weeks remaining"
+msgstr "剩余 %s 星期"
+
+msgid "Timeago|%s years ago"
+msgstr " %s 年前"
+
+msgid "Timeago|%s years remaining"
+msgstr "剩余 %s 年"
+
+msgid "Timeago|1 day remaining"
+msgstr "剩余 1 天"
+
+msgid "Timeago|1 hour remaining"
+msgstr "剩余 1 小时"
+
+msgid "Timeago|1 minute remaining"
+msgstr "剩余 1 分钟"
+
+msgid "Timeago|1 month remaining"
+msgstr "剩余 1 个月"
+
+msgid "Timeago|1 week remaining"
+msgstr "剩余 1 星期"
+
+msgid "Timeago|1 year remaining"
+msgstr "剩余 1 年"
+
+msgid "Timeago|Past due"
+msgstr "逾期"
+
+msgid "Timeago|a day ago"
+msgstr " 1 天前"
+
+msgid "Timeago|a month ago"
+msgstr " 1 个月前"
+
+msgid "Timeago|a week ago"
+msgstr " 1 星期前"
+
+msgid "Timeago|a while"
+msgstr "刚刚"
+
+msgid "Timeago|a year ago"
+msgstr " 1 年前"
+
+msgid "Timeago|about %s hours ago"
+msgstr "约 %s 小时前"
+
+msgid "Timeago|about a minute ago"
+msgstr "约 1 分钟前"
+
+msgid "Timeago|about an hour ago"
+msgstr "约 1 小时前"
+
+msgid "Timeago|in %s days"
+msgstr " %s 天后"
+
+msgid "Timeago|in %s hours"
+msgstr " %s 小时后"
+
+msgid "Timeago|in %s minutes"
+msgstr " %s 分钟后"
+
+msgid "Timeago|in %s months"
+msgstr " %s 个月后"
+
+msgid "Timeago|in %s seconds"
+msgstr " %s 秒后"
+
+msgid "Timeago|in %s weeks"
+msgstr " %s 星期后"
+
+msgid "Timeago|in %s years"
+msgstr " %s 年后"
+
+msgid "Timeago|in 1 day"
+msgstr " 1 天后"
+
+msgid "Timeago|in 1 hour"
+msgstr " 1 小时后"
+
+msgid "Timeago|in 1 minute"
+msgstr " 1 分钟后"
+
+msgid "Timeago|in 1 month"
+msgstr " 1 月后"
+
+msgid "Timeago|in 1 week"
+msgstr " 1 星期后"
+
+msgid "Timeago|in 1 year"
+msgstr " 1 年后"
+
+msgid "Timeago|less than a minute ago"
+msgstr "不到 1 分钟前"
+
 msgid "Time|hr"
 msgid_plural "Time|hrs"
 msgstr[0] "小时"
@@ -277,15 +965,108 @@ msgstr "总时间"
 msgid "Total test time for all commits/merges"
 msgstr "所有提交和合并的总测试时间"
 
+msgid "Unstar"
+msgstr "取消星标"
+
+msgid "Upload New File"
+msgstr "上传新文件"
+
+msgid "Upload file"
+msgstr "上传文件"
+
+msgid "Use your global notification setting"
+msgstr "使用全局通知设置"
+
+msgid "VisibilityLevel|Internal"
+msgstr "内部"
+
+msgid "VisibilityLevel|Private"
+msgstr "私有"
+
+msgid "VisibilityLevel|Public"
+msgstr "公开"
+
 msgid "Want to see the data? Please ask an administrator for access."
 msgstr "权限不足。如需查看相关数据,请向管理员申请权限。"
 
 msgid "We don't have enough data to show this stage."
 msgstr "该阶段的数据不足,无法显示。"
 
+msgid "Withdraw Access Request"
+msgstr "取消权限申请"
+
+msgid ""
+"You are going to remove %{project_name_with_namespace}.\n"
+"Removed project CANNOT be restored!\n"
+"Are you ABSOLUTELY sure?"
+msgstr "即将要删除 %{project_name_with_namespace}。\n"
+"已删除的项目无法恢复!\n"
+"确定继续吗?"
+
+msgid ""
+"You are going to remove the fork relationship to source project "
+"%{forked_from_project}. Are you ABSOLUTELY sure?"
+msgstr "即将删除与源项目 %{forked_from_project} 的派生关系。确定继续吗?"
+
+msgid ""
+"You are going to transfer %{project_name_with_namespace} to another owner. "
+"Are you ABSOLUTELY sure?"
+msgstr "即将 %{project_name_with_namespace} 转移给另一个所有者。确定继续吗?"
+
+msgid "You can only add files when you are on a branch"
+msgstr "只能在分支上添加文件"
+
+msgid "You have reached your project limit"
+msgstr "您已达到项目数量限制"
+
+msgid "You must sign in to star a project"
+msgstr "必须登录才能对项目加星标"
+
 msgid "You need permission."
-msgstr "您需要相关的权限。"
+msgstr "需要相关的权限。"
+
+msgid "You will not get any notifications via email"
+msgstr "不会收到任何通知邮件"
+
+msgid "You will only receive notifications for the events you choose"
+msgstr "只接收选择的事件通知"
+
+msgid ""
+"You will only receive notifications for threads you have participated in"
+msgstr "只接收参与的主题的通知"
+
+msgid "You will receive notifications for any activity"
+msgstr "接收所有活动的通知"
+
+msgid ""
+"You will receive notifications only for comments in which you were "
+"@mentioned"
+msgstr "只接收评论中提及(@)您的通知"
+
+msgid ""
+"You won't be able to pull or push project code via %{protocol} until you "
+"%{set_password_link} on your account"
+msgstr "在账号中 %{set_password_link} 之前将无法通过 %{protocol} 拉取或推送代码。"
+
+msgid ""
+"You won't be able to pull or push project code via SSH until you "
+"%{add_ssh_key_link} to your profile"
+msgstr "在账号中 %{add_ssh_key_link} 之前将无法通过 SSH 拉取或推送代码。"
+
+msgid "Your name"
+msgstr "您的名字"
 
 msgid "day"
 msgid_plural "days"
 msgstr[0] "天"
+
+msgid "new merge request"
+msgstr "新建合并请求"
+
+msgid "notification emails"
+msgstr "通知邮件"
+
+msgid "parent"
+msgid_plural "parents"
+msgstr[0] "父级"
+
diff --git a/locale/zh_HK/gitlab.po b/locale/zh_HK/gitlab.po
index 81b2ff863eaa9e71962baa5c3f10e98a4eebab06..f0a9e44daf3397d04acb455fa9911def86710046 100644
--- a/locale/zh_HK/gitlab.po
+++ b/locale/zh_HK/gitlab.po
@@ -283,6 +283,9 @@ msgstr "權限不足。如需查看相關數據,請向管理員申請權限。
 msgid "We don't have enough data to show this stage."
 msgstr "該階段的數據不足,無法顯示。"
 
+msgid "You have reached your project limit"
+msgstr ""
+
 msgid "You need permission."
 msgstr "您需要相關的權限。"
 
diff --git a/locale/zh_TW/gitlab.po b/locale/zh_TW/gitlab.po
index e40723a9d8d462573bc56275e3c8b6d6f17cc032..5130572d7ed51ce9460f4403e6ad847ae74436db 100644
--- a/locale/zh_TW/gitlab.po
+++ b/locale/zh_TW/gitlab.po
@@ -283,6 +283,9 @@ msgstr "權限不足。如需查看相關資料,請向管理員申請權限。
 msgid "We don't have enough data to show this stage."
 msgstr "因該階段的資料不足而無法顯示相關資訊"
 
+msgid "You have reached your project limit"
+msgstr ""
+
 msgid "You need permission."
 msgstr "您需要相關的權限。"
 
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
index 7d6c317482f510eaa71dd5d6fd7efba2b2aab2c4..69928a906c6639844b25be93ed4ab2f50381997f 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -116,8 +116,8 @@ describe Admin::UsersController do
     it 'displays an alert' do
       go
 
-      expect(flash[:notice]).
-        to eq 'Two-factor Authentication has been disabled for this user'
+      expect(flash[:notice])
+        .to eq 'Two-factor Authentication has been disabled for this user'
     end
 
     def go
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb
index b0b24b1de1bcdd67ed1429d342c258c6639457c9..c4092303a672c71b911fa53a22a82c817aa1df70 100644
--- a/spec/controllers/groups_controller_spec.rb
+++ b/spec/controllers/groups_controller_spec.rb
@@ -2,7 +2,7 @@ require 'rails_helper'
 
 describe GroupsController do
   let(:user) { create(:user) }
-  let(:group) { create(:group) }
+  let(:group) { create(:group, :public) }
   let(:project) { create(:empty_project, namespace: group) }
   let!(:group_member) { create(:group_member, group: group, user: user) }
 
@@ -35,14 +35,15 @@ describe GroupsController do
         sign_in(user)
       end
 
-      it 'shows the public subgroups' do
+      it 'shows all subgroups' do
         get :subgroups, id: group.to_param
 
-        expect(assigns(:nested_groups)).to contain_exactly(public_subgroup)
+        expect(assigns(:nested_groups)).to contain_exactly(public_subgroup, private_subgroup)
       end
 
-      context 'being member' do
+      context 'being member of private subgroup' do
         it 'shows public and private subgroups the user is member of' do
+          group_member.destroy!
           private_subgroup.add_guest(user)
 
           get :subgroups, id: group.to_param
diff --git a/spec/controllers/import/bitbucket_controller_spec.rb b/spec/controllers/import/bitbucket_controller_spec.rb
index 0be7bc6a0452cd7fb9e9b8882577ceb38c7bf50c..8ef10dabd4c4d733ee90a62b2dc921c6ff9fa3ce 100644
--- a/spec/controllers/import/bitbucket_controller_spec.rb
+++ b/spec/controllers/import/bitbucket_controller_spec.rb
@@ -31,8 +31,8 @@ describe Import::BitbucketController do
                             expires_at: expires_at,
                             expires_in: expires_in,
                             refresh_token: refresh_token)
-      allow_any_instance_of(OAuth2::Client).
-        to receive(:get_token).and_return(access_token)
+      allow_any_instance_of(OAuth2::Client)
+        .to receive(:get_token).and_return(access_token)
       stub_omniauth_provider('bitbucket')
 
       get :callback
@@ -93,9 +93,9 @@ describe Import::BitbucketController do
     context "when the repository owner is the Bitbucket user" do
       context "when the Bitbucket user and GitLab user's usernames match" do
         it "takes the current user's namespace" do
-          expect(Gitlab::BitbucketImport::ProjectCreator).
-            to receive(:new).with(bitbucket_repo, bitbucket_repo.name, user.namespace, user, access_params).
-            and_return(double(execute: true))
+          expect(Gitlab::BitbucketImport::ProjectCreator)
+            .to receive(:new).with(bitbucket_repo, bitbucket_repo.name, user.namespace, user, access_params)
+            .and_return(double(execute: true))
 
           post :create, format: :js
         end
@@ -105,9 +105,9 @@ describe Import::BitbucketController do
         let(:bitbucket_username) { "someone_else" }
 
         it "takes the current user's namespace" do
-          expect(Gitlab::BitbucketImport::ProjectCreator).
-            to receive(:new).with(bitbucket_repo, bitbucket_repo.name, user.namespace, user, access_params).
-            and_return(double(execute: true))
+          expect(Gitlab::BitbucketImport::ProjectCreator)
+            .to receive(:new).with(bitbucket_repo, bitbucket_repo.name, user.namespace, user, access_params)
+            .and_return(double(execute: true))
 
           post :create, format: :js
         end
@@ -141,9 +141,9 @@ describe Import::BitbucketController do
           end
 
           it "takes the existing namespace" do
-            expect(Gitlab::BitbucketImport::ProjectCreator).
-              to receive(:new).with(bitbucket_repo, bitbucket_repo.name, existing_namespace, user, access_params).
-              and_return(double(execute: true))
+            expect(Gitlab::BitbucketImport::ProjectCreator)
+              .to receive(:new).with(bitbucket_repo, bitbucket_repo.name, existing_namespace, user, access_params)
+              .and_return(double(execute: true))
 
             post :create, format: :js
           end
@@ -151,8 +151,8 @@ describe Import::BitbucketController do
 
         context "when the namespace is not owned by the GitLab user" do
           it "doesn't create a project" do
-            expect(Gitlab::BitbucketImport::ProjectCreator).
-              not_to receive(:new)
+            expect(Gitlab::BitbucketImport::ProjectCreator)
+              .not_to receive(:new)
 
             post :create, format: :js
           end
@@ -162,16 +162,16 @@ describe Import::BitbucketController do
       context "when a namespace with the Bitbucket user's username doesn't exist" do
         context "when current user can create namespaces" do
           it "creates the namespace" do
-            expect(Gitlab::BitbucketImport::ProjectCreator).
-              to receive(:new).and_return(double(execute: true))
+            expect(Gitlab::BitbucketImport::ProjectCreator)
+              .to receive(:new).and_return(double(execute: true))
 
             expect { post :create, format: :js }.to change(Namespace, :count).by(1)
           end
 
           it "takes the new namespace" do
-            expect(Gitlab::BitbucketImport::ProjectCreator).
-              to receive(:new).with(bitbucket_repo, bitbucket_repo.name, an_instance_of(Group), user, access_params).
-              and_return(double(execute: true))
+            expect(Gitlab::BitbucketImport::ProjectCreator)
+              .to receive(:new).with(bitbucket_repo, bitbucket_repo.name, an_instance_of(Group), user, access_params)
+              .and_return(double(execute: true))
 
             post :create, format: :js
           end
@@ -183,16 +183,16 @@ describe Import::BitbucketController do
           end
 
           it "doesn't create the namespace" do
-            expect(Gitlab::BitbucketImport::ProjectCreator).
-              to receive(:new).and_return(double(execute: true))
+            expect(Gitlab::BitbucketImport::ProjectCreator)
+              .to receive(:new).and_return(double(execute: true))
 
             expect { post :create, format: :js }.not_to change(Namespace, :count)
           end
 
           it "takes the current user's namespace" do
-            expect(Gitlab::BitbucketImport::ProjectCreator).
-              to receive(:new).with(bitbucket_repo, bitbucket_repo.name, user.namespace, user, access_params).
-              and_return(double(execute: true))
+            expect(Gitlab::BitbucketImport::ProjectCreator)
+              .to receive(:new).with(bitbucket_repo, bitbucket_repo.name, user.namespace, user, access_params)
+              .and_return(double(execute: true))
 
             post :create, format: :js
           end
@@ -210,9 +210,9 @@ describe Import::BitbucketController do
       end
 
       it 'takes the selected namespace and name' do
-        expect(Gitlab::BitbucketImport::ProjectCreator).
-          to receive(:new).with(bitbucket_repo, test_name, nested_namespace, user, access_params).
-            and_return(double(execute: true))
+        expect(Gitlab::BitbucketImport::ProjectCreator)
+          .to receive(:new).with(bitbucket_repo, test_name, nested_namespace, user, access_params)
+            .and_return(double(execute: true))
 
         post :create, { target_namespace: nested_namespace.full_path, new_name: test_name, format: :js }
       end
@@ -222,26 +222,26 @@ describe Import::BitbucketController do
       let(:test_name) { 'test_name' }
 
       it 'takes the selected namespace and name' do
-        expect(Gitlab::BitbucketImport::ProjectCreator).
-          to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params).
-            and_return(double(execute: true))
+        expect(Gitlab::BitbucketImport::ProjectCreator)
+          .to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params)
+            .and_return(double(execute: true))
 
         post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js }
       end
 
       it 'creates the namespaces' do
-        allow(Gitlab::BitbucketImport::ProjectCreator).
-          to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params).
-            and_return(double(execute: true))
+        allow(Gitlab::BitbucketImport::ProjectCreator)
+          .to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params)
+            .and_return(double(execute: true))
 
         expect { post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js } }
           .to change { Namespace.count }.by(2)
       end
 
       it 'new namespace has the right parent' do
-        allow(Gitlab::BitbucketImport::ProjectCreator).
-          to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params).
-            and_return(double(execute: true))
+        allow(Gitlab::BitbucketImport::ProjectCreator)
+          .to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params)
+            .and_return(double(execute: true))
 
         post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js }
 
@@ -254,17 +254,17 @@ describe Import::BitbucketController do
       let!(:parent_namespace) { create(:group, name: 'foo', owner: user) }
 
       it 'takes the selected namespace and name' do
-        expect(Gitlab::BitbucketImport::ProjectCreator).
-          to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params).
-            and_return(double(execute: true))
+        expect(Gitlab::BitbucketImport::ProjectCreator)
+          .to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params)
+            .and_return(double(execute: true))
 
         post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :js }
       end
 
       it 'creates the namespaces' do
-        allow(Gitlab::BitbucketImport::ProjectCreator).
-          to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params).
-            and_return(double(execute: true))
+        allow(Gitlab::BitbucketImport::ProjectCreator)
+          .to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params)
+            .and_return(double(execute: true))
 
         expect { post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :js } }
           .to change { Namespace.count }.by(2)
diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb
index 95696e14b6c5db12cb77e77fe84e05edcfa16c93..45c3fa075ef039688ad37c6ef07ce1a25121a595 100644
--- a/spec/controllers/import/github_controller_spec.rb
+++ b/spec/controllers/import/github_controller_spec.rb
@@ -21,10 +21,10 @@ describe Import::GithubController do
   describe "GET callback" do
     it "updates access token" do
       token = "asdasd12345"
-      allow_any_instance_of(Gitlab::GithubImport::Client).
-        to receive(:get_token).and_return(token)
-      allow_any_instance_of(Gitlab::GithubImport::Client).
-        to receive(:github_options).and_return({})
+      allow_any_instance_of(Gitlab::GithubImport::Client)
+        .to receive(:get_token).and_return(token)
+      allow_any_instance_of(Gitlab::GithubImport::Client)
+        .to receive(:github_options).and_return({})
       stub_omniauth_provider('github')
 
       get :callback
diff --git a/spec/controllers/import/gitlab_controller_spec.rb b/spec/controllers/import/gitlab_controller_spec.rb
index 3afd09063d7efc6674ec0c83252e45f3eecf2cda..997107dadeab8d88b4155202d24ccecd312ed3b1 100644
--- a/spec/controllers/import/gitlab_controller_spec.rb
+++ b/spec/controllers/import/gitlab_controller_spec.rb
@@ -18,8 +18,8 @@ describe Import::GitlabController do
 
   describe "GET callback" do
     it "updates access token" do
-      allow_any_instance_of(Gitlab::GitlabImport::Client).
-        to receive(:get_token).and_return(token)
+      allow_any_instance_of(Gitlab::GitlabImport::Client)
+        .to receive(:get_token).and_return(token)
       stub_omniauth_provider('gitlab')
 
       get :callback
@@ -78,9 +78,9 @@ describe Import::GitlabController do
     context "when the repository owner is the GitLab.com user" do
       context "when the GitLab.com user and GitLab server user's usernames match" do
         it "takes the current user's namespace" do
-          expect(Gitlab::GitlabImport::ProjectCreator).
-            to receive(:new).with(gitlab_repo, user.namespace, user, access_params).
-            and_return(double(execute: true))
+          expect(Gitlab::GitlabImport::ProjectCreator)
+            .to receive(:new).with(gitlab_repo, user.namespace, user, access_params)
+            .and_return(double(execute: true))
 
           post :create, format: :js
         end
@@ -90,9 +90,9 @@ describe Import::GitlabController do
         let(:gitlab_username) { "someone_else" }
 
         it "takes the current user's namespace" do
-          expect(Gitlab::GitlabImport::ProjectCreator).
-            to receive(:new).with(gitlab_repo, user.namespace, user, access_params).
-            and_return(double(execute: true))
+          expect(Gitlab::GitlabImport::ProjectCreator)
+            .to receive(:new).with(gitlab_repo, user.namespace, user, access_params)
+            .and_return(double(execute: true))
 
           post :create, format: :js
         end
@@ -116,9 +116,9 @@ describe Import::GitlabController do
           end
 
           it "takes the existing namespace" do
-            expect(Gitlab::GitlabImport::ProjectCreator).
-              to receive(:new).with(gitlab_repo, existing_namespace, user, access_params).
-              and_return(double(execute: true))
+            expect(Gitlab::GitlabImport::ProjectCreator)
+              .to receive(:new).with(gitlab_repo, existing_namespace, user, access_params)
+              .and_return(double(execute: true))
 
             post :create, format: :js
           end
@@ -126,8 +126,8 @@ describe Import::GitlabController do
 
         context "when the namespace is not owned by the GitLab server user" do
           it "doesn't create a project" do
-            expect(Gitlab::GitlabImport::ProjectCreator).
-              not_to receive(:new)
+            expect(Gitlab::GitlabImport::ProjectCreator)
+              .not_to receive(:new)
 
             post :create, format: :js
           end
@@ -137,16 +137,16 @@ describe Import::GitlabController do
       context "when a namespace with the GitLab.com user's username doesn't exist" do
         context "when current user can create namespaces" do
           it "creates the namespace" do
-            expect(Gitlab::GitlabImport::ProjectCreator).
-              to receive(:new).and_return(double(execute: true))
+            expect(Gitlab::GitlabImport::ProjectCreator)
+              .to receive(:new).and_return(double(execute: true))
 
             expect { post :create, format: :js }.to change(Namespace, :count).by(1)
           end
 
           it "takes the new namespace" do
-            expect(Gitlab::GitlabImport::ProjectCreator).
-              to receive(:new).with(gitlab_repo, an_instance_of(Group), user, access_params).
-              and_return(double(execute: true))
+            expect(Gitlab::GitlabImport::ProjectCreator)
+              .to receive(:new).with(gitlab_repo, an_instance_of(Group), user, access_params)
+              .and_return(double(execute: true))
 
             post :create, format: :js
           end
@@ -158,16 +158,16 @@ describe Import::GitlabController do
           end
 
           it "doesn't create the namespace" do
-            expect(Gitlab::GitlabImport::ProjectCreator).
-              to receive(:new).and_return(double(execute: true))
+            expect(Gitlab::GitlabImport::ProjectCreator)
+              .to receive(:new).and_return(double(execute: true))
 
             expect { post :create, format: :js }.not_to change(Namespace, :count)
           end
 
           it "takes the current user's namespace" do
-            expect(Gitlab::GitlabImport::ProjectCreator).
-              to receive(:new).with(gitlab_repo, user.namespace, user, access_params).
-              and_return(double(execute: true))
+            expect(Gitlab::GitlabImport::ProjectCreator)
+              .to receive(:new).with(gitlab_repo, user.namespace, user, access_params)
+              .and_return(double(execute: true))
 
             post :create, format: :js
           end
@@ -183,9 +183,9 @@ describe Import::GitlabController do
         end
 
         it 'takes the selected namespace and name' do
-          expect(Gitlab::GitlabImport::ProjectCreator).
-            to receive(:new).with(gitlab_repo, nested_namespace, user, access_params).
-              and_return(double(execute: true))
+          expect(Gitlab::GitlabImport::ProjectCreator)
+            .to receive(:new).with(gitlab_repo, nested_namespace, user, access_params)
+              .and_return(double(execute: true))
 
           post :create, { target_namespace: nested_namespace.full_path, format: :js }
         end
@@ -195,26 +195,26 @@ describe Import::GitlabController do
         let(:test_name) { 'test_name' }
 
         it 'takes the selected namespace and name' do
-          expect(Gitlab::GitlabImport::ProjectCreator).
-            to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params).
-              and_return(double(execute: true))
+          expect(Gitlab::GitlabImport::ProjectCreator)
+            .to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params)
+              .and_return(double(execute: true))
 
           post :create, { target_namespace: 'foo/bar', format: :js }
         end
 
         it 'creates the namespaces' do
-          allow(Gitlab::GitlabImport::ProjectCreator).
-            to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params).
-              and_return(double(execute: true))
+          allow(Gitlab::GitlabImport::ProjectCreator)
+            .to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params)
+              .and_return(double(execute: true))
 
           expect { post :create, { target_namespace: 'foo/bar', format: :js } }
             .to change { Namespace.count }.by(2)
         end
 
         it 'new namespace has the right parent' do
-          allow(Gitlab::GitlabImport::ProjectCreator).
-            to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params).
-              and_return(double(execute: true))
+          allow(Gitlab::GitlabImport::ProjectCreator)
+            .to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params)
+              .and_return(double(execute: true))
 
           post :create, { target_namespace: 'foo/bar', format: :js }
 
@@ -227,17 +227,17 @@ describe Import::GitlabController do
         let!(:parent_namespace) { create(:group, name: 'foo', owner: user) }
 
         it 'takes the selected namespace and name' do
-          expect(Gitlab::GitlabImport::ProjectCreator).
-            to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params).
-              and_return(double(execute: true))
+          expect(Gitlab::GitlabImport::ProjectCreator)
+            .to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params)
+              .and_return(double(execute: true))
 
           post :create, { target_namespace: 'foo/foobar/bar', format: :js }
         end
 
         it 'creates the namespaces' do
-          allow(Gitlab::GitlabImport::ProjectCreator).
-            to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params).
-              and_return(double(execute: true))
+          allow(Gitlab::GitlabImport::ProjectCreator)
+            .to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params)
+              .and_return(double(execute: true))
 
           expect { post :create, { target_namespace: 'foo/foobar/bar', format: :js } }
             .to change { Namespace.count }.by(2)
diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb
index 3b3caa9d3e63b4d4fdae61587dd3ae8b8fbe7487..c20cf6a4291f1f2b7bd41b3c91fe2c7ca21c39ff 100644
--- a/spec/controllers/projects/blob_controller_spec.rb
+++ b/spec/controllers/projects/blob_controller_spec.rb
@@ -47,8 +47,8 @@ describe Projects::BlobController do
       context 'redirect to tree' do
         let(:id) { 'markdown/doc' }
         it 'redirects' do
-          expect(subject).
-            to redirect_to("/#{project.path_with_namespace}/tree/markdown/doc")
+          expect(subject)
+            .to redirect_to("/#{project.path_with_namespace}/tree/markdown/doc")
         end
       end
     end
diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb
index f9e21f9d8f625d3947b855386cd47b2a9d3d0068..14426b09c73b06fd7d38acd778b2d1747da77eb4 100644
--- a/spec/controllers/projects/branches_controller_spec.rb
+++ b/spec/controllers/projects/branches_controller_spec.rb
@@ -32,8 +32,8 @@ describe Projects::BranchesController do
         let(:branch) { "merge_branch" }
         let(:ref) { "master" }
         it 'redirects' do
-          expect(subject).
-            to redirect_to("/#{project.path_with_namespace}/tree/merge_branch")
+          expect(subject)
+            .to redirect_to("/#{project.path_with_namespace}/tree/merge_branch")
         end
       end
 
@@ -41,8 +41,8 @@ describe Projects::BranchesController do
         let(:branch) { "<script>alert('merge');</script>" }
         let(:ref) { "master" }
         it 'redirects' do
-          expect(subject).
-            to redirect_to("/#{project.path_with_namespace}/tree/alert('merge');")
+          expect(subject)
+            .to redirect_to("/#{project.path_with_namespace}/tree/alert('merge');")
         end
       end
 
@@ -81,8 +81,8 @@ describe Projects::BranchesController do
           branch_name: branch,
           issue_iid: issue.iid
 
-        expect(subject).
-          to redirect_to("/#{project.path_with_namespace}/tree/1-feature-branch")
+        expect(subject)
+          .to redirect_to("/#{project.path_with_namespace}/tree/1-feature-branch")
       end
 
       it 'posts a system note' do
diff --git a/spec/controllers/projects/commit_controller_spec.rb b/spec/controllers/projects/commit_controller_spec.rb
index 7fb08df1950f7871a94bff61130ffccf182f0e0a..e10da40eaab945ba88fae912878a4fc056edd691 100644
--- a/spec/controllers/projects/commit_controller_spec.rb
+++ b/spec/controllers/projects/commit_controller_spec.rb
@@ -66,8 +66,8 @@ describe Projects::CommitController do
       end
 
       it "does not escape Html" do
-        allow_any_instance_of(Commit).to receive(:"to_#{format}").
-          and_return('HTML entities &<>" ')
+        allow_any_instance_of(Commit).to receive(:"to_#{format}")
+          .and_return('HTML entities &<>" ')
 
         go(id: commit.id, format: format)
 
diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb
index 749b090d6e02a59e8eabcdec0294d8bf2a5f3c57..ad0b046742d2e728129409033e5f50ad1c0ef188 100644
--- a/spec/controllers/projects/environments_controller_spec.rb
+++ b/spec/controllers/projects/environments_controller_spec.rb
@@ -58,9 +58,11 @@ describe Projects::EnvironmentsController do
           expect(json_response['stopped_count']).to eq 1
         end
 
-        it 'sets the polling interval header' do
+        it 'does not set the polling interval header' do
+          # TODO, this is a temporary fix, see follow up issue:
+          # https://gitlab.com/gitlab-org/gitlab-ee/issues/2677
           expect(response).to have_http_status(:ok)
-          expect(response.headers['Poll-Interval']).to eq("3000")
+          expect(response.headers['Poll-Interval']).to be_nil
         end
       end
 
@@ -233,14 +235,14 @@ describe Projects::EnvironmentsController do
 
       context 'and valid id' do
         it 'returns the first terminal for the environment' do
-          expect_any_instance_of(Environment).
-            to receive(:terminals).
-            and_return([:fake_terminal])
-
-          expect(Gitlab::Workhorse).
-            to receive(:terminal_websocket).
-            with(:fake_terminal).
-            and_return(workhorse: :response)
+          expect_any_instance_of(Environment)
+            .to receive(:terminals)
+            .and_return([:fake_terminal])
+
+          expect(Gitlab::Workhorse)
+            .to receive(:terminal_websocket)
+            .with(:fake_terminal)
+            .and_return(workhorse: :response)
 
           get :terminal_websocket_authorize, environment_params
 
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index f853bfe370c3b130dbb9a92e00b12ab325c77952..9f98427a86b129a98d5ca80c2b0d2d87e227a3ce 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -328,8 +328,8 @@ describe Projects::IssuesController do
             it 'redirect to issue page' do
               update_verified_issue
 
-              expect(response).
-                to redirect_to(namespace_project_issue_path(project.namespace, project, issue))
+              expect(response)
+                .to redirect_to(namespace_project_issue_path(project.namespace, project, issue))
             end
 
             it 'accepts an issue after recaptcha is verified' do
@@ -343,8 +343,8 @@ describe Projects::IssuesController do
             it 'does not mark spam log as recaptcha_verified when it does not belong to current_user' do
               spam_log = create(:spam_log)
 
-              expect { update_issue(spam_log_id: spam_log.id, recaptcha_verification: true) }.
-                not_to change { SpamLog.last.recaptcha_verified }
+              expect { update_issue(spam_log_id: spam_log.id, recaptcha_verification: true) }
+                .not_to change { SpamLog.last.recaptcha_verified }
             end
           end
         end
@@ -685,8 +685,8 @@ describe Projects::IssuesController do
           it 'does not mark spam log as recaptcha_verified when it does not belong to current_user' do
             spam_log = create(:spam_log)
 
-            expect { post_new_issue({}, { spam_log_id: spam_log.id, recaptcha_verification: true } ) }.
-              not_to change { SpamLog.last.recaptcha_verified }
+            expect { post_new_issue({}, { spam_log_id: spam_log.id, recaptcha_verification: true } ) }
+              .not_to change { SpamLog.last.recaptcha_verified }
           end
         end
       end
@@ -702,7 +702,7 @@ describe Projects::IssuesController do
       end
     end
 
-    context 'when description has slash commands' do
+    context 'when description has quick actions' do
       before do
         sign_in(user)
       end
diff --git a/spec/controllers/projects/mattermosts_controller_spec.rb b/spec/controllers/projects/mattermosts_controller_spec.rb
index c5abf11cfa5df2472db7cc57990a7e514e7ca661..422a8b6fac082fc7df5bd2b7ba9857fb8e03e81c 100644
--- a/spec/controllers/projects/mattermosts_controller_spec.rb
+++ b/spec/controllers/projects/mattermosts_controller_spec.rb
@@ -11,8 +11,8 @@ describe Projects::MattermostsController do
 
   describe 'GET #new' do
     before do
-      allow_any_instance_of(MattermostSlashCommandsService).
-        to receive(:list_teams).and_return([])
+      allow_any_instance_of(MattermostSlashCommandsService)
+        .to receive(:list_teams).and_return([])
     end
 
     it 'accepts the request' do
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index d8a3a510f97eae2c795e3ddfbfce810ea63e8c26..6817c2652fd6b1d60e2734780d37425c5c25f35b 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -783,8 +783,8 @@ describe Projects::MergeRequestsController do
   describe 'GET conflicts' do
     context 'when the conflicts cannot be resolved in the UI' do
       before do
-        allow_any_instance_of(Gitlab::Conflict::Parser).
-          to receive(:parse).and_raise(Gitlab::Conflict::Parser::UnmergeableFile)
+        allow_any_instance_of(Gitlab::Conflict::Parser)
+          .to receive(:parse).and_raise(Gitlab::Conflict::Parser::UnmergeableFile)
 
         get :conflicts,
             namespace_id: merge_request_with_conflicts.project.namespace.to_param,
@@ -926,8 +926,8 @@ describe Projects::MergeRequestsController do
 
     context 'when the conflicts cannot be resolved in the UI' do
       before do
-        allow_any_instance_of(Gitlab::Conflict::Parser).
-          to receive(:parse).and_raise(Gitlab::Conflict::Parser::UnmergeableFile)
+        allow_any_instance_of(Gitlab::Conflict::Parser)
+          .to receive(:parse).and_raise(Gitlab::Conflict::Parser::UnmergeableFile)
 
         conflict_for_path('files/ruby/regex.rb')
       end
@@ -959,9 +959,9 @@ describe Projects::MergeRequestsController do
       end
 
       it 'returns the file in JSON format' do
-        content = MergeRequests::Conflicts::ListService.new(merge_request_with_conflicts).
-                    file_for_path(path, path).
-                    content
+        content = MergeRequests::Conflicts::ListService.new(merge_request_with_conflicts)
+                    .file_for_path(path, path)
+                    .content
 
         expect(json_response).to include('old_path' => path,
                                          'new_path' => path,
@@ -1085,9 +1085,9 @@ describe Projects::MergeRequestsController do
 
     context 'when a file has identical content to the conflict' do
       before do
-        content = MergeRequests::Conflicts::ListService.new(merge_request_with_conflicts).
-                    file_for_path('files/ruby/popen.rb', 'files/ruby/popen.rb').
-                    content
+        content = MergeRequests::Conflicts::ListService.new(merge_request_with_conflicts)
+                    .file_for_path('files/ruby/popen.rb', 'files/ruby/popen.rb')
+                    .content
 
         resolved_files = [
           {
@@ -1153,9 +1153,9 @@ describe Projects::MergeRequestsController do
     end
 
     it 'calls MergeRequests::AssignIssuesService' do
-      expect(MergeRequests::AssignIssuesService).to receive(:new).
-        with(project, user, merge_request: merge_request).
-        and_return(double(execute: { count: 1 }))
+      expect(MergeRequests::AssignIssuesService).to receive(:new)
+        .with(project, user, merge_request: merge_request)
+        .and_return(double(execute: { count: 1 }))
 
       post_assign_issues
     end
diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb
index 952071af57ff432556f8150ab6193e799e255028..b4eaab29fed50645b4ac663ef28a6dfe5c82a7ac 100644
--- a/spec/controllers/projects/raw_controller_spec.rb
+++ b/spec/controllers/projects/raw_controller_spec.rb
@@ -15,8 +15,8 @@ describe Projects::RawController do
 
         expect(response).to have_http_status(200)
         expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
-        expect(response.header['Content-Disposition']).
-            to eq('inline')
+        expect(response.header['Content-Disposition'])
+            .to eq('inline')
         expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
       end
     end
@@ -88,8 +88,8 @@ describe Projects::RawController do
 
           expect(response).to have_http_status(200)
           expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
-          expect(response.header['Content-Disposition']).
-              to eq('inline')
+          expect(response.header['Content-Disposition'])
+              .to eq('inline')
           expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with('git-blob:')
         end
       end
diff --git a/spec/controllers/projects/services_controller_spec.rb b/spec/controllers/projects/services_controller_spec.rb
index 23b463c0082a6ce4c35c09586468a556102e7404..4dc227a36d4131d352d14ebbc1619964fecaa24b 100644
--- a/spec/controllers/projects/services_controller_spec.rb
+++ b/spec/controllers/projects/services_controller_spec.rb
@@ -67,8 +67,8 @@ describe Projects::ServicesController do
         put :test, namespace_id: project.namespace.id, project_id: project.id, id: service.id, service: service_params
 
         expect(response.status).to eq(200)
-        expect(JSON.parse(response.body)).
-          to eq('error' => true, 'message' => 'Test failed.', 'service_response' => 'Bad test')
+        expect(JSON.parse(response.body))
+          .to eq('error' => true, 'message' => 'Test failed.', 'service_response' => 'Bad test')
       end
     end
   end
diff --git a/spec/controllers/projects/snippets_controller_spec.rb b/spec/controllers/projects/snippets_controller_spec.rb
index 2434f822c6f0ef6e50f588d6f9737eb193dae7e6..ec0b7f8c96769e23fc6eb3f2cc383dab68e6e833 100644
--- a/spec/controllers/projects/snippets_controller_spec.rb
+++ b/spec/controllers/projects/snippets_controller_spec.rb
@@ -103,21 +103,21 @@ describe Projects::SnippetsController do
 
       context 'when the snippet is private' do
         it 'creates the snippet' do
-          expect { create_snippet(project, visibility_level: Snippet::PRIVATE) }.
-            to change { Snippet.count }.by(1)
+          expect { create_snippet(project, visibility_level: Snippet::PRIVATE) }
+            .to change { Snippet.count }.by(1)
         end
       end
 
       context 'when the snippet is public' do
         it 'rejects the shippet' do
-          expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }.
-            not_to change { Snippet.count }
+          expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }
+            .not_to change { Snippet.count }
           expect(response).to render_template(:new)
         end
 
         it 'creates a spam log' do
-          expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }.
-            to change { SpamLog.count }.by(1)
+          expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }
+            .to change { SpamLog.count }.by(1)
         end
 
         it 'renders :new with recaptcha disabled' do
@@ -183,8 +183,8 @@ describe Projects::SnippetsController do
         let(:visibility_level) { Snippet::PRIVATE }
 
         it 'updates the snippet' do
-          expect { update_snippet(title: 'Foo') }.
-            to change { snippet.reload.title }.to('Foo')
+          expect { update_snippet(title: 'Foo') }
+            .to change { snippet.reload.title }.to('Foo')
         end
       end
 
@@ -192,13 +192,13 @@ describe Projects::SnippetsController do
         let(:visibility_level) { Snippet::PUBLIC }
 
         it 'rejects the shippet' do
-          expect { update_snippet(title: 'Foo') }.
-            not_to change { snippet.reload.title }
+          expect { update_snippet(title: 'Foo') }
+            .not_to change { snippet.reload.title }
         end
 
         it 'creates a spam log' do
-          expect { update_snippet(title: 'Foo') }.
-            to change { SpamLog.count }.by(1)
+          expect { update_snippet(title: 'Foo') }
+            .to change { SpamLog.count }.by(1)
         end
 
         it 'renders :edit with recaptcha disabled' do
@@ -237,13 +237,13 @@ describe Projects::SnippetsController do
         let(:visibility_level) { Snippet::PRIVATE }
 
         it 'rejects the shippet' do
-          expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
-            not_to change { snippet.reload.title }
+          expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }
+            .not_to change { snippet.reload.title }
         end
 
         it 'creates a spam log' do
-          expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
-            to change { SpamLog.count }.by(1)
+          expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }
+            .to change { SpamLog.count }.by(1)
         end
 
         it 'renders :edit with recaptcha disabled' do
diff --git a/spec/controllers/projects/tree_controller_spec.rb b/spec/controllers/projects/tree_controller_spec.rb
index a43dad5756d23df2a123ec9b63e5f69c33d28756..16cd2e076e5e0bc14dbaccd0cf8255d0d06d9d33 100644
--- a/spec/controllers/projects/tree_controller_spec.rb
+++ b/spec/controllers/projects/tree_controller_spec.rb
@@ -82,8 +82,8 @@ describe Projects::TreeController do
       let(:id) { 'master/README.md' }
       it 'redirects' do
         redirect_url = "/#{project.path_with_namespace}/blob/master/README.md"
-        expect(subject).
-          to redirect_to(redirect_url)
+        expect(subject)
+          .to redirect_to(redirect_url)
       end
     end
   end
@@ -106,8 +106,8 @@ describe Projects::TreeController do
       let(:branch_name) { 'master-test'}
 
       it 'redirects to the new directory' do
-        expect(subject).
-            to redirect_to("/#{project.path_with_namespace}/tree/#{branch_name}/#{path}")
+        expect(subject)
+            .to redirect_to("/#{project.path_with_namespace}/tree/#{branch_name}/#{path}")
         expect(flash[:notice]).to eq('The directory has been successfully created.')
       end
     end
@@ -117,8 +117,8 @@ describe Projects::TreeController do
       let(:branch_name) { 'master'}
 
       it 'does not allow overwriting of existing files' do
-        expect(subject).
-            to redirect_to("/#{project.path_with_namespace}/tree/master")
+        expect(subject)
+            .to redirect_to("/#{project.path_with_namespace}/tree/master")
         expect(flash[:alert]).to eq('A file with this name already exists')
       end
     end
diff --git a/spec/controllers/sent_notifications_controller_spec.rb b/spec/controllers/sent_notifications_controller_spec.rb
index 0cc8a3b68eb589e8826b6665e6675ef4afb704f9..917bd44c91b32abc6c8c763843adac1300bc034b 100644
--- a/spec/controllers/sent_notifications_controller_spec.rb
+++ b/spec/controllers/sent_notifications_controller_spec.rb
@@ -87,8 +87,8 @@ describe SentNotificationsController, type: :controller do
         end
 
         it 'redirects to the issue page' do
-          expect(response).
-            to redirect_to(namespace_project_issue_path(project.namespace, project, issue))
+          expect(response)
+            .to redirect_to(namespace_project_issue_path(project.namespace, project, issue))
         end
       end
 
@@ -113,8 +113,8 @@ describe SentNotificationsController, type: :controller do
         end
 
         it 'redirects to the merge request page' do
-          expect(response).
-            to redirect_to(namespace_project_merge_request_path(project.namespace, project, merge_request))
+          expect(response)
+            .to redirect_to(namespace_project_merge_request_path(project.namespace, project, merge_request))
         end
       end
     end
diff --git a/spec/controllers/sessions_controller_spec.rb b/spec/controllers/sessions_controller_spec.rb
index 03f4b0ba3437c73f601d9ed5b480df353475b4e7..bf922260b2f7a61d2a82767b00a5851f698be114 100644
--- a/spec/controllers/sessions_controller_spec.rb
+++ b/spec/controllers/sessions_controller_spec.rb
@@ -9,8 +9,8 @@ describe SessionsController do
     context 'when auto sign-in is enabled' do
       before do
         stub_omniauth_setting(auto_sign_in_with_provider: :saml)
-        allow(controller).to receive(:omniauth_authorize_path).with(:user, :saml).
-          and_return('/saml')
+        allow(controller).to receive(:omniauth_authorize_path).with(:user, :saml)
+          .and_return('/saml')
       end
 
       context 'and no auto_sign_in param is passed' do
@@ -89,8 +89,8 @@ describe SessionsController do
       context 'remember_me field' do
         it 'sets a remember_user_token cookie when enabled' do
           allow(controller).to receive(:find_user).and_return(user)
-          expect(controller).
-            to receive(:remember_me).with(user).and_call_original
+          expect(controller)
+            .to receive(:remember_me).with(user).and_call_original
 
           authenticate_2fa(remember_me: '1', otp_attempt: user.current_otp)
 
@@ -228,8 +228,8 @@ describe SessionsController do
         it 'sets a remember_user_token cookie when enabled' do
           allow(U2fRegistration).to receive(:authenticate).and_return(true)
           allow(controller).to receive(:find_user).and_return(user)
-          expect(controller).
-            to receive(:remember_me).with(user).and_call_original
+          expect(controller)
+            .to receive(:remember_me).with(user).and_call_original
 
           authenticate_2fa_u2f(remember_me: '1', login: user.username, device_response: "{}")
 
@@ -262,8 +262,8 @@ describe SessionsController do
 
     it 'redirects correctly for referer on same host with params' do
       search_path = '/search?search=seed_project'
-      allow(controller.request).to receive(:referer).
-        and_return('http://%{host}%{path}' % { host: Gitlab.config.gitlab.host, path: search_path })
+      allow(controller.request).to receive(:referer)
+        .and_return('http://%{host}%{path}' % { host: Gitlab.config.gitlab.host, path: search_path })
 
       get(:new, redirect_to_referer: :yes)
 
diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb
index b1339b2a185320c9c7981cd3569d8dbefc74367c..430d1208cd1cfb4a678e78c67c274d7688081a0f 100644
--- a/spec/controllers/snippets_controller_spec.rb
+++ b/spec/controllers/snippets_controller_spec.rb
@@ -222,20 +222,20 @@ describe SnippetsController do
 
       context 'when the snippet is private' do
         it 'creates the snippet' do
-          expect { create_snippet(visibility_level: Snippet::PRIVATE) }.
-            to change { Snippet.count }.by(1)
+          expect { create_snippet(visibility_level: Snippet::PRIVATE) }
+            .to change { Snippet.count }.by(1)
         end
       end
 
       context 'when the snippet is public' do
         it 'rejects the shippet' do
-          expect { create_snippet(visibility_level: Snippet::PUBLIC) }.
-            not_to change { Snippet.count }
+          expect { create_snippet(visibility_level: Snippet::PUBLIC) }
+            .not_to change { Snippet.count }
         end
 
         it 'creates a spam log' do
-          expect { create_snippet(visibility_level: Snippet::PUBLIC) }.
-            to change { SpamLog.count }.by(1)
+          expect { create_snippet(visibility_level: Snippet::PUBLIC) }
+            .to change { SpamLog.count }.by(1)
         end
 
         it 'renders :new with recaptcha disabled' do
@@ -296,8 +296,8 @@ describe SnippetsController do
         let(:visibility_level) { Snippet::PRIVATE }
 
         it 'updates the snippet' do
-          expect { update_snippet(title: 'Foo') }.
-            to change { snippet.reload.title }.to('Foo')
+          expect { update_snippet(title: 'Foo') }
+            .to change { snippet.reload.title }.to('Foo')
         end
       end
 
@@ -305,13 +305,13 @@ describe SnippetsController do
         let(:visibility_level) { Snippet::PRIVATE }
 
         it 'rejects the snippet' do
-          expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
-            not_to change { snippet.reload.title }
+          expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }
+            .not_to change { snippet.reload.title }
         end
 
         it 'creates a spam log' do
-          expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
-            to change { SpamLog.count }.by(1)
+          expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }
+            .to change { SpamLog.count }.by(1)
         end
 
         it 'renders :edit with recaptcha disabled' do
@@ -350,13 +350,13 @@ describe SnippetsController do
         let(:visibility_level) { Snippet::PUBLIC }
 
         it 'rejects the shippet' do
-          expect { update_snippet(title: 'Foo') }.
-            not_to change { snippet.reload.title }
+          expect { update_snippet(title: 'Foo') }
+            .not_to change { snippet.reload.title }
         end
 
         it 'creates a spam log' do
-          expect { update_snippet(title: 'Foo') }.
-            to change { SpamLog.count }.by(1)
+          expect { update_snippet(title: 'Foo') }
+            .to change { SpamLog.count }.by(1)
         end
 
         it 'renders :edit with recaptcha disabled' do
diff --git a/spec/factories/application_settings.rb b/spec/factories/application_settings.rb
new file mode 100644
index 0000000000000000000000000000000000000000..aef65e724c280d0dba6cf6e43ae54bbb4144d5d6
--- /dev/null
+++ b/spec/factories/application_settings.rb
@@ -0,0 +1,4 @@
+FactoryGirl.define do
+  factory :application_setting do
+  end
+end
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index e17e50db143f455067242e36c4b7135a7beea35e..aef1c17a2394a902bdd3c749704f6da6d5ce1ede 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -118,8 +118,8 @@ FactoryGirl.define do
       builds_access_level = [evaluator.builds_access_level, evaluator.repository_access_level].min
       merge_requests_access_level = [evaluator.merge_requests_access_level, evaluator.repository_access_level].min
 
-      project.project_feature.
-        update_attributes!(
+      project.project_feature
+        .update_attributes!(
           wiki_access_level: evaluator.wiki_access_level,
           builds_access_level: builds_access_level,
           snippets_access_level: evaluator.snippets_access_level,
diff --git a/spec/features/abuse_report_spec.rb b/spec/features/abuse_report_spec.rb
index 1e11fb756b23953c3408238ffb51fc65ff7ed77e..5e6cd64c5c11e7f324f20c7c08f5787b136b2e60 100644
--- a/spec/features/abuse_report_spec.rb
+++ b/spec/features/abuse_report_spec.rb
@@ -4,7 +4,7 @@ feature 'Abuse reports', feature: true do
   let(:another_user) { create(:user) }
 
   before do
-    login_as :user
+    gitlab_sign_in :user
   end
 
   scenario 'Report abuse' do
diff --git a/spec/features/admin/admin_abuse_reports_spec.rb b/spec/features/admin/admin_abuse_reports_spec.rb
index 340884fc986835dd6133470d1582bc178bb463b9..3a6e356b0b07a288a99fb4be5373694727c6feb9 100644
--- a/spec/features/admin/admin_abuse_reports_spec.rb
+++ b/spec/features/admin/admin_abuse_reports_spec.rb
@@ -5,7 +5,7 @@ describe "Admin::AbuseReports", feature: true, js: true  do
 
   context 'as an admin' do
     before do
-      login_as :admin
+      gitlab_sign_in :admin
     end
 
     describe 'if a user has been reported for abuse' do
diff --git a/spec/features/admin/admin_active_tab_spec.rb b/spec/features/admin/admin_active_tab_spec.rb
index 16064d60ce2c24d67b6714b30426e2d6735351af..c74336d8221203da8b2550b6e839c72a7ce3863d 100644
--- a/spec/features/admin/admin_active_tab_spec.rb
+++ b/spec/features/admin/admin_active_tab_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
 
 RSpec.describe 'admin active tab' do
   before do
-    login_as :admin
+    gitlab_sign_in :admin
   end
 
   shared_examples 'page has active tab' do |title|
diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb
index 595366ce35278feac0effa3f71a67eb3b821e567..d8fd4319328e9999d80756b8d7f926ea4e0dceff 100644
--- a/spec/features/admin/admin_appearance_spec.rb
+++ b/spec/features/admin/admin_appearance_spec.rb
@@ -4,7 +4,7 @@ feature 'Admin Appearance', feature: true do
   let!(:appearance) { create(:appearance) }
 
   scenario 'Create new appearance' do
-    login_as :admin
+    gitlab_sign_in :admin
     visit admin_appearances_path
 
     fill_in 'appearance_title', with: 'MyCompany'
@@ -20,7 +20,7 @@ feature 'Admin Appearance', feature: true do
   end
 
   scenario 'Preview appearance' do
-    login_as :admin
+    gitlab_sign_in :admin
 
     visit admin_appearances_path
     click_link "Preview"
@@ -34,7 +34,7 @@ feature 'Admin Appearance', feature: true do
   end
 
   scenario 'Appearance logo' do
-    login_as :admin
+    gitlab_sign_in :admin
     visit admin_appearances_path
 
     attach_file(:appearance_logo, logo_fixture)
@@ -46,7 +46,7 @@ feature 'Admin Appearance', feature: true do
   end
 
   scenario 'Header logos' do
-    login_as :admin
+    gitlab_sign_in :admin
     visit admin_appearances_path
 
     attach_file(:appearance_header_logo, logo_fixture)
diff --git a/spec/features/admin/admin_broadcast_messages_spec.rb b/spec/features/admin/admin_broadcast_messages_spec.rb
index d6c63f66a9bf0e0d4c63db02ae625a0e810ed290..da063bf7b744542336caaf903fbe326d4940a4a2 100644
--- a/spec/features/admin/admin_broadcast_messages_spec.rb
+++ b/spec/features/admin/admin_broadcast_messages_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
 
 feature 'Admin Broadcast Messages', feature: true do
   before do
-    login_as :admin
+    gitlab_sign_in :admin
     create(:broadcast_message, :expired, message: 'Migration to new server')
     visit admin_broadcast_messages_path
   end
diff --git a/spec/features/admin/admin_browse_spam_logs_spec.rb b/spec/features/admin/admin_browse_spam_logs_spec.rb
index bee57472270f8e8cc7912a876f2430c3cd58c758..d9c4fc686b138687ac347ea107125b3376c78996 100644
--- a/spec/features/admin/admin_browse_spam_logs_spec.rb
+++ b/spec/features/admin/admin_browse_spam_logs_spec.rb
@@ -4,7 +4,7 @@ describe 'Admin browse spam logs' do
   let!(:spam_log) { create(:spam_log, description: 'abcde ' * 20) }
 
   before do
-    login_as :admin
+    gitlab_sign_in :admin
   end
 
   scenario 'Browse spam logs' do
diff --git a/spec/features/admin/admin_browses_logs_spec.rb b/spec/features/admin/admin_browses_logs_spec.rb
index d880f3f07db3bf51fc2a7009426dc7b61a4a5e36..c734a2ef16daef47ec20001b0a8560c0e64c468d 100644
--- a/spec/features/admin/admin_browses_logs_spec.rb
+++ b/spec/features/admin/admin_browses_logs_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
 
 describe 'Admin browses logs' do
   before do
-    login_as :admin
+    gitlab_sign_in :admin
   end
 
   it 'shows available log files' do
diff --git a/spec/features/admin/admin_builds_spec.rb b/spec/features/admin/admin_builds_spec.rb
index 999ce3611b505f651c26b7b12e53a8e2a5b4bee5..e767081f3e58d91099607b41a1719c22214d3977 100644
--- a/spec/features/admin/admin_builds_spec.rb
+++ b/spec/features/admin/admin_builds_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
 
 describe 'Admin Builds' do
   before do
-    login_as :admin
+    gitlab_sign_in :admin
   end
 
   describe 'GET /admin/builds' do
diff --git a/spec/features/admin/admin_cohorts_spec.rb b/spec/features/admin/admin_cohorts_spec.rb
index dd14ffdb2ceb3b304091dc145de81b31e8682ef6..952e5475213a9ec578e7ea8a888de847bcfa7678 100644
--- a/spec/features/admin/admin_cohorts_spec.rb
+++ b/spec/features/admin/admin_cohorts_spec.rb
@@ -2,7 +2,7 @@ require 'rails_helper'
 
 feature 'Admin cohorts page', feature: true do
   before do
-    login_as :admin
+    gitlab_sign_in :admin
   end
 
   scenario 'See users count per month' do
diff --git a/spec/features/admin/admin_conversational_development_index_spec.rb b/spec/features/admin/admin_conversational_development_index_spec.rb
index 739ab907a29adb14afdeecc376a26dfa9654e5f7..b484677a6dfb48f2f78f4d3bc75a949a7948c17f 100644
--- a/spec/features/admin/admin_conversational_development_index_spec.rb
+++ b/spec/features/admin/admin_conversational_development_index_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
 
 describe 'Admin Conversational Development Index' do
   before do
-    login_as :admin
+    gitlab_sign_in :admin
   end
 
   context 'when usage ping is disabled' do
diff --git a/spec/features/admin/admin_deploy_keys_spec.rb b/spec/features/admin/admin_deploy_keys_spec.rb
index 5f5fa4e932afafae92a28d2ba109e811c3c205c8..81cddd03f80bf72a45daa7fb587b2af33b411233 100644
--- a/spec/features/admin/admin_deploy_keys_spec.rb
+++ b/spec/features/admin/admin_deploy_keys_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe 'admin deploy keys', type: :feature do
   let!(:another_deploy_key) { create(:another_deploy_key, public: true) }
 
   before do
-    login_as(:admin)
+    gitlab_sign_in(:admin)
   end
 
   it 'show all public deploy keys' do
diff --git a/spec/features/admin/admin_disables_git_access_protocol_spec.rb b/spec/features/admin/admin_disables_git_access_protocol_spec.rb
index e8e080ce3e26ba52fc3c18fd170249e4320d89ae..679bf63e0fd654ca5a224c699f32b038a66e6b8f 100644
--- a/spec/features/admin/admin_disables_git_access_protocol_spec.rb
+++ b/spec/features/admin/admin_disables_git_access_protocol_spec.rb
@@ -8,7 +8,7 @@ feature 'Admin disables Git access protocol', feature: true do
 
   background do
     stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
-    login_as(admin)
+    gitlab_sign_in(admin)
   end
 
   context 'with HTTP disabled' do
diff --git a/spec/features/admin/admin_disables_two_factor_spec.rb b/spec/features/admin/admin_disables_two_factor_spec.rb
index 71be66303d236041e90b026472f9348cd26f3f2a..5437da299797cd2f565a4c857b7cdb903ae10f1b 100644
--- a/spec/features/admin/admin_disables_two_factor_spec.rb
+++ b/spec/features/admin/admin_disables_two_factor_spec.rb
@@ -2,7 +2,7 @@ require 'rails_helper'
 
 feature 'Admin disables 2FA for a user', feature: true do
   scenario 'successfully', js: true do
-    login_as(:admin)
+    gitlab_sign_in(:admin)
     user = create(:user, :two_factor)
 
     edit_user(user)
@@ -17,7 +17,7 @@ feature 'Admin disables 2FA for a user', feature: true do
   end
 
   scenario 'for a user without 2FA enabled' do
-    login_as(:admin)
+    gitlab_sign_in(:admin)
     user = create(:user)
 
     edit_user(user)
diff --git a/spec/features/admin/admin_groups_spec.rb b/spec/features/admin/admin_groups_spec.rb
index cf9d7bca2558562fc45292c8b9df0b061bdeac94..8b0fafc5f07003960ac5b78a5fb8a025bfd0258d 100644
--- a/spec/features/admin/admin_groups_spec.rb
+++ b/spec/features/admin/admin_groups_spec.rb
@@ -6,7 +6,7 @@ feature 'Admin Groups', feature: true do
   let(:internal) { Gitlab::VisibilityLevel::INTERNAL }
   let(:user) { create :user }
   let!(:group) { create :group }
-  let!(:current_user) { login_as :admin }
+  let!(:current_user) { gitlab_sign_in :admin }
 
   before do
     stub_application_setting(default_group_visibility: internal)
diff --git a/spec/features/admin/admin_health_check_spec.rb b/spec/features/admin/admin_health_check_spec.rb
index 523afa2318fe805fd2ab7d9df8914609e388502f..75093aa4167c8bffee6875283670dbedc0977162 100644
--- a/spec/features/admin/admin_health_check_spec.rb
+++ b/spec/features/admin/admin_health_check_spec.rb
@@ -5,7 +5,7 @@ feature "Admin Health Check", feature: true do
 
   before do
     stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
-    login_as :admin
+    gitlab_sign_in :admin
   end
 
   describe '#show' do
diff --git a/spec/features/admin/admin_hook_logs_spec.rb b/spec/features/admin/admin_hook_logs_spec.rb
index 5b67f4de6ac309aed016dc5254d5b32cdc155165..ec80c420c7938f7cee54647898d2d75ca7bab799 100644
--- a/spec/features/admin/admin_hook_logs_spec.rb
+++ b/spec/features/admin/admin_hook_logs_spec.rb
@@ -6,7 +6,7 @@ feature 'Admin::HookLogs', feature: true do
   let(:hook_log) { create(:web_hook_log, web_hook: system_hook, internal_error_message: 'some error') }
 
   before do
-    login_as :admin
+    gitlab_sign_in :admin
   end
 
   scenario 'show list of hook logs' do
diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/features/admin/admin_hooks_spec.rb
index 80f7ec43c060c76f2a31ae7f424c065f019a6313..c07c21bd6a188f935887aad2422c31b1c28fe20c 100644
--- a/spec/features/admin/admin_hooks_spec.rb
+++ b/spec/features/admin/admin_hooks_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
 describe 'Admin::Hooks', feature: true do
   before do
     @project = create(:project)
-    login_as :admin
+    gitlab_sign_in :admin
 
     @system_hook = create(:system_hook)
   end
diff --git a/spec/features/admin/admin_labels_spec.rb b/spec/features/admin/admin_labels_spec.rb
index a9251db13e543b7ad2dd8ccb2ed8f4a13d187ac9..bb40918bd22ed23eeacaa106104d1134b46a2691 100644
--- a/spec/features/admin/admin_labels_spec.rb
+++ b/spec/features/admin/admin_labels_spec.rb
@@ -5,7 +5,7 @@ RSpec.describe 'admin issues labels' do
   let!(:feature_label) { Label.create(title: 'feature', template: true) }
 
   before do
-    login_as :admin
+    gitlab_sign_in :admin
   end
 
   describe 'list' do
diff --git a/spec/features/admin/admin_manage_applications_spec.rb b/spec/features/admin/admin_manage_applications_spec.rb
index 0079125889b9d331f6d0612cb72fa20d5d636329..ae41267e5fc0491d619c5505c5ad095137d5c221 100644
--- a/spec/features/admin/admin_manage_applications_spec.rb
+++ b/spec/features/admin/admin_manage_applications_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
 
 RSpec.describe 'admin manage applications', feature: true do
   before do
-    login_as :admin
+    gitlab_sign_in :admin
   end
 
   it do
diff --git a/spec/features/admin/admin_projects_spec.rb b/spec/features/admin/admin_projects_spec.rb
index 9d205104ebecdf4b7bdac92a1a3130ee3afe17d5..a4ce3e1d5ee0b6b9c0bc79dc223f2b7714f54f48 100644
--- a/spec/features/admin/admin_projects_spec.rb
+++ b/spec/features/admin/admin_projects_spec.rb
@@ -6,7 +6,7 @@ describe "Admin::Projects", feature: true  do
   let(:user) { create :user }
   let!(:project) { create(:project) }
   let!(:current_user) do
-    login_as :admin
+    gitlab_sign_in :admin
   end
 
   describe "GET /admin/projects" do
@@ -57,8 +57,8 @@ describe "Admin::Projects", feature: true  do
     before do
       create(:group, name: 'Web')
 
-      allow_any_instance_of(Projects::TransferService).
-        to receive(:move_uploads_to_new_namespace).and_return(true)
+      allow_any_instance_of(Projects::TransferService)
+        .to receive(:move_uploads_to_new_namespace).and_return(true)
     end
 
     it 'transfers project to group web', js: true do
diff --git a/spec/features/admin/admin_requests_profiles_spec.rb b/spec/features/admin/admin_requests_profiles_spec.rb
index e8ecb70306baa03621a2a78db943a3deea4de91f..2bfe401521be9d39a178cd362955ebdacc9e4694 100644
--- a/spec/features/admin/admin_requests_profiles_spec.rb
+++ b/spec/features/admin/admin_requests_profiles_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
 describe 'Admin::RequestsProfilesController', feature: true do
   before do
     FileUtils.mkdir_p(Gitlab::RequestProfiler::PROFILES_DIR)
-    login_as(:admin)
+    gitlab_sign_in(:admin)
   end
 
   after do
diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb
index bc11b090fdb67f942a2ef1293b61fc15da78688c..5b3323fed1304b9c692e8e0981f204a9403ff093 100644
--- a/spec/features/admin/admin_runners_spec.rb
+++ b/spec/features/admin/admin_runners_spec.rb
@@ -5,7 +5,7 @@ describe "Admin Runners" do
 
   before do
     stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
-    login_as :admin
+    gitlab_sign_in :admin
   end
 
   describe "Runners page" do
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index 27bc25be5801d3c61c65953fdf08d979de5a8aeb..2d6565e6d3b271dd0c0907c3873356d235f2ee51 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -5,7 +5,7 @@ feature 'Admin updates settings', feature: true do
 
   before do
     stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
-    login_as :admin
+    gitlab_sign_in :admin
     visit admin_application_settings_path
   end
 
diff --git a/spec/features/admin/admin_system_info_spec.rb b/spec/features/admin/admin_system_info_spec.rb
index 1548234788668c6df091677f0329c52a0be32b0e..4efc7f0eb4813ca4fc03504e898d88119eaa36fd 100644
--- a/spec/features/admin/admin_system_info_spec.rb
+++ b/spec/features/admin/admin_system_info_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
 
 describe 'Admin System Info' do
   before do
-    login_as :admin
+    gitlab_sign_in :admin
   end
 
   describe 'GET /admin/system_info' do
diff --git a/spec/features/admin/admin_users_impersonation_tokens_spec.rb b/spec/features/admin/admin_users_impersonation_tokens_spec.rb
index 849ec829f754aad33c8c78025573df950284398c..231c094c91d749542763dca2338dda902cbf8cb5 100644
--- a/spec/features/admin/admin_users_impersonation_tokens_spec.rb
+++ b/spec/features/admin/admin_users_impersonation_tokens_spec.rb
@@ -13,7 +13,7 @@ describe 'Admin > Users > Impersonation Tokens', feature: true, js: true do
   end
 
   before do
-    login_as(admin)
+    gitlab_sign_in(admin)
   end
 
   describe "token creation" do
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index f72651667eee5bdb514936bb54861ebe5a3e3c04..6dbc697642fcbc6381e6c7c53b48396cf4d336fe 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -5,7 +5,7 @@ describe "Admin::Users", feature: true do
     create(:omniauth_user, provider: 'twitter', extern_uid: '123456')
   end
 
-  let!(:current_user) { login_as :admin }
+  let!(:current_user) { gitlab_sign_in :admin }
 
   describe "GET /admin/users" do
     before do
@@ -78,10 +78,10 @@ describe "Admin::Users", feature: true do
     it "applies defaults to user" do
       click_button "Create user"
       user = User.find_by(username: 'bang')
-      expect(user.projects_limit).
-        to eq(Gitlab.config.gitlab.default_projects_limit)
-      expect(user.can_create_group).
-        to eq(Gitlab.config.gitlab.default_can_create_group)
+      expect(user.projects_limit)
+        .to eq(Gitlab.config.gitlab.default_projects_limit)
+      expect(user.can_create_group)
+        .to eq(Gitlab.config.gitlab.default_can_create_group)
     end
 
     it "creates user with valid data" do
diff --git a/spec/features/admin/admin_uses_repository_checks_spec.rb b/spec/features/admin/admin_uses_repository_checks_spec.rb
index ab5c42365fe6aa759706093da42ab306aaff4d57..91d70435db88f6461232d3ac5ba19bc7321fa487 100644
--- a/spec/features/admin/admin_uses_repository_checks_spec.rb
+++ b/spec/features/admin/admin_uses_repository_checks_spec.rb
@@ -5,7 +5,7 @@ feature 'Admin uses repository checks', feature: true do
 
   before do
     stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
-    login_as :admin
+    gitlab_sign_in :admin
   end
 
   scenario 'to trigger a single check' do
diff --git a/spec/features/atom/dashboard_spec.rb b/spec/features/atom/dashboard_spec.rb
index 1df058b023cb0eaad21f679bb1dfd51fe06e6eb2..2f4bb45d74b990f84dd325934faaeba8cd567e5c 100644
--- a/spec/features/atom/dashboard_spec.rb
+++ b/spec/features/atom/dashboard_spec.rb
@@ -35,8 +35,8 @@ describe "Dashboard Feed", feature: true  do
       end
 
       it "has issue comment event" do
-        expect(body).
-          to have_content("#{user.name} commented on issue ##{issue.iid}")
+        expect(body)
+          .to have_content("#{user.name} commented on issue ##{issue.iid}")
       end
     end
   end
diff --git a/spec/features/atom/issues_spec.rb b/spec/features/atom/issues_spec.rb
index a61231ea254ff27a12ff40217c0f13f05b4f87b3..b94ad973fed955cd63b40692239e1b6554843a35 100644
--- a/spec/features/atom/issues_spec.rb
+++ b/spec/features/atom/issues_spec.rb
@@ -15,11 +15,11 @@ describe 'Issues Feed', feature: true  do
 
     context 'when authenticated' do
       it 'renders atom feed' do
-        login_with user
+        gitlab_sign_in user
         visit namespace_project_issues_path(project.namespace, project, :atom)
 
-        expect(response_headers['Content-Type']).
-          to have_content('application/atom+xml')
+        expect(response_headers['Content-Type'])
+          .to have_content('application/atom+xml')
         expect(body).to have_selector('title', text: "#{project.name} issues")
         expect(body).to have_selector('author email', text: issue.author_public_email)
         expect(body).to have_selector('assignees assignee email', text: issue.assignees.first.public_email)
@@ -33,8 +33,8 @@ describe 'Issues Feed', feature: true  do
         visit namespace_project_issues_path(project.namespace, project, :atom,
                                             private_token: user.private_token)
 
-        expect(response_headers['Content-Type']).
-          to have_content('application/atom+xml')
+        expect(response_headers['Content-Type'])
+          .to have_content('application/atom+xml')
         expect(body).to have_selector('title', text: "#{project.name} issues")
         expect(body).to have_selector('author email', text: issue.author_public_email)
         expect(body).to have_selector('assignees assignee email', text: issue.assignees.first.public_email)
@@ -48,8 +48,8 @@ describe 'Issues Feed', feature: true  do
         visit namespace_project_issues_path(project.namespace, project, :atom,
                                             rss_token: user.rss_token)
 
-        expect(response_headers['Content-Type']).
-          to have_content('application/atom+xml')
+        expect(response_headers['Content-Type'])
+          .to have_content('application/atom+xml')
         expect(body).to have_selector('title', text: "#{project.name} issues")
         expect(body).to have_selector('author email', text: issue.author_public_email)
         expect(body).to have_selector('assignees assignee email', text: issue.assignees.first.public_email)
diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb
index fae5aaa52bd7ca3ee83b7c51759a2c372ac238e2..44ae7204bcf349ebc1e60557535ce1126e69ebd8 100644
--- a/spec/features/atom/users_spec.rb
+++ b/spec/features/atom/users_spec.rb
@@ -55,8 +55,8 @@ describe "User Feed", feature: true  do
       end
 
       it 'has issue comment event' do
-        expect(body).
-          to have_content("#{safe_name} commented on issue ##{issue.iid}")
+        expect(body)
+          .to have_content("#{safe_name} commented on issue ##{issue.iid}")
       end
 
       it 'has XHTML summaries in issue descriptions' do
diff --git a/spec/features/auto_deploy_spec.rb b/spec/features/auto_deploy_spec.rb
index 1cf7396bbaca1f9b5a97cf09d4799caf3ae684c0..74f5f70702a55d4c35cd66bd36fdc4c902e6eb00 100644
--- a/spec/features/auto_deploy_spec.rb
+++ b/spec/features/auto_deploy_spec.rb
@@ -7,7 +7,7 @@ describe 'Auto deploy' do
   before do
     create :kubernetes_service, project: project
     project.team << [user, :master]
-    login_as user
+    gitlab_sign_in user
   end
 
   context 'when no deployment service is active' do
diff --git a/spec/features/boards/add_issues_modal_spec.rb b/spec/features/boards/add_issues_modal_spec.rb
index 2b8edac4f10fa4e5f621c1fd2f80385ca8d04f10..ba58af2284146a3e2c60b5dc97620d28dbe8953b 100644
--- a/spec/features/boards/add_issues_modal_spec.rb
+++ b/spec/features/boards/add_issues_modal_spec.rb
@@ -14,7 +14,7 @@ describe 'Issue Boards add issue modal', :feature, :js do
   before do
     project.team << [user, :master]
 
-    login_as(user)
+    gitlab_sign_in(user)
 
     visit namespace_project_board_path(project.namespace, project, board)
     wait_for_requests
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index 968cc9d9c8078af03d6cc53343cd29864567c2ff..87fc31d414ced7ad818bcda913cd09dbd0ea88e7 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -12,7 +12,7 @@ describe 'Issue Boards', feature: true, js: true do
     project.team << [user, :master]
     project.team << [user2, :master]
 
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   context 'no lists' do
@@ -247,13 +247,13 @@ describe 'Issue Boards', feature: true, js: true do
       end
 
       it 'issue moves from closed' do
-        drag(list_from_index: 3, list_to_index: 2)
+        drag(list_from_index: 2, list_to_index: 3)
 
         wait_for_board_cards(2, 8)
-        wait_for_board_cards(3, 3)
-        wait_for_board_cards(4, 0)
+        wait_for_board_cards(3, 1)
+        wait_for_board_cards(4, 2)
 
-        expect(find('.board:nth-child(3)')).to have_content(issue8.title)
+        expect(find('.board:nth-child(4)')).to have_content(issue8.title)
       end
 
       context 'issue card' do
@@ -519,7 +519,7 @@ describe 'Issue Boards', feature: true, js: true do
 
   context 'signed out user' do
     before do
-      logout
+      gitlab_sign_out
       visit namespace_project_board_path(project.namespace, project, board)
       wait_for_requests
     end
@@ -542,8 +542,8 @@ describe 'Issue Boards', feature: true, js: true do
 
     before do
       project.team << [user_guest, :guest]
-      logout
-      login_as(user_guest)
+      gitlab_sign_out
+      gitlab_sign_in(user_guest)
       visit namespace_project_board_path(project.namespace, project, board)
       wait_for_requests
     end
diff --git a/spec/features/boards/issue_ordering_spec.rb b/spec/features/boards/issue_ordering_spec.rb
index 1c289993e281a18d928565d42645799fbd102ad6..1e620061e5edecd1928466ca8fca80eba6d2bd52 100644
--- a/spec/features/boards/issue_ordering_spec.rb
+++ b/spec/features/boards/issue_ordering_spec.rb
@@ -15,7 +15,7 @@ describe 'Issue Boards', :feature, :js do
   before do
     project.team << [user, :master]
 
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   context 'un-ordered issues' do
diff --git a/spec/features/boards/keyboard_shortcut_spec.rb b/spec/features/boards/keyboard_shortcut_spec.rb
index c2167ba12cd6f4c1723a2fe848456cbebdadd84d..ed3b38e6a7e9b3c146ec034d303993d5cfefde8d 100644
--- a/spec/features/boards/keyboard_shortcut_spec.rb
+++ b/spec/features/boards/keyboard_shortcut_spec.rb
@@ -6,7 +6,7 @@ describe 'Issue Boards shortcut', feature: true, js: true do
   before do
     create(:board, project: project)
 
-    login_as :admin
+    gitlab_sign_in :admin
 
     visit namespace_project_path(project.namespace, project)
   end
diff --git a/spec/features/boards/modal_filter_spec.rb b/spec/features/boards/modal_filter_spec.rb
index b6de6143354b22d8f112c0a7b6ea47215e834bb7..8899e1ef5e5076e2bc115e44fe4e63eaeabdd545 100644
--- a/spec/features/boards/modal_filter_spec.rb
+++ b/spec/features/boards/modal_filter_spec.rb
@@ -12,7 +12,7 @@ describe 'Issue Boards add issue modal filtering', :feature, :js do
   before do
     project.team << [user, :master]
 
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   it 'shows empty state when no results found' do
diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb
index 7ba60247587eba7092e16e7e40324d97c9e9c7a9..77cd87d660178e8340134f87d19472727ee27eaa 100644
--- a/spec/features/boards/new_issue_spec.rb
+++ b/spec/features/boards/new_issue_spec.rb
@@ -10,7 +10,7 @@ describe 'Issue Boards new issue', feature: true, js: true do
     before do
       project.team << [user, :master]
 
-      login_as(user)
+      gitlab_sign_in(user)
 
       visit namespace_project_board_path(project.namespace, project, board)
       wait_for_requests
diff --git a/spec/features/boards/sidebar_spec.rb b/spec/features/boards/sidebar_spec.rb
index 235e48997073d95037587b4d4b08bb73f6b02999..301c243febd448917896b53694be13365a768180 100644
--- a/spec/features/boards/sidebar_spec.rb
+++ b/spec/features/boards/sidebar_spec.rb
@@ -20,7 +20,7 @@ describe 'Issue Boards', feature: true, js: true do
 
     project.team << [user, :master]
 
-    login_as(user)
+    gitlab_sign_in(user)
 
     visit namespace_project_board_path(project.namespace, project, board)
     wait_for_requests
diff --git a/spec/features/boards/sub_group_project_spec.rb b/spec/features/boards/sub_group_project_spec.rb
index 4cd05010a9326f4d22242df3406159a9e3961893..d57ae6a71e71eab6e20dab3388c6c14627949fad 100644
--- a/spec/features/boards/sub_group_project_spec.rb
+++ b/spec/features/boards/sub_group_project_spec.rb
@@ -13,7 +13,7 @@ describe 'Sub-group project issue boards', :feature, :js do
   before do
     project.add_master(user)
 
-    login_as(user)
+    gitlab_sign_in(user)
 
     visit namespace_project_board_path(project.namespace, project, board)
     wait_for_requests
diff --git a/spec/features/calendar_spec.rb b/spec/features/calendar_spec.rb
index 1b6d8439f92778d816f7f7c54cc7b9f258cfa284..b2e72fc7dee4af29b0d5d962f4c51778244869df 100644
--- a/spec/features/calendar_spec.rb
+++ b/spec/features/calendar_spec.rb
@@ -68,7 +68,7 @@ feature 'Contributions Calendar', :feature, :js do
   end
 
   before do
-    login_as user
+    gitlab_sign_in user
   end
 
   describe 'calendar day selection' do
diff --git a/spec/features/ci_lint_spec.rb b/spec/features/ci_lint_spec.rb
index 3ebc432206a0049e9dc12cca291b2f583cf6d467..de16ee3e567d2514b4edc686182b16c7c640c7b6 100644
--- a/spec/features/ci_lint_spec.rb
+++ b/spec/features/ci_lint_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
 
 describe 'CI Lint', js: true do
   before do
-    login_as :user
+    gitlab_sign_in :user
   end
 
   describe 'YAML parsing' do
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index 2772f05982a0db9b555ffe58d01b4e7bf475bbe4..0373f649ee81524ae154313735b54f9cda689ee2 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -4,10 +4,11 @@ describe 'Commits' do
   include CiStatusHelper
 
   let(:project) { create(:project, :repository) }
+  let(:user) { create(:user) }
 
   describe 'CI' do
     before do
-      login_as :user
+      sign_in(user)
       stub_ci_pipeline_to_return_yaml_file
     end
 
@@ -27,7 +28,7 @@ describe 'Commits' do
       let!(:status) { create(:generic_commit_status, pipeline: pipeline) }
 
       before do
-        project.team << [@user, :reporter]
+        project.team << [user, :reporter]
       end
 
       describe 'Commit builds' do
@@ -52,7 +53,7 @@ describe 'Commits' do
 
       context 'when logged as developer' do
         before do
-          project.team << [@user, :developer]
+          project.team << [user, :developer]
         end
 
         describe 'Project commits' do
@@ -146,7 +147,7 @@ describe 'Commits' do
 
       context "when logged as reporter" do
         before do
-          project.team << [@user, :reporter]
+          project.team << [user, :reporter]
           build.update_attributes(artifacts_file: artifacts_file)
           visit ci_status_path(pipeline)
         end
@@ -187,11 +188,10 @@ describe 'Commits' do
 
   context 'viewing commits for a branch' do
     let(:branch_name) { 'master' }
-    let(:user) { create(:user) }
 
     before do
       project.team << [user, :master]
-      login_with(user)
+      sign_in(user)
       visit namespace_project_commits_path(project.namespace, project, branch_name)
     end
 
diff --git a/spec/features/container_registry_spec.rb b/spec/features/container_registry_spec.rb
index fa7adbe71ea073060383af5c10dbdc6aea66aa09..80d16539d5a692a88e9545b0cc1c4e4dad2e0e16 100644
--- a/spec/features/container_registry_spec.rb
+++ b/spec/features/container_registry_spec.rb
@@ -9,7 +9,7 @@ describe "Container Registry" do
   end
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
     project.add_developer(user)
     stub_container_registry_config(enabled: true)
     stub_container_registry_tags(repository: :any, tags: [])
diff --git a/spec/features/copy_as_gfm_spec.rb b/spec/features/copy_as_gfm_spec.rb
index 740f60c05cce4a0ae4d16bf7a01089e6c5eee962..005c88f6bab0953d0243625493eb412b278feca2 100644
--- a/spec/features/copy_as_gfm_spec.rb
+++ b/spec/features/copy_as_gfm_spec.rb
@@ -6,7 +6,7 @@ describe 'Copy as GFM', feature: true, js: true do
   include ActionView::Helpers::JavaScriptHelper
 
   before do
-    login_as :admin
+    gitlab_sign_in :admin
   end
 
   describe 'Copying rendered GFM' do
diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb
index b416bbd3c7979bccbec1555c5c262e60955eaa02..5a7ea975455f7d50f3d36e45059c1d12363956eb 100644
--- a/spec/features/cycle_analytics_spec.rb
+++ b/spec/features/cycle_analytics_spec.rb
@@ -14,7 +14,7 @@ feature 'Cycle Analytics', feature: true, js: true do
       before  do
         project.add_master(user)
 
-        login_as(user)
+        gitlab_sign_in(user)
 
         visit namespace_project_cycle_analytics_path(project.namespace, project)
         wait_for_requests
@@ -38,7 +38,7 @@ feature 'Cycle Analytics', feature: true, js: true do
         create_cycle
         deploy_master
 
-        login_as(user)
+        gitlab_sign_in(user)
         visit namespace_project_cycle_analytics_path(project.namespace, project)
       end
 
@@ -70,7 +70,7 @@ feature 'Cycle Analytics', feature: true, js: true do
         user.update_attribute(:preferred_language, 'es')
 
         project.team << [user, :master]
-        login_as(user)
+        gitlab_sign_in(user)
         visit namespace_project_cycle_analytics_path(project.namespace, project)
         wait_for_requests
       end
@@ -93,7 +93,7 @@ feature 'Cycle Analytics', feature: true, js: true do
       create_cycle
       deploy_master
 
-      login_as(guest)
+      gitlab_sign_in(guest)
       visit namespace_project_cycle_analytics_path(project.namespace, project)
       wait_for_requests
     end
diff --git a/spec/features/dashboard/active_tab_spec.rb b/spec/features/dashboard/active_tab_spec.rb
index ae750be4d4ae26a972356ecec4ee7928cbf3567e..f7ddded10c1a24f3e4355b444eada0b16fce68cb 100644
--- a/spec/features/dashboard/active_tab_spec.rb
+++ b/spec/features/dashboard/active_tab_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
 
 RSpec.describe 'Dashboard Active Tab', js: true, feature: true do
   before do
-    login_as :user
+    gitlab_sign_in :user
   end
 
   shared_examples 'page has active tab' do |title|
diff --git a/spec/features/dashboard/activity_spec.rb b/spec/features/dashboard/activity_spec.rb
index 0764044260e350da067ce456cae127f751b2a7ab..1e9cabe7850bb21e2f36bdcc652dca34ca42c22f 100644
--- a/spec/features/dashboard/activity_spec.rb
+++ b/spec/features/dashboard/activity_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
 
 RSpec.describe 'Dashboard Activity', feature: true do
   before do
-    login_as(create :user)
+    gitlab_sign_in(create :user)
     visit activity_dashboard_path
   end
 
diff --git a/spec/features/dashboard/archived_projects_spec.rb b/spec/features/dashboard/archived_projects_spec.rb
index f33bcbb5318b362be36fb0579e84bb70b6ad27f6..a5ba3e7e3cff6d47be81669852054b9dac0bd47e 100644
--- a/spec/features/dashboard/archived_projects_spec.rb
+++ b/spec/features/dashboard/archived_projects_spec.rb
@@ -9,7 +9,7 @@ RSpec.describe 'Dashboard Archived Project', feature: true do
     project.team << [user, :master]
     archived_project.team << [user, :master]
 
-    login_as(user)
+    gitlab_sign_in(user)
 
     visit dashboard_projects_path
   end
diff --git a/spec/features/dashboard/datetime_on_tooltips_spec.rb b/spec/features/dashboard/datetime_on_tooltips_spec.rb
index 1793e323588fd747b789937c977082d57105c16e..6931d0a840ea59edb3760aa744091bc5554ebe75 100644
--- a/spec/features/dashboard/datetime_on_tooltips_spec.rb
+++ b/spec/features/dashboard/datetime_on_tooltips_spec.rb
@@ -4,7 +4,7 @@ feature 'Tooltips on .timeago dates', feature: true, js: true do
   let(:user)            { create(:user) }
   let(:project)         { create(:project, name: 'test', namespace: user.namespace) }
   let(:created_date)    { Date.yesterday.to_time }
-  let(:expected_format) { created_date.strftime('%b %-d, %Y %l:%M%P') }
+  let(:expected_format) { created_date.in_time_zone.strftime('%b %-d, %Y %l:%M%P') }
 
   context 'on the activity tab' do
     before do
@@ -13,7 +13,7 @@ feature 'Tooltips on .timeago dates', feature: true, js: true do
       Event.create( project: project, author_id: user.id, action: Event::JOINED,
                     updated_at: created_date, created_at: created_date)
 
-      login_as user
+      gitlab_sign_in user
       visit user_path(user)
       wait_for_requests()
 
@@ -30,7 +30,7 @@ feature 'Tooltips on .timeago dates', feature: true, js: true do
       project.team << [user, :master]
       create(:snippet, author: user, updated_at: created_date, created_at: created_date)
 
-      login_as user
+      gitlab_sign_in user
       visit user_snippets_path(user)
       wait_for_requests()
 
diff --git a/spec/features/dashboard/group_spec.rb b/spec/features/dashboard/group_spec.rb
index 8e20fdec8ade63a60f077fd7def3b1203bb1ebce..2f7245950ec42c218bda9bf779e454ac4d19f886 100644
--- a/spec/features/dashboard/group_spec.rb
+++ b/spec/features/dashboard/group_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
 
 RSpec.describe 'Dashboard Group', feature: true do
   before do
-    login_as(:user)
+    gitlab_sign_in(:user)
   end
 
   it 'creates new group', js: true do
diff --git a/spec/features/dashboard/groups_list_spec.rb b/spec/features/dashboard/groups_list_spec.rb
index 7eb254f845120d973ef7e306758d9553ef7c34ee..e520027bc38c6eb11286e9423a7f1e590da0de43 100644
--- a/spec/features/dashboard/groups_list_spec.rb
+++ b/spec/features/dashboard/groups_list_spec.rb
@@ -10,7 +10,7 @@ describe 'Dashboard Groups page', js: true, feature: true do
     group.add_owner(user)
     nested_group.add_owner(user)
 
-    login_as(user)
+    gitlab_sign_in(user)
     visit dashboard_groups_path
 
     expect(page).to have_content(group.full_name)
@@ -23,7 +23,7 @@ describe 'Dashboard Groups page', js: true, feature: true do
       group.add_owner(user)
       nested_group.add_owner(user)
 
-      login_as(user)
+      gitlab_sign_in(user)
 
       visit dashboard_groups_path
     end
@@ -58,7 +58,7 @@ describe 'Dashboard Groups page', js: true, feature: true do
       group.add_owner(user)
       subgroup.add_owner(user)
 
-      login_as(user)
+      gitlab_sign_in(user)
 
       visit dashboard_groups_path
     end
@@ -98,7 +98,7 @@ describe 'Dashboard Groups page', js: true, feature: true do
 
       allow(Kaminari.config).to receive(:default_per_page).and_return(1)
 
-      login_as(user)
+      gitlab_sign_in(user)
       visit dashboard_groups_path
     end
 
diff --git a/spec/features/dashboard/help_spec.rb b/spec/features/dashboard/help_spec.rb
index 2803f7ec62b1a907d909d1d14ab509f9f67aa733..25b0f40c9cda8cccfc32735f884652f758c35d5c 100644
--- a/spec/features/dashboard/help_spec.rb
+++ b/spec/features/dashboard/help_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
 
 RSpec.describe 'Dashboard Help', feature: true do
   before do
-    login_as(:user)
+    gitlab_sign_in(:user)
   end
 
   it 'renders correctly markdown' do
diff --git a/spec/features/dashboard/issuables_counter_spec.rb b/spec/features/dashboard/issuables_counter_spec.rb
index 354267dbee7fe24ef1a49d2bcf2f86c4dfe36bbb..8a8a20fd5b1b884baf1fbf0ec6643e201039ed5a 100644
--- a/spec/features/dashboard/issuables_counter_spec.rb
+++ b/spec/features/dashboard/issuables_counter_spec.rb
@@ -9,7 +9,7 @@ describe 'Navigation bar counter', feature: true, caching: true do
   before do
     issue.assignees = [user]
     merge_request.update(assignee: user)
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   it 'reflects dashboard issues count' do
diff --git a/spec/features/dashboard/issues_spec.rb b/spec/features/dashboard/issues_spec.rb
index 2cea6b1563e302b46cd88c91789f4084386285f8..a57962abbda31b8e0b0ce9e5e22d818726e990af 100644
--- a/spec/features/dashboard/issues_spec.rb
+++ b/spec/features/dashboard/issues_spec.rb
@@ -12,7 +12,7 @@ RSpec.describe 'Dashboard Issues', feature: true do
 
   before do
     [project, project_with_issues_disabled].each { |project| project.team << [current_user, :master] }
-    login_as(current_user)
+    gitlab_sign_in(current_user)
     visit issues_dashboard_path(assignee_id: current_user.id)
   end
 
@@ -59,6 +59,11 @@ RSpec.describe 'Dashboard Issues', feature: true do
       expect(page).to have_content(other_issue.title)
     end
 
+    it 'state filter tabs work' do
+      find('#state-closed').click
+      expect(page).to have_current_path(issues_dashboard_url(assignee_id: current_user.id, scope: 'all', state: 'closed'), url: true)
+    end
+
     it_behaves_like "it has an RSS button with current_user's RSS token"
     it_behaves_like "an autodiscoverable RSS feed with current_user's RSS token"
   end
diff --git a/spec/features/dashboard/label_filter_spec.rb b/spec/features/dashboard/label_filter_spec.rb
index 4cff12de854a2da9a41f22d7a3717ee4653ea5e6..88bbb9e75b910653d78540384b33daf57b1cb6d2 100644
--- a/spec/features/dashboard/label_filter_spec.rb
+++ b/spec/features/dashboard/label_filter_spec.rb
@@ -11,7 +11,7 @@ describe 'Dashboard > label filter', feature: true, js: true do
     project.labels << label
     project2.labels << label2
 
-    login_as(user)
+    gitlab_sign_in(user)
     visit issues_dashboard_path
   end
 
diff --git a/spec/features/dashboard/merge_requests_spec.rb b/spec/features/dashboard/merge_requests_spec.rb
index bcb52f602b0a895c9efa8d6092d5e69cbec0ba6e..69d5500848eaf855ac5668af36e096c47e605c9c 100644
--- a/spec/features/dashboard/merge_requests_spec.rb
+++ b/spec/features/dashboard/merge_requests_spec.rb
@@ -8,7 +8,7 @@ describe 'Dashboard Merge Requests' do
   before do
     [project, project_with_merge_requests_disabled].each { |project| project.team << [current_user, :master] }
 
-    login_as(current_user)
+    gitlab_sign_in(current_user)
   end
 
   describe 'new merge request dropdown' do
diff --git a/spec/features/dashboard/milestone_filter_spec.rb b/spec/features/dashboard/milestone_filter_spec.rb
index b5b92c368956afe2f3c1484f3fecce8253112759..295262980a6ed66758d098ebf31e89368f55f163 100644
--- a/spec/features/dashboard/milestone_filter_spec.rb
+++ b/spec/features/dashboard/milestone_filter_spec.rb
@@ -9,7 +9,7 @@ describe 'Dashboard > milestone filter', :feature, :js do
   let!(:issue2) { create :issue, author: user, project: project, milestone: milestone2 }
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
     visit issues_dashboard_path(author_id: user.id)
   end
 
diff --git a/spec/features/dashboard/milestone_tabs_spec.rb b/spec/features/dashboard/milestone_tabs_spec.rb
index 0c7b992c5008e5be9caec6f7b7d92be684735e5c..cc4193b180f9e3555d8dff0de1dd5fcaf6d8da1e 100644
--- a/spec/features/dashboard/milestone_tabs_spec.rb
+++ b/spec/features/dashboard/milestone_tabs_spec.rb
@@ -15,7 +15,7 @@ describe 'Dashboard milestone tabs', :js, :feature do
 
   before do
     project.add_master(user)
-    login_as(user)
+    gitlab_sign_in(user)
 
     visit dashboard_milestone_path(milestone.safe_title, title: milestone.title)
   end
@@ -23,7 +23,7 @@ describe 'Dashboard milestone tabs', :js, :feature do
   it 'loads merge requests async' do
     click_link 'Merge Requests'
 
-    expect(page).to have_selector('.merge_requests-sortable-list')
+    expect(page).to have_selector('.milestone-merge_requests-list')
   end
 
   it 'loads participants async' do
diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb
index 3568954a54819fddeb11af9e15b95f39494340af..2a8185ca6690bae7b52f5c1ce607e9ad39f20d1f 100644
--- a/spec/features/dashboard/projects_spec.rb
+++ b/spec/features/dashboard/projects_spec.rb
@@ -7,7 +7,7 @@ RSpec.describe 'Dashboard Projects', feature: true do
 
   before do
     project.team << [user, :developer]
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   it 'shows the project the user in a member of in the list' do
diff --git a/spec/features/dashboard/shortcuts_spec.rb b/spec/features/dashboard/shortcuts_spec.rb
index 349b948eaeec26e835d917d7485a524c255cff71..525b0e1b2107e715dbeee09af02887e32281a612 100644
--- a/spec/features/dashboard/shortcuts_spec.rb
+++ b/spec/features/dashboard/shortcuts_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
 feature 'Dashboard shortcuts', :feature, :js do
   context 'logged in' do
     before do
-      login_as :user
+      gitlab_sign_in :user
       visit root_dashboard_path
     end
 
diff --git a/spec/features/dashboard/snippets_spec.rb b/spec/features/dashboard/snippets_spec.rb
index c6ba118220aef6ac9029bb229dc91e71a50ff96b..0c069ae5cf047359a0423548088b49db47138741 100644
--- a/spec/features/dashboard/snippets_spec.rb
+++ b/spec/features/dashboard/snippets_spec.rb
@@ -6,7 +6,7 @@ describe 'Dashboard snippets', feature: true do
     let!(:snippets) { create_list(:project_snippet, 2, :public, author: project.owner, project: project) }
     before do
       allow(Snippet).to receive(:default_per_page).and_return(1)
-      login_as(project.owner)
+      gitlab_sign_in(project.owner)
       visit dashboard_snippets_path
     end
 
@@ -25,7 +25,7 @@ describe 'Dashboard snippets', feature: true do
     end
 
     before do
-      login_as(user)
+      gitlab_sign_in(user)
 
       visit dashboard_snippets_path
     end
diff --git a/spec/features/dashboard/user_filters_projects_spec.rb b/spec/features/dashboard/user_filters_projects_spec.rb
index 34d6257f5fd77b217b016a75700e2e63fd108f99..e9f34760143b5af473cb807668bcca7cc4c2d966 100644
--- a/spec/features/dashboard/user_filters_projects_spec.rb
+++ b/spec/features/dashboard/user_filters_projects_spec.rb
@@ -9,7 +9,7 @@ describe 'Dashboard > User filters projects', :feature do
   before do
     project.team << [user, :master]
 
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   describe 'filtering personal projects' do
diff --git a/spec/features/dashboard_issues_spec.rb b/spec/features/dashboard_issues_spec.rb
index 1c53f6dff06561b6ce72db0e99ea494e68b72b6b..c4dbaad2895836967e4da23ced815d4b791043c4 100644
--- a/spec/features/dashboard_issues_spec.rb
+++ b/spec/features/dashboard_issues_spec.rb
@@ -8,7 +8,7 @@ describe "Dashboard Issues filtering", feature: true, js: true do
   context 'filtering by milestone' do
     before do
       project.team << [user, :master]
-      login_as(user)
+      gitlab_sign_in(user)
 
       create(:issue, project: project, author: user, assignees: [user])
       create(:issue, project: project, author: user, assignees: [user], milestone: milestone)
diff --git a/spec/features/dashboard_milestones_spec.rb b/spec/features/dashboard_milestones_spec.rb
index f32fddbc9fadafc0e7ec8aef58a7d343fc7acc1a..b308a2297b949744eb05f33a2b664fcc1727f3b9 100644
--- a/spec/features/dashboard_milestones_spec.rb
+++ b/spec/features/dashboard_milestones_spec.rb
@@ -17,7 +17,7 @@ feature 'Dashboard > Milestones', feature: true do
     let!(:milestone) { create(:milestone, project: project) }
     before do
       project.team << [user, :master]
-      login_with(user)
+      gitlab_sign_in(user)
       visit dashboard_milestones_path
     end
 
diff --git a/spec/features/discussion_comments/commit_spec.rb b/spec/features/discussion_comments/commit_spec.rb
index 96e0b78f6b9446420083a5cdf2348c526cdbf34d..96128061e4dedd48b0f92a8279d65d2241f4a179 100644
--- a/spec/features/discussion_comments/commit_spec.rb
+++ b/spec/features/discussion_comments/commit_spec.rb
@@ -9,7 +9,7 @@ describe 'Discussion Comments Merge Request', :feature, :js do
 
   before do
     project.add_master(user)
-    login_as(user)
+    gitlab_sign_in(user)
 
     visit namespace_project_commit_path(project.namespace, project, sample_commit.id)
   end
diff --git a/spec/features/discussion_comments/issue_spec.rb b/spec/features/discussion_comments/issue_spec.rb
index ccc9efccd1855fa12f6f80ea1a742c52a4341a0b..d7c1cd12fb52615be3d893cf0649d620fb974a91 100644
--- a/spec/features/discussion_comments/issue_spec.rb
+++ b/spec/features/discussion_comments/issue_spec.rb
@@ -7,7 +7,7 @@ describe 'Discussion Comments Issue', :feature, :js do
 
   before do
     project.add_master(user)
-    login_as(user)
+    gitlab_sign_in(user)
 
     visit namespace_project_issue_path(project.namespace, project, issue)
   end
diff --git a/spec/features/discussion_comments/merge_request_spec.rb b/spec/features/discussion_comments/merge_request_spec.rb
index f99ebeb9cd927ad0bca7f2050a960028483f1f1b..31fb9c72d257e1d05a064b2a63c42b585102710c 100644
--- a/spec/features/discussion_comments/merge_request_spec.rb
+++ b/spec/features/discussion_comments/merge_request_spec.rb
@@ -7,7 +7,7 @@ describe 'Discussion Comments Merge Request', :feature, :js do
 
   before do
     project.add_master(user)
-    login_as(user)
+    gitlab_sign_in(user)
 
     visit namespace_project_merge_request_path(project.namespace, project, merge_request)
   end
diff --git a/spec/features/discussion_comments/snippets_spec.rb b/spec/features/discussion_comments/snippets_spec.rb
index 19a306511b2efe901afc642f2c9ae916a523cd26..998d633c83dc23db5bae9957b60442936879de6c 100644
--- a/spec/features/discussion_comments/snippets_spec.rb
+++ b/spec/features/discussion_comments/snippets_spec.rb
@@ -7,7 +7,7 @@ describe 'Discussion Comments Issue', :feature, :js do
 
   before do
     project.add_master(user)
-    login_as(user)
+    gitlab_sign_in(user)
 
     visit namespace_project_snippet_path(project.namespace, project, snippet)
   end
diff --git a/spec/features/expand_collapse_diffs_spec.rb b/spec/features/expand_collapse_diffs_spec.rb
index 36b0c371e6eaf37ad2434c0e36a3063e7c831871..ea749528c1172e6397a1412c0c65c31fe26d5319 100644
--- a/spec/features/expand_collapse_diffs_spec.rb
+++ b/spec/features/expand_collapse_diffs_spec.rb
@@ -10,7 +10,7 @@ feature 'Expand and collapse diffs', js: true, feature: true do
     allow(Gitlab::Git::Diff).to receive(:size_limit).and_return(100.kilobytes)
     allow(Gitlab::Git::Diff).to receive(:collapse_limit).and_return(10.kilobytes)
 
-    login_as :admin
+    gitlab_sign_in :admin
 
     # Ensure that undiffable.md is in .gitattributes
     project.repository.copy_gitattributes(branch)
diff --git a/spec/features/explore/groups_list_spec.rb b/spec/features/explore/groups_list_spec.rb
index d4284ed099b390a76bfe07d43bd4ac97550e444c..6be5dee0c3c52ea6e55d8c8e9fabcd29e5a37875 100644
--- a/spec/features/explore/groups_list_spec.rb
+++ b/spec/features/explore/groups_list_spec.rb
@@ -10,7 +10,7 @@ describe 'Explore Groups page', :js, :feature do
   before do
     group.add_owner(user)
 
-    login_as(user)
+    gitlab_sign_in(user)
 
     visit explore_groups_path
   end
diff --git a/spec/features/explore/new_menu_spec.rb b/spec/features/explore/new_menu_spec.rb
index 15a6354211b03bf00ff688c06ef6b4763e9fc8be..2d7e703688f70dfd5459f032de1ce88c86704581 100644
--- a/spec/features/explore/new_menu_spec.rb
+++ b/spec/features/explore/new_menu_spec.rb
@@ -16,7 +16,7 @@ feature 'Top Plus Menu', feature: true, js: true do
 
   context 'used by full user' do
     before do
-      login_as(user)
+      gitlab_sign_in(user)
     end
 
     scenario 'click on New project shows new project page' do
@@ -103,7 +103,7 @@ feature 'Top Plus Menu', feature: true, js: true do
 
   context 'used by guest user' do
     before do
-      login_as(guest_user)
+      gitlab_sign_in(guest_user)
     end
 
     scenario 'click on New issue shows new issue page' do
diff --git a/spec/features/gitlab_flavored_markdown_spec.rb b/spec/features/gitlab_flavored_markdown_spec.rb
index 550924123400eb3b854f53077e37727c0595c75a..2d13af2a52aa8793db4e76dc29637e050177903b 100644
--- a/spec/features/gitlab_flavored_markdown_spec.rb
+++ b/spec/features/gitlab_flavored_markdown_spec.rb
@@ -1,6 +1,7 @@
 require 'spec_helper'
 
 describe "GitLab Flavored Markdown", feature: true do
+  let(:user) { create(:user) }
   let(:project) { create(:empty_project) }
   let(:issue) { create(:issue, project: project) }
   let(:fred) do
@@ -10,8 +11,8 @@ describe "GitLab Flavored Markdown", feature: true do
   end
 
   before do
-    login_as(:user)
-    project.add_developer(@user)
+    sign_in(user)
+    project.add_developer(user)
   end
 
   describe "for commits" do
@@ -19,8 +20,8 @@ describe "GitLab Flavored Markdown", feature: true do
     let(:commit) { project.commit }
 
     before do
-      allow_any_instance_of(Commit).to receive(:title).
-        and_return("fix #{issue.to_reference}\n\nask #{fred.to_reference} for details")
+      allow_any_instance_of(Commit).to receive(:title)
+        .and_return("fix #{issue.to_reference}\n\nask #{fred.to_reference} for details")
     end
 
     it "renders title in commits#index" do
@@ -51,12 +52,12 @@ describe "GitLab Flavored Markdown", feature: true do
   describe "for issues", feature: true, js: true do
     before do
       @other_issue = create(:issue,
-                            author: @user,
-                            assignees: [@user],
+                            author: user,
+                            assignees: [user],
                             project: project)
       @issue = create(:issue,
-                      author: @user,
-                      assignees: [@user],
+                      author: user,
+                      assignees: [user],
                       project: project,
                       title: "fix #{@other_issue.to_reference}",
                       description: "ask #{fred.to_reference} for details")
diff --git a/spec/features/global_search_spec.rb b/spec/features/global_search_spec.rb
index 4b22b07494d11101811915ea8f451a821cc2253b..54ebfe6cf77acab933a9367fa1d52281c5133299 100644
--- a/spec/features/global_search_spec.rb
+++ b/spec/features/global_search_spec.rb
@@ -6,7 +6,7 @@ feature 'Global search', feature: true do
 
   before do
     project.team << [user, :master]
-    login_with(user)
+    gitlab_sign_in(user)
   end
 
   describe 'I search through the issues and I see pagination' do
diff --git a/spec/features/groups/activity_spec.rb b/spec/features/groups/activity_spec.rb
index 81f9c103e954beac541c7d91e3580684c2862bf9..9f66a3d8c72337943516cc68d28bece76959905a 100644
--- a/spec/features/groups/activity_spec.rb
+++ b/spec/features/groups/activity_spec.rb
@@ -7,7 +7,7 @@ feature 'Group activity page', feature: true do
   context 'when signed in' do
     before do
       user = create(:group_member, :developer, user: create(:user), group: group ).user
-      login_as(user)
+      gitlab_sign_in(user)
       visit path
     end
 
diff --git a/spec/features/groups/empty_states_spec.rb b/spec/features/groups/empty_states_spec.rb
index fef8e41bffe1692dbae4ce6e73670016941c2493..b1c7151dfa86c13e8d5bc9460cf90005ba0d0f29 100644
--- a/spec/features/groups/empty_states_spec.rb
+++ b/spec/features/groups/empty_states_spec.rb
@@ -5,7 +5,7 @@ feature 'Groups Merge Requests Empty States' do
   let(:user) { create(:group_member, :developer, user: create(:user), group: group ).user }
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   context 'group has a project' do
diff --git a/spec/features/groups/group_name_toggle_spec.rb b/spec/features/groups/group_name_toggle_spec.rb
index dfc3c84f29af23c0d3489f4e25deed2069a99d49..f450626c37076843ea527a4d05df2eb4591b2e6d 100644
--- a/spec/features/groups/group_name_toggle_spec.rb
+++ b/spec/features/groups/group_name_toggle_spec.rb
@@ -9,7 +9,7 @@ feature 'Group name toggle', feature: true, js: true do
   SMALL_SCREEN = 300
 
   before do
-    login_as :user
+    gitlab_sign_in :user
   end
 
   it 'is not present if enough horizontal space' do
diff --git a/spec/features/groups/group_settings_spec.rb b/spec/features/groups/group_settings_spec.rb
index 6afde1d0bed31b0f84c9e2e8a173100680f885bd..5ad777248ecba7c31e3c66f4e2d9f4f85405b63b 100644
--- a/spec/features/groups/group_settings_spec.rb
+++ b/spec/features/groups/group_settings_spec.rb
@@ -6,7 +6,7 @@ feature 'Edit group settings', feature: true do
 
   background do
     group.add_owner(user)
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   describe 'when the group path is changed' do
diff --git a/spec/features/groups/labels/edit_spec.rb b/spec/features/groups/labels/edit_spec.rb
index 69281cecb7b4251969529fb5ff95ddae653512c6..b33040ef843a662d590f946a7f17f79f4b2fe289 100644
--- a/spec/features/groups/labels/edit_spec.rb
+++ b/spec/features/groups/labels/edit_spec.rb
@@ -7,7 +7,7 @@ feature 'Edit group label', feature: true do
 
   background do
     group.add_owner(user)
-    login_as(user)
+    gitlab_sign_in(user)
     visit edit_group_label_path(group, label)
   end
 
diff --git a/spec/features/groups/members/last_owner_cannot_leave_group_spec.rb b/spec/features/groups/members/last_owner_cannot_leave_group_spec.rb
index be60b0489c7a9643fdec5336ee6a7a504e1f97da..5af94e4069b0b4f4a2cbb6e9576343a16282ac8b 100644
--- a/spec/features/groups/members/last_owner_cannot_leave_group_spec.rb
+++ b/spec/features/groups/members/last_owner_cannot_leave_group_spec.rb
@@ -6,7 +6,7 @@ feature 'Groups > Members > Last owner cannot leave group', feature: true do
 
   background do
     group.add_owner(owner)
-    login_as(owner)
+    gitlab_sign_in(owner)
     visit group_path(group)
   end
 
diff --git a/spec/features/groups/members/list_spec.rb b/spec/features/groups/members/list_spec.rb
index f654fa16a0670bc861c913eed996379146609c54..5d00ed30c83d90c0fa1be280f0f05afb50d3f58f 100644
--- a/spec/features/groups/members/list_spec.rb
+++ b/spec/features/groups/members/list_spec.rb
@@ -9,7 +9,7 @@ feature 'Groups members list', feature: true do
   let(:nested_group) { create(:group, parent: group) }
 
   background do
-    login_as(user1)
+    gitlab_sign_in(user1)
   end
 
   scenario 'show members from current group and parent', :nested_groups do
diff --git a/spec/features/groups/members/member_cannot_request_access_to_his_project_spec.rb b/spec/features/groups/members/member_cannot_request_access_to_his_project_spec.rb
index 37c433cc09a886abb55a695c667edb582fef3998..135bb3572bc4bd4648f055bdebcc153db7050f48 100644
--- a/spec/features/groups/members/member_cannot_request_access_to_his_project_spec.rb
+++ b/spec/features/groups/members/member_cannot_request_access_to_his_project_spec.rb
@@ -6,7 +6,7 @@ feature 'Groups > Members > Member cannot request access to his project', featur
 
   background do
     group.add_developer(member)
-    login_as(member)
+    gitlab_sign_in(member)
     visit group_path(group)
   end
 
diff --git a/spec/features/groups/members/member_leaves_group_spec.rb b/spec/features/groups/members/member_leaves_group_spec.rb
index ac4d94658ae85d480e1b6d1f289c360ecd6093ed..40f3b166e74dcef23015ecab0b6879ad22802c07 100644
--- a/spec/features/groups/members/member_leaves_group_spec.rb
+++ b/spec/features/groups/members/member_leaves_group_spec.rb
@@ -8,7 +8,7 @@ feature 'Groups > Members > Member leaves group', feature: true do
   background do
     group.add_owner(owner)
     group.add_developer(user)
-    login_as(user)
+    gitlab_sign_in(user)
     visit group_path(group)
   end
 
diff --git a/spec/features/groups/members/owner_manages_access_requests_spec.rb b/spec/features/groups/members/owner_manages_access_requests_spec.rb
index dbe150823ba1e1013696359056ce90c6f40bf2d0..4e4cf12e8afc5baab57b4de02282e62a59ee2dbd 100644
--- a/spec/features/groups/members/owner_manages_access_requests_spec.rb
+++ b/spec/features/groups/members/owner_manages_access_requests_spec.rb
@@ -8,7 +8,7 @@ feature 'Groups > Members > Owner manages access requests', feature: true do
   background do
     group.request_access(user)
     group.add_owner(owner)
-    login_as(owner)
+    gitlab_sign_in(owner)
   end
 
   scenario 'owner can see access requests' do
diff --git a/spec/features/groups/members/sorting_spec.rb b/spec/features/groups/members/sorting_spec.rb
index 902d3f789ff1428e8d66341d4c4dd9df79b23425..719fa0b40b89e978b2caaed525da270ba7c1370b 100644
--- a/spec/features/groups/members/sorting_spec.rb
+++ b/spec/features/groups/members/sorting_spec.rb
@@ -9,7 +9,7 @@ feature 'Groups > Members > Sorting', feature: true do
     create(:group_member, :owner, user: owner, group: group, created_at: 5.days.ago)
     create(:group_member, :developer, user: developer, group: group, created_at: 3.days.ago)
 
-    login_as(owner)
+    gitlab_sign_in(owner)
   end
 
   scenario 'sorts alphabetically by default' do
diff --git a/spec/features/groups/members/user_requests_access_spec.rb b/spec/features/groups/members/user_requests_access_spec.rb
index e4b5ea91bd3ab27faa15c159b65c16ee82e1aacb..3813308c237d40307822c118be05f27e1c549d66 100644
--- a/spec/features/groups/members/user_requests_access_spec.rb
+++ b/spec/features/groups/members/user_requests_access_spec.rb
@@ -8,7 +8,7 @@ feature 'Groups > Members > User requests access', feature: true do
 
   background do
     group.add_owner(owner)
-    login_as(user)
+    gitlab_sign_in(user)
     visit group_path(group)
   end
 
diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb
index daa2c6afd63410ee58e076913412fc2ef5ce4dfc..330310eae6b6a5601a365ed71ba0e6f529d59140 100644
--- a/spec/features/groups/milestone_spec.rb
+++ b/spec/features/groups/milestone_spec.rb
@@ -8,7 +8,7 @@ feature 'Group milestones', :feature, :js do
   before do
     Timecop.freeze
 
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   after do
diff --git a/spec/features/groups/show_spec.rb b/spec/features/groups/show_spec.rb
index d3c49c37374802a60a69cfedf8163ca621bde2cb..76575f61528863504179f3bfafc52cb96903d7d7 100644
--- a/spec/features/groups/show_spec.rb
+++ b/spec/features/groups/show_spec.rb
@@ -7,7 +7,7 @@ feature 'Group show page', feature: true do
   context 'when signed in' do
     before do
       user = create(:group_member, :developer, user: create(:user), group: group ).user
-      login_as(user)
+      gitlab_sign_in(user)
       visit path
     end
 
diff --git a/spec/features/groups_spec.rb b/spec/features/groups_spec.rb
index 5737ca39b4ef53a813263b93706f760676dd7031..ecacca00a61a3fc4a00f3aa75a995061759406af 100644
--- a/spec/features/groups_spec.rb
+++ b/spec/features/groups_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
 
 feature 'Group', feature: true do
   before do
-    login_as(:admin)
+    gitlab_sign_in(:admin)
   end
 
   matcher :have_namespace_error_message do
@@ -108,8 +108,8 @@ feature 'Group', feature: true do
 
       before do
         group.add_owner(user)
-        logout
-        login_as(user)
+        gitlab_sign_out
+        gitlab_sign_in(user)
 
         visit subgroups_group_path(group)
         click_link 'New Subgroup'
@@ -128,8 +128,8 @@ feature 'Group', feature: true do
   it 'checks permissions to avoid exposing groups by parent_id' do
     group = create(:group, :private, path: 'secret-group')
 
-    logout
-    login_as(:user)
+    gitlab_sign_out
+    gitlab_sign_in(:user)
     visit new_group_path(parent_id: group.id)
 
     expect(page).not_to have_content('secret-group')
diff --git a/spec/features/help_pages_spec.rb b/spec/features/help_pages_spec.rb
index 18102146b5f6155b4838ae2fd4689a5e3a9c6c70..b01ee1cf491979fbd71f14572f3da13b0854fc71 100644
--- a/spec/features/help_pages_spec.rb
+++ b/spec/features/help_pages_spec.rb
@@ -40,7 +40,7 @@ describe 'Help Pages', feature: true do
       allow_any_instance_of(ApplicationSetting).to receive(:version_check_enabled) { true }
       allow_any_instance_of(VersionCheck).to receive(:url) { '/version-check-url' }
 
-      login_as :user
+      gitlab_sign_in :user
       visit help_path
     end
 
@@ -60,7 +60,7 @@ describe 'Help Pages', feature: true do
       allow_any_instance_of(ApplicationSetting).to receive(:help_page_text) { "My Custom Text" }
       allow_any_instance_of(ApplicationSetting).to receive(:help_page_support_url) { "http://example.com/help" }
 
-      login_as :user
+      gitlab_sign_in(:user)
       visit help_path
     end
 
diff --git a/spec/features/issuables/issuable_list_spec.rb b/spec/features/issuables/issuable_list_spec.rb
index 414838fa22e3c7de30cb434a4187f9b1bb244423..f3a5a8463d1be2a85332ab768db0f55f7e02bff6 100644
--- a/spec/features/issuables/issuable_list_spec.rb
+++ b/spec/features/issuables/issuable_list_spec.rb
@@ -8,7 +8,7 @@ describe 'issuable list', feature: true do
 
   before do
     project.add_user(user, :developer)
-    login_as(user)
+    gitlab_sign_in(user)
     issuable_types.each { |type| create_issuables(type) }
   end
 
diff --git a/spec/features/issues/award_emoji_spec.rb b/spec/features/issues/award_emoji_spec.rb
index 81ae54c7a108caf7e548488a7af21b170ca4ae3c..6698e2c79a147a468657f34666dcd07978abaffd 100644
--- a/spec/features/issues/award_emoji_spec.rb
+++ b/spec/features/issues/award_emoji_spec.rb
@@ -12,7 +12,7 @@ describe 'Awards Emoji', feature: true do
   context 'authorized user' do
     before do
       project.team << [user, :master]
-      login_as(user)
+      gitlab_sign_in(user)
     end
 
     describe 'visiting an issue with a legacy award emoji that is not valid anymore' do
@@ -81,13 +81,13 @@ describe 'Awards Emoji', feature: true do
         end
       end
 
-      context 'execute /award slash command' do
+      context 'execute /award quick action' do
         it 'toggles the emoji award on noteable', js: true do
-          execute_slash_command('/award :100:')
+          execute_quick_action('/award :100:')
 
           expect(find(noteable_award_counter)).to have_text("1")
 
-          execute_slash_command('/award :100:')
+          execute_quick_action('/award :100:')
 
           expect(page).not_to have_selector(noteable_award_counter)
         end
@@ -105,7 +105,7 @@ describe 'Awards Emoji', feature: true do
     end
   end
 
-  def execute_slash_command(cmd)
+  def execute_quick_action(cmd)
     within('.js-main-target-form') do
       fill_in 'note[note]', with: cmd
       click_button 'Comment'
diff --git a/spec/features/issues/award_spec.rb b/spec/features/issues/award_spec.rb
index fcf22dd50330a86595fcb3132e24f7497f5f0737..a1c97caea2055c73ecfd87716e1562556dd61521 100644
--- a/spec/features/issues/award_spec.rb
+++ b/spec/features/issues/award_spec.rb
@@ -7,7 +7,7 @@ feature 'Issue awards', js: true, feature: true do
 
   describe 'logged in' do
     before do
-      login_as(user)
+      gitlab_sign_in(user)
       visit namespace_project_issue_path(project.namespace, project, issue)
       wait_for_requests
     end
diff --git a/spec/features/issues/bulk_assignment_labels_spec.rb b/spec/features/issues/bulk_assignment_labels_spec.rb
index 95b4930cd321d7b2a7c1b2a500b92610b4296fde..a99c19cb78701717df1b58592020b503d9613b38 100644
--- a/spec/features/issues/bulk_assignment_labels_spec.rb
+++ b/spec/features/issues/bulk_assignment_labels_spec.rb
@@ -13,7 +13,22 @@ feature 'Issues > Labels bulk assignment', feature: true do
     before do
       project.team << [user, :master]
 
-      login_as user
+      gitlab_sign_in user
+    end
+
+    context 'sidebar' do
+      before do
+        enable_bulk_update
+      end
+
+      it 'is present when bulk edit is enabled' do
+        expect(page).to have_css('.issuable-sidebar')
+      end
+
+      it 'is not present when bulk edit is disabled' do
+        disable_bulk_update
+        expect(page).not_to have_css('.issuable-sidebar')
+      end
     end
 
     context 'can bulk assign' do
@@ -331,7 +346,7 @@ feature 'Issues > Labels bulk assignment', feature: true do
 
   context 'as a guest' do
     before do
-      login_as user
+      gitlab_sign_in user
 
       visit namespace_project_issues_path(project.namespace, project)
     end
@@ -398,4 +413,8 @@ feature 'Issues > Labels bulk assignment', feature: true do
     visit namespace_project_issues_path(project.namespace, project)
     click_button 'Edit Issues'
   end
+
+  def disable_bulk_update
+    click_button 'Cancel'
+  end
 end
diff --git a/spec/features/issues/create_branch_merge_request_spec.rb b/spec/features/issues/create_branch_merge_request_spec.rb
index 1d7d8d291b29264025dc61a53af0003ada2db421..aa538803dd880fd1277c7ab249535c7c8897ca9f 100644
--- a/spec/features/issues/create_branch_merge_request_spec.rb
+++ b/spec/features/issues/create_branch_merge_request_spec.rb
@@ -8,7 +8,7 @@ feature 'Create Branch/Merge Request Dropdown on issue page', feature: true, js:
   context 'for team members' do
     before do
       project.team << [user, :developer]
-      login_as(user)
+      gitlab_sign_in(user)
     end
 
     it 'allows creating a merge request from the issue page' do
diff --git a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb
index 24e2419b5ce1fe1ae87e2a28c507328082ed8b7d..5f631043e15e7b60f680aa406407aa16c895d247 100644
--- a/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb
+++ b/spec/features/issues/create_issue_for_discussions_in_merge_request_spec.rb
@@ -9,7 +9,7 @@ feature 'Resolving all open discussions in a merge request from an issue', featu
   describe 'as a user with access to the project' do
     before do
       project.team << [user, :master]
-      login_as user
+      gitlab_sign_in user
       visit namespace_project_merge_request_path(project.namespace, project, merge_request)
     end
 
@@ -82,7 +82,7 @@ feature 'Resolving all open discussions in a merge request from an issue', featu
   describe 'as a reporter' do
     before do
       project.team << [user, :reporter]
-      login_as user
+      gitlab_sign_in user
       visit new_namespace_project_issue_path(project.namespace, project, merge_request_to_resolve_discussions_of: merge_request.iid)
     end
 
diff --git a/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb b/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb
index 3a5a79e03f4082779ff5ebcbab9d609a41980dd9..9e9e214060f04501b828a76cf127fffe698ecc82 100644
--- a/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb
+++ b/spec/features/issues/create_issue_for_single_discussion_in_merge_request_spec.rb
@@ -9,7 +9,7 @@ feature 'Resolve an open discussion in a merge request by creating an issue', fe
   describe 'As a user with access to the project' do
     before do
       project.team << [user, :master]
-      login_as user
+      gitlab_sign_in user
       visit namespace_project_merge_request_path(project.namespace, project, merge_request)
     end
 
@@ -66,7 +66,7 @@ feature 'Resolve an open discussion in a merge request by creating an issue', fe
   describe 'as a reporter' do
     before do
       project.team << [user, :reporter]
-      login_as user
+      gitlab_sign_in user
       visit new_namespace_project_issue_path(project.namespace, project,
                                              merge_request_to_resolve_discussions_of: merge_request.iid,
                                              discussion_to_resolve: discussion.id)
diff --git a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
index 44353d880c2e7ac61304935da86c01d95880b2c6..96f6739af2d5aff80967ee8968db73f125ec2e6a 100644
--- a/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_assignee_spec.rb
@@ -23,7 +23,7 @@ describe 'Dropdown assignee', :feature, :js do
     project.team << [user, :master]
     project.team << [user_john, :master]
     project.team << [user_jacob, :master]
-    login_as(user)
+    gitlab_sign_in(user)
     create(:issue, project: project)
 
     visit namespace_project_issues_path(project.namespace, project)
diff --git a/spec/features/issues/filtered_search/dropdown_author_spec.rb b/spec/features/issues/filtered_search/dropdown_author_spec.rb
index 6b707c4be4ae029711c2a648afccaf2b04de4a36..5ee824c662aabd46ad8f35c68e29be41328adf03 100644
--- a/spec/features/issues/filtered_search/dropdown_author_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_author_spec.rb
@@ -31,7 +31,7 @@ describe 'Dropdown author', js: true, feature: true do
     project.team << [user, :master]
     project.team << [user_john, :master]
     project.team << [user_jacob, :master]
-    login_as(user)
+    gitlab_sign_in(user)
     create(:issue, project: project)
 
     visit namespace_project_issues_path(project.namespace, project)
diff --git a/spec/features/issues/filtered_search/dropdown_hint_spec.rb b/spec/features/issues/filtered_search/dropdown_hint_spec.rb
index b9a37cfcc2244bd33aa82894feeab01e71401a71..a05e4394ffdfcc767115e784d6d9bab324724d6d 100644
--- a/spec/features/issues/filtered_search/dropdown_hint_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_hint_spec.rb
@@ -14,7 +14,7 @@ describe 'Dropdown hint', :js, :feature do
 
   before do
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
     create(:issue, project: project)
 
     visit namespace_project_issues_path(project.namespace, project)
diff --git a/spec/features/issues/filtered_search/dropdown_label_spec.rb b/spec/features/issues/filtered_search/dropdown_label_spec.rb
index abe5d61e38cad56c328c99de7e527926812bc939..aec9d7ceb5def6350ec032ac25111a30adc68bfb 100644
--- a/spec/features/issues/filtered_search/dropdown_label_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_label_spec.rb
@@ -34,7 +34,7 @@ describe 'Dropdown label', js: true, feature: true do
 
   before do
     project.add_master(user)
-    login_as(user)
+    gitlab_sign_in(user)
     create(:issue, project: project)
 
     visit namespace_project_issues_path(project.namespace, project)
diff --git a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
index 448259057b012e1f011476412f01a814cd7ee72f..b21f41946b72646615706f1b61c4feaa2fd50b5c 100644
--- a/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_milestone_spec.rb
@@ -30,7 +30,7 @@ describe 'Dropdown milestone', :feature, :js do
 
   before do
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
     create(:issue, project: project)
 
     visit namespace_project_issues_path(project.namespace, project)
diff --git a/spec/features/issues/filtered_search/search_bar_spec.rb b/spec/features/issues/filtered_search/search_bar_spec.rb
index 3ea95aed0a64bb0e7a59ff5559b684fe68de54cb..806c732b93545f42a053e9bd84e62067ba7645f2 100644
--- a/spec/features/issues/filtered_search/search_bar_spec.rb
+++ b/spec/features/issues/filtered_search/search_bar_spec.rb
@@ -9,7 +9,7 @@ describe 'Search bar', js: true, feature: true do
 
   before do
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
     create(:issue, project: project)
 
     visit namespace_project_issues_path(project.namespace, project)
diff --git a/spec/features/issues/filtered_search/visual_tokens_spec.rb b/spec/features/issues/filtered_search/visual_tokens_spec.rb
index ff32b0c7d11da560de72bfd37baaab27bcaf2d2b..22488f3481382c21efc03894ee42e25d6dfc1c2f 100644
--- a/spec/features/issues/filtered_search/visual_tokens_spec.rb
+++ b/spec/features/issues/filtered_search/visual_tokens_spec.rb
@@ -25,7 +25,7 @@ describe 'Visual tokens', js: true, feature: true do
   before do
     project.add_user(user, :master)
     project.add_user(user_rock, :master)
-    login_as(user)
+    gitlab_sign_in(user)
     create(:issue, project: project)
 
     visit namespace_project_issues_path(project.namespace, project)
diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb
index 96d37e33f3d268519f166910ac3108c14c0ee06f..b369ef1ff79f708a9746940bd6b1aae6d4183c1f 100644
--- a/spec/features/issues/form_spec.rb
+++ b/spec/features/issues/form_spec.rb
@@ -16,7 +16,7 @@ describe 'New/edit issue', :feature, :js do
   before do
     project.team << [user, :master]
     project.team << [user2, :master]
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   context 'new issue' do
@@ -210,6 +210,13 @@ describe 'New/edit issue', :feature, :js do
 
       expect(find('.js-assignee-search')).to have_content(user2.name)
     end
+
+    it 'description has autocomplete' do
+      find('#issue_description').native.send_keys('')
+      fill_in 'issue_description', with: '@'
+
+      expect(page).to have_selector('.atwho-view')
+    end
   end
 
   context 'edit issue' do
@@ -258,6 +265,13 @@ describe 'New/edit issue', :feature, :js do
         end
       end
     end
+
+    it 'description has autocomplete' do
+      find('#issue_description').native.send_keys('')
+      fill_in 'issue_description', with: '@'
+
+      expect(page).to have_selector('.atwho-view')
+    end
   end
 
   describe 'sub-group project' do
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index 350473437a811bb26e2929db5ee4e2da8c8f0c81..e61eb5233d0c5b35d688155a0efb0b1a7a64728e 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -8,7 +8,7 @@ feature 'GFM autocomplete', feature: true, js: true do
 
   before do
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
     visit namespace_project_issue_path(project.namespace, project, issue)
 
     wait_for_requests
@@ -208,7 +208,7 @@ feature 'GFM autocomplete', feature: true, js: true do
       expect(page).not_to have_selector('.atwho-view')
     end
 
-    it 'triggers autocomplete after selecting a slash command' do
+    it 'triggers autocomplete after selecting a quick action' do
       note = find('#note_note')
       page.within '.timeline-content-form' do
         note.native.send_keys('')
diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb
index 96c24750250a7036a2709e8af80eef20c6ece7fe..163bc4bb32f0f9963ce2f0c6f2a02d9bb8857513 100644
--- a/spec/features/issues/issue_sidebar_spec.rb
+++ b/spec/features/issues/issue_sidebar_spec.rb
@@ -10,7 +10,7 @@ feature 'Issue Sidebar', feature: true do
   let!(:label) { create(:label, project: project, title: 'bug') }
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   context 'assignee', js: true do
diff --git a/spec/features/issues/markdown_toolbar_spec.rb b/spec/features/issues/markdown_toolbar_spec.rb
index c8c9c50396bee009cac0cff4f60023488a7692bb..66d823ec9d04b89d5ff0834daf7d2614567754bf 100644
--- a/spec/features/issues/markdown_toolbar_spec.rb
+++ b/spec/features/issues/markdown_toolbar_spec.rb
@@ -6,7 +6,7 @@ feature 'Issue markdown toolbar', feature: true, js: true do
   let(:user)   { create(:user) }
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
 
     visit namespace_project_issue_path(project.namespace, project, issue)
   end
diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb
index e75bf0592189980b5a90ea1f05b0817ba4a69569..21a7637fe7f94ba9cf95b800aa24c3f32e0e1063 100644
--- a/spec/features/issues/move_spec.rb
+++ b/spec/features/issues/move_spec.rb
@@ -9,7 +9,7 @@ feature 'issue move to another project' do
     create(:issue, description: text, project: old_project, author: user)
   end
 
-  background { login_as(user) }
+  background { gitlab_sign_in(user) }
 
   context 'user does not have permission to move issue' do
     background do
diff --git a/spec/features/issues/note_polling_spec.rb b/spec/features/issues/note_polling_spec.rb
index 2c0a6ffd3cbfdf91c7f5c2b93934f5bc759ddabe..bd31e44ef337a4dfa6376f9ec641e6ce53255514 100644
--- a/spec/features/issues/note_polling_spec.rb
+++ b/spec/features/issues/note_polling_spec.rb
@@ -27,7 +27,7 @@ feature 'Issue notes polling', :feature, :js do
       let!(:existing_note) { create(:note, noteable: issue, project: project, author: user, note: note_text) }
 
       before do
-        login_as(user)
+        gitlab_sign_in(user)
         visit namespace_project_issue_path(project.namespace, project, issue)
       end
 
@@ -93,7 +93,7 @@ feature 'Issue notes polling', :feature, :js do
       let!(:existing_note) { create(:note, noteable: issue, project: project, author: user1, note: note_text) }
 
       before do
-        login_as(user2)
+        gitlab_sign_in(user2)
         visit namespace_project_issue_path(project.namespace, project, issue)
       end
 
@@ -114,7 +114,7 @@ feature 'Issue notes polling', :feature, :js do
       let!(:system_note) { create(:system_note, noteable: issue, project: project, author: user, note: note_text) }
 
       before do
-        login_as(user)
+        gitlab_sign_in(user)
         visit namespace_project_issue_path(project.namespace, project, issue)
       end
 
diff --git a/spec/features/issues/notes_on_issues_spec.rb b/spec/features/issues/notes_on_issues_spec.rb
index 15c817cabac901ad6c1802bf1169ee27166954d2..f648295416f769e3d73f53a854436c317be2c06d 100644
--- a/spec/features/issues/notes_on_issues_spec.rb
+++ b/spec/features/issues/notes_on_issues_spec.rb
@@ -9,7 +9,7 @@ describe 'Create notes on issues', :js, :feature do
 
     before do
       project.team << [user, :developer]
-      login_as(user)
+      gitlab_sign_in(user)
       visit namespace_project_issue_path(project.namespace, project, issue)
 
       fill_in 'note[note]', with: note_text
diff --git a/spec/features/issues/spam_issues_spec.rb b/spec/features/issues/spam_issues_spec.rb
index 6001476d0ca676cf489514796f80a0d383ef1155..57c783790b51f1d78b7166849f02031a746e1677 100644
--- a/spec/features/issues/spam_issues_spec.rb
+++ b/spec/features/issues/spam_issues_spec.rb
@@ -18,7 +18,7 @@ describe 'New issue', feature: true, js: true do
     )
 
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   context 'when identified as a spam' do
diff --git a/spec/features/issues/todo_spec.rb b/spec/features/issues/todo_spec.rb
index 3fde85b0a5c5178887677d988ac67167d183352d..a1c00dd64f6c7cf6d7ef0afddd2cb03e6e0e5f04 100644
--- a/spec/features/issues/todo_spec.rb
+++ b/spec/features/issues/todo_spec.rb
@@ -7,7 +7,7 @@ feature 'Manually create a todo item from issue', feature: true, js: true do
 
   before do
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
     visit namespace_project_issue_path(project.namespace, project, issue)
   end
 
diff --git a/spec/features/issues/update_issues_spec.rb b/spec/features/issues/update_issues_spec.rb
index 8595847d31356407f9bd536c44e7cf54f6aa347d..dc981406e4ebcdb91efc7f910da178f5b54e1671 100644
--- a/spec/features/issues/update_issues_spec.rb
+++ b/spec/features/issues/update_issues_spec.rb
@@ -7,7 +7,7 @@ feature 'Multiple issue updating from issues#index', feature: true do
 
   before do
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   context 'status', js: true do
diff --git a/spec/features/issues/user_uses_slash_commands_spec.rb b/spec/features/issues/user_uses_slash_commands_spec.rb
index d14c319707c76685497da40abad464ef803bfee5..168cdd0813754ee9d26f26a48c3ed13f450b93bc 100644
--- a/spec/features/issues/user_uses_slash_commands_spec.rb
+++ b/spec/features/issues/user_uses_slash_commands_spec.rb
@@ -1,9 +1,9 @@
 require 'rails_helper'
 
-feature 'Issues > User uses slash commands', feature: true, js: true do
-  include SlashCommandsHelpers
+feature 'Issues > User uses quick actions', feature: true, js: true do
+  include QuickActionsHelpers
 
-  it_behaves_like 'issuable record that supports slash commands in its description and notes', :issue do
+  it_behaves_like 'issuable record that supports quick actions in its description and notes', :issue do
     let(:issuable) { create(:issue, project: project) }
   end
 
@@ -13,7 +13,7 @@ feature 'Issues > User uses slash commands', feature: true, js: true do
 
     before do
       project.team << [user, :master]
-      login_with(user)
+      gitlab_sign_in(user)
       visit namespace_project_issue_path(project.namespace, project, issue)
     end
 
@@ -41,8 +41,8 @@ feature 'Issues > User uses slash commands', feature: true, js: true do
         let(:guest) { create(:user) }
         before do
           project.team << [guest, :guest]
-          logout
-          login_with(guest)
+          gitlab_sign_out
+          gitlab_sign_in(guest)
           visit namespace_project_issue_path(project.namespace, project, issue)
         end
 
@@ -81,8 +81,8 @@ feature 'Issues > User uses slash commands', feature: true, js: true do
         let(:guest) { create(:user) }
         before do
           project.team << [guest, :guest]
-          logout
-          login_with(guest)
+          gitlab_sign_out
+          gitlab_sign_in(guest)
           visit namespace_project_issue_path(project.namespace, project, issue)
         end
 
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index 2cff53539f39b11af7d2850ff971dcb9aae91a5e..f47b89fd718e861f4f6f9fb34179773b82b6be9c 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -5,20 +5,21 @@ describe 'Issues', feature: true do
   include IssueHelpers
   include SortingHelper
 
+  let(:user) { create(:user) }
   let(:project) { create(:empty_project, :public) }
 
   before do
-    login_as :user
+    sign_in(user)
     user2 = create(:user)
 
-    project.team << [[@user, user2], :developer]
+    project.team << [[user, user2], :developer]
   end
 
   describe 'Edit issue' do
     let!(:issue) do
       create(:issue,
-             author: @user,
-             assignees: [@user],
+             author: user,
+             assignees: [user],
              project: project)
     end
 
@@ -35,15 +36,15 @@ describe 'Issues', feature: true do
   describe 'Editing issue assignee' do
     let!(:issue) do
       create(:issue,
-             author: @user,
-             assignees: [@user],
+             author: user,
+             assignees: [user],
              project: project)
     end
 
     it 'allows user to select unassigned', js: true do
       visit edit_namespace_project_issue_path(project.namespace, project, issue)
 
-      expect(page).to have_content "Assignee #{@user.name}"
+      expect(page).to have_content "Assignee #{user.name}"
 
       first('.js-user-search').click
       click_link 'Unassigned'
@@ -86,7 +87,7 @@ describe 'Issues', feature: true do
     end
 
     context 'on edit form' do
-      let(:issue) { create(:issue, author: @user, project: project, due_date: Date.today.at_beginning_of_month.to_s) }
+      let(:issue) { create(:issue, author: user, project: project, due_date: Date.today.at_beginning_of_month.to_s) }
 
       before do
         visit edit_namespace_project_issue_path(project.namespace, project, issue)
@@ -131,10 +132,10 @@ describe 'Issues', feature: true do
 
   describe 'Issue info' do
     it 'excludes award_emoji from comment count' do
-      issue = create(:issue, author: @user, assignees: [@user], project: project, title: 'foobar')
+      issue = create(:issue, author: user, assignees: [user], project: project, title: 'foobar')
       create(:award_emoji, awardable: issue)
 
-      visit namespace_project_issues_path(project.namespace, project, assignee_id: @user.id)
+      visit namespace_project_issues_path(project.namespace, project, assignee_id: user.id)
 
       expect(page).to have_content 'foobar'
       expect(page.all('.no-comments').first.text).to eq "0"
@@ -145,8 +146,8 @@ describe 'Issues', feature: true do
     before do
       %w(foobar barbaz gitlab).each do |title|
         create(:issue,
-               author: @user,
-               assignees: [@user],
+               author: user,
+               assignees: [user],
                project: project,
                title: title)
       end
@@ -168,7 +169,7 @@ describe 'Issues', feature: true do
     end
 
     it 'allows filtering by a specified assignee' do
-      visit namespace_project_issues_path(project.namespace, project, assignee_id: @user.id)
+      visit namespace_project_issues_path(project.namespace, project, assignee_id: user.id)
 
       expect(page).not_to have_content 'foobar'
       expect(page).to have_content 'barbaz'
@@ -366,13 +367,13 @@ describe 'Issues', feature: true do
   end
 
   describe 'when I want to reset my incoming email token' do
-    let(:project1) { create(:empty_project, namespace: @user.namespace) }
+    let(:project1) { create(:empty_project, namespace: user.namespace) }
     let!(:issue) { create(:issue, project: project1) }
 
     before do
       stub_incoming_email_setting(enabled: true, address: "p+%{key}@gl.ab")
-      project1.team << [@user, :master]
-      visit namespace_project_issues_path(@user.namespace, project1)
+      project1.team << [user, :master]
+      visit namespace_project_issues_path(user.namespace, project1)
     end
 
     it 'changes incoming email address token', js: true do
@@ -383,7 +384,7 @@ describe 'Issues', feature: true do
       wait_for_requests
 
       expect(page).to have_no_field('issue_email', with: previous_token)
-      new_token = project1.new_issue_address(@user.reload)
+      new_token = project1.new_issue_address(user.reload)
       expect(page).to have_field(
         'issue_email',
         with: new_token
@@ -392,7 +393,7 @@ describe 'Issues', feature: true do
   end
 
   describe 'update labels from issue#show', js: true do
-    let(:issue) { create(:issue, project: project, author: @user, assignees: [@user]) }
+    let(:issue) { create(:issue, project: project, author: user, assignees: [user]) }
     let!(:label) { create(:label, project: project) }
 
     before do
@@ -411,14 +412,14 @@ describe 'Issues', feature: true do
   end
 
   describe 'update assignee from issue#show' do
-    let(:issue) { create(:issue, project: project, author: @user, assignees: [@user]) }
+    let(:issue) { create(:issue, project: project, author: user, assignees: [user]) }
 
     context 'by authorized user' do
       it 'allows user to select unassigned', js: true do
         visit namespace_project_issue_path(project.namespace, project, issue)
 
         page.within('.assignee') do
-          expect(page).to have_content "#{@user.name}"
+          expect(page).to have_content "#{user.name}"
 
           click_link 'Edit'
           click_link 'Unassigned'
@@ -433,7 +434,7 @@ describe 'Issues', feature: true do
       end
 
       it 'allows user to select an assignee', js: true do
-        issue2 = create(:issue, project: project, author: @user)
+        issue2 = create(:issue, project: project, author: user)
         visit namespace_project_issue_path(project.namespace, project, issue2)
 
         page.within('.assignee') do
@@ -445,28 +446,28 @@ describe 'Issues', feature: true do
         end
 
         page.within '.dropdown-menu-user' do
-          click_link @user.name
+          click_link user.name
         end
 
         page.within('.assignee') do
-          expect(page).to have_content @user.name
+          expect(page).to have_content user.name
         end
       end
 
       it 'allows user to unselect themselves', js: true do
-        issue2 = create(:issue, project: project, author: @user)
+        issue2 = create(:issue, project: project, author: user)
         visit namespace_project_issue_path(project.namespace, project, issue2)
 
         page.within '.assignee' do
           click_link 'Edit'
-          click_link @user.name
+          click_link user.name
 
           page.within '.value .author' do
-            expect(page).to have_content @user.name
+            expect(page).to have_content user.name
           end
 
           click_link 'Edit'
-          click_link @user.name
+          click_link user.name
 
           page.within '.value .assign-yourself' do
             expect(page).to have_content "No assignee"
@@ -483,8 +484,8 @@ describe 'Issues', feature: true do
       end
 
       it 'shows assignee text', js: true do
-        logout
-        login_with guest
+        sign_out(:user)
+        sign_in(guest)
 
         visit namespace_project_issue_path(project.namespace, project, issue)
         expect(page).to have_content issue.assignees.first.name
@@ -493,7 +494,7 @@ describe 'Issues', feature: true do
   end
 
   describe 'update milestone from issue#show' do
-    let!(:issue) { create(:issue, project: project, author: @user) }
+    let!(:issue) { create(:issue, project: project, author: user) }
     let!(:milestone) { create(:milestone, project: project) }
 
     context 'by authorized user' do
@@ -546,8 +547,8 @@ describe 'Issues', feature: true do
       end
 
       it 'shows milestone text', js: true do
-        logout
-        login_with guest
+        sign_out(:user)
+        sign_in(guest)
 
         visit namespace_project_issue_path(project.namespace, project, issue)
         expect(page).to have_content milestone.title
@@ -560,7 +561,7 @@ describe 'Issues', feature: true do
 
     context 'by unauthenticated user' do
       before do
-        logout
+        sign_out(:user)
       end
 
       it 'redirects to signin then back to new issue after signin' do
@@ -570,7 +571,9 @@ describe 'Issues', feature: true do
 
         expect(current_path).to eq new_user_session_path
 
-        login_as :user
+        # NOTE: This is specifically testing the redirect after login, so we
+        # need the full login flow
+        gitlab_sign_in(create(:user))
 
         expect(current_path).to eq new_namespace_project_issue_path(project.namespace, project)
       end
@@ -599,7 +602,7 @@ describe 'Issues', feature: true do
 
       before do
         project.repository.create_file(
-          @user,
+          user,
           '.gitlab/issue_templates/bug.md',
           'this is a test "bug" template',
           message: 'added issue template',
@@ -628,7 +631,7 @@ describe 'Issues', feature: true do
 
       it 'click the button to show modal for the new email' do
         page.within '#issue-email-modal' do
-          email = project.new_issue_address(@user)
+          email = project.new_issue_address(user)
 
           expect(page).to have_selector("input[value='#{email}']")
         end
@@ -636,7 +639,7 @@ describe 'Issues', feature: true do
     end
 
     context 'with existing issues' do
-      let!(:issue) { create(:issue, project: project, author: @user) }
+      let!(:issue) { create(:issue, project: project, author: user) }
 
       it_behaves_like 'show the email in the modal'
     end
@@ -648,7 +651,7 @@ describe 'Issues', feature: true do
 
   describe 'due date' do
     context 'update due on issue#show', js: true do
-      let(:issue) { create(:issue, project: project, author: @user, assignees: [@user]) }
+      let(:issue) { create(:issue, project: project, author: user, assignees: [user]) }
 
       before do
         visit namespace_project_issue_path(project.namespace, project, issue)
@@ -693,7 +696,7 @@ describe 'Issues', feature: true do
 
   describe 'title issue#show', js: true do
     it 'updates the title', js: true do
-      issue = create(:issue, author: @user, assignees: [@user], project: project, title: 'new title')
+      issue = create(:issue, author: user, assignees: [user], project: project, title: 'new title')
 
       visit namespace_project_issue_path(project.namespace, project, issue)
 
diff --git a/spec/features/login_spec.rb b/spec/features/login_spec.rb
index 4763f45481064f260917096c743b340491037600..53b8ba5b0f70272f50b50f0fd5e0eab7f0e26403 100644
--- a/spec/features/login_spec.rb
+++ b/spec/features/login_spec.rb
@@ -36,7 +36,7 @@ feature 'Login', feature: true do
     it 'prevents the user from logging in' do
       user = create(:user, :blocked)
 
-      login_with(user)
+      gitlab_sign_in(user)
 
       expect(page).to have_content('Your account has been blocked.')
     end
@@ -44,19 +44,19 @@ feature 'Login', feature: true do
     it 'does not update Devise trackable attributes', :redis do
       user = create(:user, :blocked)
 
-      expect { login_with(user) }.not_to change { user.reload.sign_in_count }
+      expect { gitlab_sign_in(user) }.not_to change { user.reload.sign_in_count }
     end
   end
 
   describe 'with the ghost user' do
     it 'disallows login' do
-      login_with(User.ghost)
+      gitlab_sign_in(User.ghost)
 
       expect(page).to have_content('Invalid Login or password.')
     end
 
     it 'does not update Devise trackable attributes', :redis do
-      expect { login_with(User.ghost) }.not_to change { User.ghost.reload.sign_in_count }
+      expect { gitlab_sign_in(User.ghost) }.not_to change { User.ghost.reload.sign_in_count }
     end
   end
 
@@ -70,7 +70,7 @@ feature 'Login', feature: true do
       let(:user) { create(:user, :two_factor) }
 
       before do
-        login_with(user, remember: true)
+        gitlab_sign_in(user, remember: true)
         expect(page).to have_content('Two-Factor Authentication')
       end
 
@@ -122,8 +122,8 @@ feature 'Login', feature: true do
           end
 
           it 'invalidates the used code' do
-            expect { enter_code(codes.sample) }.
-              to change { user.reload.otp_backup_codes.size }.by(-1)
+            expect { enter_code(codes.sample) }
+              .to change { user.reload.otp_backup_codes.size }.by(-1)
           end
         end
 
@@ -167,7 +167,7 @@ feature 'Login', feature: true do
       it 'shows 2FA prompt after OAuth login' do
         stub_omniauth_config(enabled: true, auto_link_saml_user: true, allow_single_sign_on: ['saml'], providers: [saml_config])
         user = create(:omniauth_user, :two_factor, extern_uid: 'my-uid', provider: 'saml')
-        login_via('saml', user, 'my-uid')
+        gitlab_sign_in_via('saml', user, 'my-uid')
 
         expect(page).to have_content('Two-Factor Authentication')
         enter_code(user.current_otp)
@@ -180,19 +180,19 @@ feature 'Login', feature: true do
     let(:user) { create(:user) }
 
     it 'allows basic login' do
-      login_with(user)
+      gitlab_sign_in(user)
       expect(current_path).to eq root_path
     end
 
     it 'does not show a "You are already signed in." error message' do
-      login_with(user)
+      gitlab_sign_in(user)
       expect(page).not_to have_content('You are already signed in.')
     end
 
     it 'blocks invalid login' do
       user = create(:user, password: 'not-the-default')
 
-      login_with(user)
+      gitlab_sign_in(user)
       expect(page).to have_content('Invalid Login or password.')
     end
   end
@@ -209,7 +209,7 @@ feature 'Login', feature: true do
       context 'with grace period defined' do
         before do
           stub_application_setting(two_factor_grace_period: 48)
-          login_with(user)
+          gitlab_sign_in(user)
         end
 
         context 'within the grace period' do
@@ -246,7 +246,7 @@ feature 'Login', feature: true do
       context 'without grace period defined' do
         before do
           stub_application_setting(two_factor_grace_period: 0)
-          login_with(user)
+          gitlab_sign_in(user)
         end
 
         it 'redirects to two-factor configuration page' do
@@ -269,7 +269,7 @@ feature 'Login', feature: true do
       context 'with grace period defined' do
         before do
           stub_application_setting(two_factor_grace_period: 48)
-          login_with(user)
+          gitlab_sign_in(user)
         end
 
         context 'within the grace period' do
@@ -310,7 +310,7 @@ feature 'Login', feature: true do
       context 'without grace period defined' do
         before do
           stub_application_setting(two_factor_grace_period: 0)
-          login_with(user)
+          gitlab_sign_in(user)
         end
 
         it 'redirects to two-factor configuration page' do
diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb
index ba930de937d272ede68ace0d7acbe4aef782b2ab..534be3ab5a776a7e5e02c6ed388b957c471af90c 100644
--- a/spec/features/markdown_spec.rb
+++ b/spec/features/markdown_spec.rb
@@ -58,8 +58,8 @@ describe 'GitLab Markdown', feature: true do
       end
 
       it 'allows Markdown in tables' do
-        expect(doc.at_css('td:contains("Baz")').children.to_html).
-          to eq '<strong>Baz</strong>'
+        expect(doc.at_css('td:contains("Baz")').children.to_html)
+          .to eq '<strong>Baz</strong>'
       end
 
       it 'parses fenced code blocks' do
@@ -158,14 +158,14 @@ describe 'GitLab Markdown', feature: true do
     describe 'Edge Cases' do
       it 'allows markup inside link elements' do
         aggregate_failures do
-          expect(doc.at_css('a[href="#link-emphasis"]').to_html).
-            to eq %{<a href="#link-emphasis"><em>text</em></a>}
+          expect(doc.at_css('a[href="#link-emphasis"]').to_html)
+            .to eq %{<a href="#link-emphasis"><em>text</em></a>}
 
-          expect(doc.at_css('a[href="#link-strong"]').to_html).
-            to eq %{<a href="#link-strong"><strong>text</strong></a>}
+          expect(doc.at_css('a[href="#link-strong"]').to_html)
+            .to eq %{<a href="#link-strong"><strong>text</strong></a>}
 
-          expect(doc.at_css('a[href="#link-code"]').to_html).
-            to eq %{<a href="#link-code"><code>text</code></a>}
+          expect(doc.at_css('a[href="#link-code"]').to_html)
+            .to eq %{<a href="#link-code"><code>text</code></a>}
         end
       end
     end
diff --git a/spec/features/merge_requests/assign_issues_spec.rb b/spec/features/merge_requests/assign_issues_spec.rb
index b306e2f5f757a6bc1068bf242b41f2a4c0a7b02b..cb835f533e08dbedfd9b592839cabf5407701811 100644
--- a/spec/features/merge_requests/assign_issues_spec.rb
+++ b/spec/features/merge_requests/assign_issues_spec.rb
@@ -13,7 +13,7 @@ feature 'Merge request issue assignment', js: true, feature: true do
   end
 
   def visit_merge_request(current_user = nil)
-    login_as(current_user || user)
+    gitlab_sign_in(current_user || user)
     visit namespace_project_merge_request_path(project.namespace, project, merge_request)
   end
 
diff --git a/spec/features/merge_requests/award_spec.rb b/spec/features/merge_requests/award_spec.rb
index ac260e118d00c9d9e8e0e263c443ad984470ba63..e9dd755b6af6f5602f9c0f8d0be1e4aa8c7e5197 100644
--- a/spec/features/merge_requests/award_spec.rb
+++ b/spec/features/merge_requests/award_spec.rb
@@ -7,7 +7,7 @@ feature 'Merge request awards', js: true, feature: true do
 
   describe 'logged in' do
     before do
-      login_as(user)
+      gitlab_sign_in(user)
       visit namespace_project_merge_request_path(project.namespace, project, merge_request)
     end
 
diff --git a/spec/features/merge_requests/check_if_mergeable_with_unresolved_discussions_spec.rb b/spec/features/merge_requests/check_if_mergeable_with_unresolved_discussions_spec.rb
index fa306c02a43f09dfbf0d29b0dc4c094cb84db317..060cfb8fdd1d06f056319301597d833c8ebdc97c 100644
--- a/spec/features/merge_requests/check_if_mergeable_with_unresolved_discussions_spec.rb
+++ b/spec/features/merge_requests/check_if_mergeable_with_unresolved_discussions_spec.rb
@@ -6,7 +6,7 @@ feature 'Check if mergeable with unresolved discussions', js: true, feature: tru
   let!(:merge_request) { create(:merge_request_with_diff_notes, source_project: project, author: user) }
 
   before do
-    login_as user
+    gitlab_sign_in user
     project.team << [user, :master]
   end
 
diff --git a/spec/features/merge_requests/cherry_pick_spec.rb b/spec/features/merge_requests/cherry_pick_spec.rb
index 6ba681e36f7377efa3bd2e2020b096ca436c0b61..6ba96570e3d4e55e7bd5caf69d18e8a0eda6825b 100644
--- a/spec/features/merge_requests/cherry_pick_spec.rb
+++ b/spec/features/merge_requests/cherry_pick_spec.rb
@@ -7,7 +7,7 @@ describe 'Cherry-pick Merge Requests', js: true do
   let(:merge_request) { create(:merge_request_with_diffs, source_project: project, author: user) }
 
   before do
-    login_as user
+    gitlab_sign_in user
     project.team << [user, :master]
   end
 
diff --git a/spec/features/merge_requests/closes_issues_spec.rb b/spec/features/merge_requests/closes_issues_spec.rb
index e627618042a52600bffd37e83a13850aa4cbc35f..365b2555c35340f036ddddb42ef2f00d8b752a63 100644
--- a/spec/features/merge_requests/closes_issues_spec.rb
+++ b/spec/features/merge_requests/closes_issues_spec.rb
@@ -20,7 +20,7 @@ feature 'Merge Request closing issues message', feature: true, js: true do
   before do
     project.team << [user, :master]
 
-    login_as user
+    gitlab_sign_in user
 
     visit namespace_project_merge_request_path(project.namespace, project, merge_request)
     wait_for_requests
diff --git a/spec/features/merge_requests/conflicts_spec.rb b/spec/features/merge_requests/conflicts_spec.rb
index 9409c32104bfcca9aedb5ea1b0250fc537b5b1f1..9c091befa27eb52a750bc1b9811f19fe24efdb3f 100644
--- a/spec/features/merge_requests/conflicts_spec.rb
+++ b/spec/features/merge_requests/conflicts_spec.rb
@@ -79,7 +79,7 @@ feature 'Merge request conflict resolution', js: true, feature: true do
   context 'can be resolved in the UI' do
     before do
       project.team << [user, :developer]
-      login_as(user)
+      gitlab_sign_in(user)
     end
 
     context 'the conflicts are resolvable' do
@@ -164,7 +164,7 @@ feature 'Merge request conflict resolution', js: true, feature: true do
 
       before do
         project.team << [user, :developer]
-        login_as(user)
+        gitlab_sign_in(user)
 
         visit namespace_project_merge_request_path(project.namespace, project, merge_request)
       end
diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb
index 82987c768d14ec884b62f6e48f815393671a0700..8f7adbccaaa8329af9d473afa5a1008a46ae21f3 100644
--- a/spec/features/merge_requests/create_new_mr_spec.rb
+++ b/spec/features/merge_requests/create_new_mr_spec.rb
@@ -7,7 +7,7 @@ feature 'Create New Merge Request', feature: true, js: true do
   before do
     project.team << [user, :master]
 
-    login_as user
+    gitlab_sign_in user
   end
 
   it 'selects the source branch sha when a tag with the same name exists' do
diff --git a/spec/features/merge_requests/created_from_fork_spec.rb b/spec/features/merge_requests/created_from_fork_spec.rb
index b43277433830ac4beb2b78a472aabf4d081f5f1e..69059dfa5623ee9a72e20f39c061d64f625857c8 100644
--- a/spec/features/merge_requests/created_from_fork_spec.rb
+++ b/spec/features/merge_requests/created_from_fork_spec.rb
@@ -16,7 +16,7 @@ feature 'Merge request created from fork' do
 
   background do
     fork_project.team << [user, :master]
-    login_as user
+    gitlab_sign_in user
   end
 
   scenario 'user can access merge request' do
diff --git a/spec/features/merge_requests/deleted_source_branch_spec.rb b/spec/features/merge_requests/deleted_source_branch_spec.rb
index 1723fb7d3658ed7b833fd711ca9e5f6a3db12afe..f2af31983197e311a635c8a6d35c855a63b8cd72 100644
--- a/spec/features/merge_requests/deleted_source_branch_spec.rb
+++ b/spec/features/merge_requests/deleted_source_branch_spec.rb
@@ -8,7 +8,7 @@ describe 'Deleted source branch', feature: true, js: true do
   let(:merge_request) { create(:merge_request) }
 
   before do
-    login_as user
+    gitlab_sign_in user
     merge_request.project.team << [user, :master]
     merge_request.update!(source_branch: 'this-branch-does-not-exist')
     visit namespace_project_merge_request_path(
diff --git a/spec/features/merge_requests/diff_notes_avatars_spec.rb b/spec/features/merge_requests/diff_notes_avatars_spec.rb
index e23dc2cd94093da1451f9fa59af8cd7da2ce5e91..989dfb71d103b34cb28719804d685f2cb33333e2 100644
--- a/spec/features/merge_requests/diff_notes_avatars_spec.rb
+++ b/spec/features/merge_requests/diff_notes_avatars_spec.rb
@@ -20,7 +20,7 @@ feature 'Diff note avatars', feature: true, js: true do
 
   before do
     project.team << [user, :master]
-    login_as user
+    gitlab_sign_in user
   end
 
   context 'discussion tab' do
diff --git a/spec/features/merge_requests/diff_notes_resolve_spec.rb b/spec/features/merge_requests/diff_notes_resolve_spec.rb
index 4d549f3bdbb3ba33e984cf46121a36efcc365220..0f8ca6f90d1d373f746b0ba81b7e847614143778 100644
--- a/spec/features/merge_requests/diff_notes_resolve_spec.rb
+++ b/spec/features/merge_requests/diff_notes_resolve_spec.rb
@@ -19,7 +19,7 @@ feature 'Diff notes resolve', feature: true, js: true do
   context 'no discussions' do
     before do
       project.team << [user, :master]
-      login_as user
+      gitlab_sign_in user
       note.destroy
       visit_merge_request
     end
@@ -33,7 +33,7 @@ feature 'Diff notes resolve', feature: true, js: true do
   context 'as authorized user' do
     before do
       project.team << [user, :master]
-      login_as user
+      gitlab_sign_in user
       visit_merge_request
     end
 
@@ -402,7 +402,7 @@ feature 'Diff notes resolve', feature: true, js: true do
 
     before do
       project.team << [guest, :guest]
-      login_as guest
+      gitlab_sign_in guest
     end
 
     context 'someone elses merge request' do
diff --git a/spec/features/merge_requests/diffs_spec.rb b/spec/features/merge_requests/diffs_spec.rb
index 44013df3ea043795f276efadf032808297e68ad2..cb6cd6571a8f5ebf27910d014c9a11729d45cfa2 100644
--- a/spec/features/merge_requests/diffs_spec.rb
+++ b/spec/features/merge_requests/diffs_spec.rb
@@ -74,8 +74,7 @@ feature 'Diffs URL', js: true, feature: true do
 
     context 'as author' do
       it 'shows direct edit link' do
-        login_as(author_user)
-
+        gitlab_sign_in(author_user)
         visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
 
         # Throws `Capybara::Poltergeist::InvalidSelector` if we try to use `#hash` syntax
@@ -85,8 +84,7 @@ feature 'Diffs URL', js: true, feature: true do
 
     context 'as user who needs to fork' do
       it 'shows fork/cancel confirmation' do
-        login_as(user)
-
+        gitlab_sign_in(user)
         visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
 
         # Throws `Capybara::Poltergeist::InvalidSelector` if we try to use `#hash` syntax
diff --git a/spec/features/merge_requests/discussion_spec.rb b/spec/features/merge_requests/discussion_spec.rb
index 9db235f35ba8641121d77f955b7e7be00e2457d1..88ae257236c3beffc8dfec7d40b15750cbf065e0 100644
--- a/spec/features/merge_requests/discussion_spec.rb
+++ b/spec/features/merge_requests/discussion_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
 
 feature 'Merge Request Discussions', feature: true do
   before do
-    login_as :admin
+    gitlab_sign_in :admin
   end
 
   describe "Diff discussions" do
diff --git a/spec/features/merge_requests/edit_mr_spec.rb b/spec/features/merge_requests/edit_mr_spec.rb
index c77a5c68bc68d096b1186663303aecf80f856a7c..804bf6967d60c7dd85f64e8c6831a69353e9ed48 100644
--- a/spec/features/merge_requests/edit_mr_spec.rb
+++ b/spec/features/merge_requests/edit_mr_spec.rb
@@ -8,7 +8,7 @@ feature 'Edit Merge Request', feature: true do
   before do
     project.team << [user, :master]
 
-    login_as user
+    gitlab_sign_in user
 
     visit edit_namespace_project_merge_request_path(project.namespace, project, merge_request)
   end
diff --git a/spec/features/merge_requests/filter_by_labels_spec.rb b/spec/features/merge_requests/filter_by_labels_spec.rb
index 32a9082b9b913f45a7b911955400dd106863721a..9b677aeca0a32a5e0059dff7a3e7d1d8e7e2b041 100644
--- a/spec/features/merge_requests/filter_by_labels_spec.rb
+++ b/spec/features/merge_requests/filter_by_labels_spec.rb
@@ -26,7 +26,7 @@ feature 'Issue filtering by Labels', feature: true, js: true do
     mr3.labels << feature
 
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
 
     visit namespace_project_merge_requests_path(project.namespace, project)
   end
diff --git a/spec/features/merge_requests/filter_by_milestone_spec.rb b/spec/features/merge_requests/filter_by_milestone_spec.rb
index 265a0cfc1980fac61373c4a200622578eafae53c..79bca0c9de2523c7be591910bde9ea09a68d434d 100644
--- a/spec/features/merge_requests/filter_by_milestone_spec.rb
+++ b/spec/features/merge_requests/filter_by_milestone_spec.rb
@@ -15,7 +15,7 @@ feature 'Merge Request filtering by Milestone', feature: true do
 
   before do
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   scenario 'filters by no Milestone', js: true do
diff --git a/spec/features/merge_requests/filter_merge_requests_spec.rb b/spec/features/merge_requests/filter_merge_requests_spec.rb
index d086be70d69d790d2492886a7f781aeb4cd0ea91..c12edf1fdf3497a9480f55b3f09231791d678149 100644
--- a/spec/features/merge_requests/filter_merge_requests_spec.rb
+++ b/spec/features/merge_requests/filter_merge_requests_spec.rb
@@ -14,7 +14,7 @@ describe 'Filter merge requests', feature: true do
   before do
     project.team << [user, :master]
     group.add_developer(user)
-    login_as(user)
+    gitlab_sign_in(user)
     create(:merge_request, source_project: project, target_project: project)
 
     visit namespace_project_merge_requests_path(project.namespace, project)
diff --git a/spec/features/merge_requests/form_spec.rb b/spec/features/merge_requests/form_spec.rb
index 00ef1ffdddcb6ff7d571ad808fb7a1e352e141e9..1996c2fa09a548f1df0601aaf08d938428a0d0d5 100644
--- a/spec/features/merge_requests/form_spec.rb
+++ b/spec/features/merge_requests/form_spec.rb
@@ -18,7 +18,7 @@ describe 'New/edit merge request', feature: true, js: true do
 
   context 'owned projects' do
     before do
-      login_as(user)
+      gitlab_sign_in(user)
     end
 
     context 'new merge request' do
@@ -96,6 +96,13 @@ describe 'New/edit merge request', feature: true, js: true do
             .to end_with(merge_request_path(merge_request))
         end
       end
+
+      it 'description has autocomplete' do
+        find('#merge_request_description').native.send_keys('')
+        fill_in 'merge_request_description', with: '@'
+
+        expect(page).to have_selector('.atwho-view')
+      end
     end
 
     context 'edit merge request' do
@@ -157,13 +164,20 @@ describe 'New/edit merge request', feature: true, js: true do
           end
         end
       end
+
+      it 'description has autocomplete' do
+        find('#merge_request_description').native.send_keys('')
+        fill_in 'merge_request_description', with: '@'
+
+        expect(page).to have_selector('.atwho-view')
+      end
     end
   end
 
   context 'forked project' do
     before do
       fork_project.team << [user, :master]
-      login_as(user)
+      gitlab_sign_in(user)
     end
 
     context 'new merge request' do
diff --git a/spec/features/merge_requests/merge_commit_message_toggle_spec.rb b/spec/features/merge_requests/merge_commit_message_toggle_spec.rb
index 221ddb5873c0d57ac506cb9632cd74ecec9e4af6..27ba380b005799ada8d2e649b117e55d14d2ca3f 100644
--- a/spec/features/merge_requests/merge_commit_message_toggle_spec.rb
+++ b/spec/features/merge_requests/merge_commit_message_toggle_spec.rb
@@ -34,7 +34,7 @@ feature 'Clicking toggle commit message link', feature: true, js: true do
   before do
     project.team << [user, :master]
 
-    login_as user
+    gitlab_sign_in user
 
     visit namespace_project_merge_request_path(project.namespace, project, merge_request)
 
diff --git a/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb b/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
index 836a7b6e09a05c3850bca18d34edbec7b980d23e..8af7d985036f8a2c4a148f74abf685fa4f6fc93a 100644
--- a/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
+++ b/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
@@ -28,7 +28,7 @@ feature 'Merge immediately', :feature, :js do
     end
 
     before do
-      login_as user
+      gitlab_sign_in user
       visit namespace_project_merge_request_path(merge_request.project.namespace, merge_request.project, merge_request)
     end
 
diff --git a/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb b/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
index 09f889d4dd6b71b7f08d2ac6f772e7e35c0f2b7f..bfadd7cb81afa9a0efacb1c809ea753134e5f6fe 100644
--- a/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
+++ b/spec/features/merge_requests/merge_when_pipeline_succeeds_spec.rb
@@ -28,7 +28,7 @@ feature 'Merge When Pipeline Succeeds', :feature, :js do
     end
 
     before do
-      login_as user
+      gitlab_sign_in user
       visit_merge_request(merge_request)
     end
 
@@ -121,7 +121,7 @@ feature 'Merge When Pipeline Succeeds', :feature, :js do
     end
 
     before do
-      login_as user
+      gitlab_sign_in user
       visit_merge_request(merge_request)
     end
 
diff --git a/spec/features/merge_requests/mini_pipeline_graph_spec.rb b/spec/features/merge_requests/mini_pipeline_graph_spec.rb
index 3a11ea3c8b2793411ebcdb87f54e92553e87908e..7664fbfbb4cb37a1fda91903947b252e18a8a79d 100644
--- a/spec/features/merge_requests/mini_pipeline_graph_spec.rb
+++ b/spec/features/merge_requests/mini_pipeline_graph_spec.rb
@@ -11,7 +11,7 @@ feature 'Mini Pipeline Graph', :js, :feature do
   before do
     build.run
 
-    login_as(user)
+    gitlab_sign_in(user)
     visit_merge_request
   end
 
diff --git a/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb b/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb
index b1dc81a606ab27b40a656ebf8fb0c4cc1a5d9b62..5cd9a7fbe269d311340719e092a02f94ecc7d10d 100644
--- a/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb
+++ b/spec/features/merge_requests/only_allow_merge_if_build_succeeds_spec.rb
@@ -5,7 +5,7 @@ feature 'Only allow merge requests to be merged if the pipeline succeeds', featu
   let(:project)       { merge_request.target_project }
 
   before do
-    login_as merge_request.author
+    gitlab_sign_in merge_request.author
 
     project.team << [merge_request.author, :master]
   end
diff --git a/spec/features/merge_requests/pipelines_spec.rb b/spec/features/merge_requests/pipelines_spec.rb
index 744bd484a80dc9b2fe0568769f1d2ef553ec6b5b..c2241317e04163daa2cc5aa5e2e0af93b614b830 100644
--- a/spec/features/merge_requests/pipelines_spec.rb
+++ b/spec/features/merge_requests/pipelines_spec.rb
@@ -7,7 +7,7 @@ feature 'Pipelines for Merge Requests', feature: true, js: true do
 
   before do
     project.team << [user, :master]
-    login_as user
+    gitlab_sign_in user
   end
 
   context 'with pipelines' do
diff --git a/spec/features/merge_requests/target_branch_spec.rb b/spec/features/merge_requests/target_branch_spec.rb
index c154cf8ade9f81f47fdb83e5d67ca1187a6ed89b..4328d66c748ce72b8732550c3bbb7ef58a495868 100644
--- a/spec/features/merge_requests/target_branch_spec.rb
+++ b/spec/features/merge_requests/target_branch_spec.rb
@@ -13,7 +13,7 @@ describe 'Target branch', feature: true, js: true do
   end
 
   before do
-    login_as user
+    gitlab_sign_in user
     project.team << [user, :master]
   end
 
diff --git a/spec/features/merge_requests/toggle_whitespace_changes_spec.rb b/spec/features/merge_requests/toggle_whitespace_changes_spec.rb
index 0f98737b7008959e510f4932d3c1c3bde4da78c8..cba9a2cda99b570ca2339545aeb36505746f121e 100644
--- a/spec/features/merge_requests/toggle_whitespace_changes_spec.rb
+++ b/spec/features/merge_requests/toggle_whitespace_changes_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
 
 feature 'Toggle Whitespace Changes', js: true, feature: true do
   before do
-    login_as :admin
+    gitlab_sign_in :admin
     merge_request = create(:merge_request)
     project = merge_request.source_project
     visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
diff --git a/spec/features/merge_requests/toggler_behavior_spec.rb b/spec/features/merge_requests/toggler_behavior_spec.rb
index 3acd3f6a8b3a9e07c25c9b68571203bc9f0180ab..c4c06e9a7a0c61d1938281ef53a8a1f4300d799c 100644
--- a/spec/features/merge_requests/toggler_behavior_spec.rb
+++ b/spec/features/merge_requests/toggler_behavior_spec.rb
@@ -8,7 +8,7 @@ feature 'toggler_behavior', js: true, feature: true do
   let(:fragment_id) { "#note_#{note.id}" }
 
   before do
-    login_as :admin
+    gitlab_sign_in :admin
     project = merge_request.source_project
     page.current_window.resize_to(1000, 300)
     visit "#{namespace_project_merge_request_path(project.namespace, project, merge_request)}#{fragment_id}"
diff --git a/spec/features/merge_requests/update_merge_requests_spec.rb b/spec/features/merge_requests/update_merge_requests_spec.rb
index bcdfdf78a44cc3b61abd3fed77736d84510ec1eb..d0418c74699bc53e01bcb2a89bbbc7fdaa846a0d 100644
--- a/spec/features/merge_requests/update_merge_requests_spec.rb
+++ b/spec/features/merge_requests/update_merge_requests_spec.rb
@@ -7,7 +7,7 @@ feature 'Multiple merge requests updating from merge_requests#index', feature: t
 
   before do
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   context 'status', js: true do
diff --git a/spec/features/merge_requests/user_posts_diff_notes_spec.rb b/spec/features/merge_requests/user_posts_diff_notes_spec.rb
index 14bc549c9f9854beb70e492d1bf67d83ec77709d..ac7e0eb27274a8dfa2eec6bcb0f5608da438d63e 100644
--- a/spec/features/merge_requests/user_posts_diff_notes_spec.rb
+++ b/spec/features/merge_requests/user_posts_diff_notes_spec.rb
@@ -7,7 +7,7 @@ feature 'Merge requests > User posts diff notes', :js do
 
   before do
     project.add_developer(user)
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   let(:comment_button_class) { '.add-diff-note' }
diff --git a/spec/features/merge_requests/user_posts_notes_spec.rb b/spec/features/merge_requests/user_posts_notes_spec.rb
index 22552529b9ed1c930c61b3541056c373cedb8e73..12f987e12ea690c744bb45aab71a13081eed4935 100644
--- a/spec/features/merge_requests/user_posts_notes_spec.rb
+++ b/spec/features/merge_requests/user_posts_notes_spec.rb
@@ -13,7 +13,7 @@ describe 'Merge requests > User posts notes', :js do
   end
 
   before do
-    login_as :admin
+    gitlab_sign_in :admin
     visit namespace_project_merge_request_path(project.namespace, project, merge_request)
   end
 
@@ -22,8 +22,8 @@ describe 'Merge requests > User posts notes', :js do
   describe 'the note form' do
     it 'is valid' do
       is_expected.to have_css('.js-main-target-form', visible: true, count: 1)
-      expect(find('.js-main-target-form .js-comment-button').value).
-        to eq('Comment')
+      expect(find('.js-main-target-form .js-comment-button').value)
+        .to eq('Comment')
       page.within('.js-main-target-form') do
         expect(page).not_to have_link('Cancel')
       end
@@ -123,8 +123,8 @@ describe 'Merge requests > User posts notes', :js do
 
         page.within("#note_#{note.id}") do
           is_expected.to have_css('.note_edited_ago')
-          expect(find('.note_edited_ago').text).
-            to match(/less than a minute ago/)
+          expect(find('.note_edited_ago').text)
+            .to match(/less than a minute ago/)
         end
       end
     end
diff --git a/spec/features/merge_requests/user_sees_system_notes_spec.rb b/spec/features/merge_requests/user_sees_system_notes_spec.rb
index 55d0f9d728cdf53f84f801e1b195ec55820a81b8..0d88a8172b0bc3c11a4b33b7ef86fc2e60ba38c7 100644
--- a/spec/features/merge_requests/user_sees_system_notes_spec.rb
+++ b/spec/features/merge_requests/user_sees_system_notes_spec.rb
@@ -11,7 +11,7 @@ feature 'Merge requests > User sees system notes' do
     before do
       user = create(:user)
       private_project.add_developer(user)
-      login_as(user)
+      gitlab_sign_in(user)
     end
 
     it 'shows the system note' do
diff --git a/spec/features/merge_requests/user_uses_slash_commands_spec.rb b/spec/features/merge_requests/user_uses_slash_commands_spec.rb
index 0e64a3e1a4b1752518c0375ee7bc415c6be4f43c..71aa71e380ec6b93b9837fa844a4fb3bb958bca2 100644
--- a/spec/features/merge_requests/user_uses_slash_commands_spec.rb
+++ b/spec/features/merge_requests/user_uses_slash_commands_spec.rb
@@ -1,14 +1,14 @@
 require 'rails_helper'
 
-feature 'Merge Requests > User uses slash commands', feature: true, js: true do
-  include SlashCommandsHelpers
+feature 'Merge Requests > User uses quick actions', feature: true, js: true do
+  include QuickActionsHelpers
 
   let(:user) { create(:user) }
   let(:project) { create(:project, :public) }
   let(:merge_request) { create(:merge_request, source_project: project) }
   let!(:milestone) { create(:milestone, project: project, title: 'ASAP') }
 
-  it_behaves_like 'issuable record that supports slash commands in its description and notes', :merge_request do
+  it_behaves_like 'issuable record that supports quick actions in its description and notes', :merge_request do
     let(:issuable) { create(:merge_request, source_project: project) }
     let(:new_url_opts) { { merge_request: { source_branch: 'feature', target_branch: 'master' } } }
   end
@@ -16,7 +16,7 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do
   describe 'merge-request-only commands' do
     before do
       project.team << [user, :master]
-      login_with(user)
+      gitlab_sign_in(user)
       visit namespace_project_merge_request_path(project.namespace, project, merge_request)
     end
 
@@ -51,8 +51,8 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do
         let(:guest) { create(:user) }
         before do
           project.team << [guest, :guest]
-          logout
-          login_with(guest)
+          gitlab_sign_out
+          gitlab_sign_in(guest)
           visit namespace_project_merge_request_path(project.namespace, project, merge_request)
         end
 
@@ -97,8 +97,8 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do
         let(:guest) { create(:user) }
         before do
           project.team << [guest, :guest]
-          logout
-          login_with(guest)
+          gitlab_sign_out
+          gitlab_sign_in(guest)
           visit namespace_project_merge_request_path(project.namespace, project, merge_request)
         end
 
@@ -125,9 +125,9 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do
       let(:new_url_opts) { { merge_request: { source_branch: 'feature' } } }
 
       before do
-        logout
+        gitlab_sign_out
         another_project.team << [user, :master]
-        login_with(user)
+        gitlab_sign_in(user)
       end
 
       it 'changes target_branch in new merge_request' do
@@ -181,8 +181,8 @@ feature 'Merge Requests > User uses slash commands', feature: true, js: true do
         let(:guest) { create(:user) }
         before do
           project.team << [guest, :guest]
-          logout
-          login_with(guest)
+          gitlab_sign_out
+          gitlab_sign_in(guest)
           visit namespace_project_merge_request_path(project.namespace, project, merge_request)
         end
 
diff --git a/spec/features/merge_requests/versions_spec.rb b/spec/features/merge_requests/versions_spec.rb
index aad522ee26efad75375d0073b545cff5caa98890..04a72d3be341dcd52f8268208180679ae2f509c5 100644
--- a/spec/features/merge_requests/versions_spec.rb
+++ b/spec/features/merge_requests/versions_spec.rb
@@ -8,7 +8,7 @@ feature 'Merge Request versions', js: true, feature: true do
   let!(:merge_request_diff3) { merge_request.merge_request_diffs.create(head_commit_sha: '5937ac0a7beb003549fc5fd26fc247adbce4a52e') }
 
   before do
-    login_as :admin
+    gitlab_sign_in :admin
     visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
   end
 
diff --git a/spec/features/merge_requests/widget_deployments_spec.rb b/spec/features/merge_requests/widget_deployments_spec.rb
index 118ecd9cba523cac3b179b6a27ddb283e021bb01..e82e69c5f4a34383a44739d47ec964465076c089 100644
--- a/spec/features/merge_requests/widget_deployments_spec.rb
+++ b/spec/features/merge_requests/widget_deployments_spec.rb
@@ -12,7 +12,7 @@ feature 'Widget Deployments Header', feature: true, js: true do
     given!(:manual) { }
 
     background do
-      login_as(user)
+      gitlab_sign_in(user)
       project.team << [user, role]
       visit namespace_project_merge_request_path(project.namespace, project, merge_request)
     end
diff --git a/spec/features/merge_requests/widget_spec.rb b/spec/features/merge_requests/widget_spec.rb
index 4f3a5119915538553bc755b8c693127ec7d427c5..3ac1f603de6be8ba84da6de0f8aed316c7a78d0c 100644
--- a/spec/features/merge_requests/widget_spec.rb
+++ b/spec/features/merge_requests/widget_spec.rb
@@ -7,7 +7,7 @@ describe 'Merge request', :feature, :js do
 
   before do
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   context 'new merge request' do
@@ -209,8 +209,8 @@ describe 'Merge request', :feature, :js do
 
     before do
       project.team << [user2, :master]
-      logout
-      login_as user2
+      gitlab_sign_out
+      gitlab_sign_in user2
       merge_request.update(target_project: fork_project)
       visit namespace_project_merge_request_path(project.namespace, project, merge_request)
     end
diff --git a/spec/features/merge_requests/wip_message_spec.rb b/spec/features/merge_requests/wip_message_spec.rb
index 3311731b33bd67bb0471ee4c83a7b86ab9eeadec..72d001bf408e24dd2b642bda5677afe4e5e4a00b 100644
--- a/spec/features/merge_requests/wip_message_spec.rb
+++ b/spec/features/merge_requests/wip_message_spec.rb
@@ -6,7 +6,7 @@ feature 'Work In Progress help message', feature: true do
 
   before do
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   context 'with WIP commits' do
diff --git a/spec/features/milestone_spec.rb b/spec/features/milestone_spec.rb
index c07de01c594da7faa4962fd9c1a7cb6938094a97..58989581ffeb9b5be810b1f3d3d5b703a524476a 100644
--- a/spec/features/milestone_spec.rb
+++ b/spec/features/milestone_spec.rb
@@ -6,7 +6,7 @@ feature 'Milestone', feature: true do
 
   before do
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   feature 'Create a milestone' do
diff --git a/spec/features/milestones/milestones_spec.rb b/spec/features/milestones/milestones_spec.rb
deleted file mode 100644
index c8a4d23f695829e8d8465225ac8d87fb2b4f520b..0000000000000000000000000000000000000000
--- a/spec/features/milestones/milestones_spec.rb
+++ /dev/null
@@ -1,109 +0,0 @@
-require 'rails_helper'
-
-describe 'Milestone draggable', feature: true, js: true do
-  include DragTo
-
-  let(:milestone) { create(:milestone, project: project, title: 8.14) }
-  let(:project)   { create(:empty_project, :public) }
-  let(:user)      { create(:user) }
-
-  context 'issues' do
-    let(:issue)        { page.find_by_id('issues-list-unassigned').find('li') }
-    let(:issue_target) { page.find_by_id('issues-list-ongoing') }
-
-    it 'does not allow guest to drag issue' do
-      create_and_drag_issue
-
-      expect(issue_target).not_to have_selector('.issuable-row')
-    end
-
-    it 'does not allow authorized user to drag issue' do
-      login_as(user)
-      create_and_drag_issue
-
-      expect(issue_target).not_to have_selector('.issuable-row')
-    end
-
-    it 'allows author to drag issue' do
-      login_as(user)
-      create_and_drag_issue(author: user)
-
-      expect(issue_target).to have_selector('.issuable-row')
-    end
-
-    it 'allows admin to drag issue' do
-      login_as(:admin)
-      create_and_drag_issue
-
-      expect(issue_target).to have_selector('.issuable-row')
-    end
-
-    it 'assigns issue when it has been dragged to ongoing list' do
-      login_as(:admin)
-      create_and_drag_issue
-
-      expect(@issue.reload.assignees).not_to be_empty
-      expect(page).to have_selector("#sortable_issue_#{@issue.iid} .assignee-icon img", count: 1)
-    end
-  end
-
-  context 'merge requests' do
-    let(:merge_request)        { page.find_by_id('merge_requests-list-unassigned').find('li') }
-    let(:merge_request_target) { page.find_by_id('merge_requests-list-ongoing') }
-
-    it 'does not allow guest to drag merge request' do
-      create_and_drag_merge_request
-
-      expect(merge_request_target).not_to have_selector('.issuable-row')
-    end
-
-    it 'does not allow authorized user to drag merge request' do
-      login_as(user)
-      create_and_drag_merge_request
-
-      expect(merge_request_target).not_to have_selector('.issuable-row')
-    end
-
-    it 'allows author to drag merge request' do
-      login_as(user)
-      create_and_drag_merge_request(author: user)
-
-      expect(merge_request_target).to have_selector('.issuable-row')
-    end
-
-    it 'allows admin to drag merge request' do
-      login_as(:admin)
-      create_and_drag_merge_request
-
-      expect(merge_request_target).to have_selector('.issuable-row')
-    end
-  end
-
-  def create_and_drag_issue(params = {})
-    @issue = create(:issue, params.merge(title: 'Foo', project: project, milestone: milestone))
-
-    visit namespace_project_milestone_path(project.namespace, project, milestone)
-    scroll_into_view('.milestone-content')
-    drag_to(selector: '.issues-sortable-list', list_to_index: 1)
-
-    wait_for_requests
-  end
-
-  def create_and_drag_merge_request(params = {})
-    create(:merge_request, params.merge(title: 'Foo', source_project: project, target_project: project, milestone: milestone))
-
-    visit namespace_project_milestone_path(project.namespace, project, milestone)
-    page.find("a[href='#tab-merge-requests']").click
-
-    wait_for_requests
-
-    scroll_into_view('.milestone-content')
-    drag_to(selector: '.merge_requests-sortable-list', list_to_index: 1)
-
-    wait_for_requests
-  end
-
-  def scroll_into_view(selector)
-    page.evaluate_script("document.querySelector('#{selector}').scrollIntoView();")
-  end
-end
diff --git a/spec/features/milestones/show_spec.rb b/spec/features/milestones/show_spec.rb
index 227eb04ba7282828d58c9f00ac1b897c280abb32..cdf6cfba402debb98d207fd1df06434aaca3b158 100644
--- a/spec/features/milestones/show_spec.rb
+++ b/spec/features/milestones/show_spec.rb
@@ -9,7 +9,7 @@ describe 'Milestone show', feature: true do
 
   before do
     project.add_user(user, :developer) 
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   def visit_milestone
diff --git a/spec/features/participants_autocomplete_spec.rb b/spec/features/participants_autocomplete_spec.rb
index 449ce80bc718699a80a088d4f3774c466a28fe06..b8966cf621c52bc8fc97b4f1cfbbfb9a1ccdbde5 100644
--- a/spec/features/participants_autocomplete_spec.rb
+++ b/spec/features/participants_autocomplete_spec.rb
@@ -8,7 +8,7 @@ feature 'Member autocomplete', :js do
 
   before do
     note # actually create the note
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   shared_examples "open suggestions when typing @" do
diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb
index 7df628fd7a0b7b4091fad466594113b2350fb839..bb4263d83f37aef54053bc41230a5aab280e947f 100644
--- a/spec/features/profile_spec.rb
+++ b/spec/features/profile_spec.rb
@@ -4,7 +4,7 @@ describe 'Profile account page', feature: true do
   let(:user) { create(:user) }
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   describe 'when signup is enabled' do
diff --git a/spec/features/profiles/account_spec.rb b/spec/features/profiles/account_spec.rb
index 89868c737f7eb896752b0b4e754d92089cd2b525..33fd29b429b59434d78d45ab1af2ed446d088788 100644
--- a/spec/features/profiles/account_spec.rb
+++ b/spec/features/profiles/account_spec.rb
@@ -4,7 +4,7 @@ feature 'Profile > Account', feature: true do
   given(:user) { create(:user, username: 'foo') }
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   describe 'Change username' do
diff --git a/spec/features/profiles/chat_names_spec.rb b/spec/features/profiles/chat_names_spec.rb
index 6f6f7029c0b6891974c8e11874c66762ce46035c..1a162d6be0e3cd2a48eecdab45e9011518614961 100644
--- a/spec/features/profiles/chat_names_spec.rb
+++ b/spec/features/profiles/chat_names_spec.rb
@@ -5,7 +5,7 @@ feature 'Profile > Chat', feature: true do
   given(:service) { create(:service) }
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   describe 'uses authorization link' do
diff --git a/spec/features/profiles/keys_spec.rb b/spec/features/profiles/keys_spec.rb
index 2f436f153aa5c193c45e106c8b15c741150e6e52..13f9afd4ce028d1d963e01716993059a2e5f3fc5 100644
--- a/spec/features/profiles/keys_spec.rb
+++ b/spec/features/profiles/keys_spec.rb
@@ -4,7 +4,7 @@ feature 'Profile > SSH Keys', feature: true do
   let(:user) { create(:user) }
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   describe 'User adds a key' do
diff --git a/spec/features/profiles/oauth_applications_spec.rb b/spec/features/profiles/oauth_applications_spec.rb
index 1a5a9059dbd6067ec4d09e5d3be81241013c8aa4..a6f9beafe174e570a05369d05aa1582e19c862c9 100644
--- a/spec/features/profiles/oauth_applications_spec.rb
+++ b/spec/features/profiles/oauth_applications_spec.rb
@@ -4,7 +4,7 @@ describe 'Profile > Applications', feature: true do
   let(:user) { create(:user) }
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   describe 'User manages applications', js: true do
diff --git a/spec/features/profiles/password_spec.rb b/spec/features/profiles/password_spec.rb
index 4cbdd89d46f02830d5cfc8e1ea5248fe373e4973..2d36f3d020fc06a35acb73022f409965d2dc2246 100644
--- a/spec/features/profiles/password_spec.rb
+++ b/spec/features/profiles/password_spec.rb
@@ -4,7 +4,7 @@ describe 'Profile > Password', feature: true do
   let(:user) { create(:user, password_automatically_set: true) }
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
     visit edit_profile_password_path
   end
 
diff --git a/spec/features/profiles/personal_access_tokens_spec.rb b/spec/features/profiles/personal_access_tokens_spec.rb
index 7e2e685df26ceafaf8d13337a6903c5b3cb37126..d7acaaf1eb85c0d4ca5649850b17d710ebb4b77b 100644
--- a/spec/features/profiles/personal_access_tokens_spec.rb
+++ b/spec/features/profiles/personal_access_tokens_spec.rb
@@ -23,7 +23,7 @@ describe 'Profile > Personal Access Tokens', feature: true, js: true do
   end
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   describe "token creation" do
diff --git a/spec/features/profiles/preferences_spec.rb b/spec/features/profiles/preferences_spec.rb
index d368bc4d7533b611ef6ad26a69de38f706632946..8e7ef6bc1101b9d0bf872b2eb8d1d0f72657c825 100644
--- a/spec/features/profiles/preferences_spec.rb
+++ b/spec/features/profiles/preferences_spec.rb
@@ -4,7 +4,7 @@ describe 'Profile > Preferences', feature: true do
   let(:user) { create(:user) }
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
     visit profile_preferences_path
   end
 
diff --git a/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb b/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb
index e05fbb3715c07d36acd1c16cd7e9eea9c4f07594..c0092836e3ba10a8e5639157ca15740f28f90213 100644
--- a/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb
+++ b/spec/features/profiles/user_changes_notified_of_own_activity_spec.rb
@@ -4,7 +4,7 @@ feature 'Profile > Notifications > User changes notified_of_own_activity setting
   let(:user) { create(:user) }
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   scenario 'User opts into receiving notifications about their own activity' do
diff --git a/spec/features/projects/activity/rss_spec.rb b/spec/features/projects/activity/rss_spec.rb
index 3c1de5c09b20248e3a8d4d66b5ec18ffbb5010c4..84c81d43448974130c9c9f5fa15e2bca045dd7f9 100644
--- a/spec/features/projects/activity/rss_spec.rb
+++ b/spec/features/projects/activity/rss_spec.rb
@@ -12,7 +12,7 @@ feature 'Project Activity RSS' do
     before do
       user = create(:user)
       project.team << [user, :developer]
-      login_as(user)
+      gitlab_sign_in(user)
       visit path
     end
 
diff --git a/spec/features/projects/badges/coverage_spec.rb b/spec/features/projects/badges/coverage_spec.rb
index 01a95bf49acb465ec9d49243214f46bf7a685409..9624e1a71b05a9106688be8f0c43e6e2d1d5b4f4 100644
--- a/spec/features/projects/badges/coverage_spec.rb
+++ b/spec/features/projects/badges/coverage_spec.rb
@@ -7,7 +7,7 @@ feature 'test coverage badge' do
   context 'when user has access to view badge' do
     background do
       project.team << [user, :developer]
-      login_as(user)
+      gitlab_sign_in(user)
     end
 
     scenario 'user requests coverage badge image for pipeline' do
@@ -45,7 +45,7 @@ feature 'test coverage badge' do
   end
 
   context 'when user does not have access to view badge' do
-    background { login_as(user) }
+    background { gitlab_sign_in(user) }
 
     scenario 'user requests test coverage badge image' do
       show_test_coverage_badge
diff --git a/spec/features/projects/badges/list_spec.rb b/spec/features/projects/badges/list_spec.rb
index ae9db0c0d6e7cf0bb4817b1985df927d2d4b4871..348748152bb50b36d3e0a57805e1dea98c52170f 100644
--- a/spec/features/projects/badges/list_spec.rb
+++ b/spec/features/projects/badges/list_spec.rb
@@ -5,7 +5,7 @@ feature 'list of badges' do
     user = create(:user)
     project = create(:project)
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
     visit namespace_project_pipelines_settings_path(project.namespace, project)
   end
 
diff --git a/spec/features/projects/blobs/edit_spec.rb b/spec/features/projects/blobs/edit_spec.rb
index d04c3248ead15c8e46dd068f62e9fe25fd6097f8..d0bc032ee9330ef7af1a513158602e50c201d5c8 100644
--- a/spec/features/projects/blobs/edit_spec.rb
+++ b/spec/features/projects/blobs/edit_spec.rb
@@ -14,7 +14,7 @@ feature 'Editing file blob', feature: true, js: true do
 
     before do
       project.team << [user, role]
-      login_as(user)
+      gitlab_sign_in(user)
     end
 
     def edit_and_commit
@@ -61,7 +61,7 @@ feature 'Editing file blob', feature: true, js: true do
         it 'redirects to sign in and returns' do
           expect(page).to have_current_path(new_user_session_path)
 
-          login_as(user)
+          gitlab_sign_in(user)
 
           expect(page).to have_current_path(namespace_project_edit_blob_path(project.namespace, project, tree_join(branch, file_path)))
         end
@@ -77,7 +77,7 @@ feature 'Editing file blob', feature: true, js: true do
         it 'redirects to sign in and returns' do
           expect(page).to have_current_path(new_user_session_path)
 
-          login_as(user)
+          gitlab_sign_in(user)
 
           expect(page).to have_current_path(namespace_project_blob_path(project.namespace, project, tree_join(branch, file_path)))
         end
@@ -92,7 +92,7 @@ feature 'Editing file blob', feature: true, js: true do
         project.team << [user, :developer]
         project.repository.add_branch(user, protected_branch, 'master')
         create(:protected_branch, project: project, name: protected_branch)
-        login_as(user)
+        gitlab_sign_in(user)
       end
 
       context 'on some branch' do
@@ -122,7 +122,7 @@ feature 'Editing file blob', feature: true, js: true do
 
       before do
         project.team << [user, :master]
-        login_as(user)
+        gitlab_sign_in(user)
         visit namespace_project_edit_blob_path(project.namespace, project, tree_join(branch, file_path))
       end
 
diff --git a/spec/features/projects/branches/download_buttons_spec.rb b/spec/features/projects/branches/download_buttons_spec.rb
index 92028c1936102665d38529103de74ad3c5eda124..d8c4d475a2cce3fd02888456548375b1485e0da4 100644
--- a/spec/features/projects/branches/download_buttons_spec.rb
+++ b/spec/features/projects/branches/download_buttons_spec.rb
@@ -22,7 +22,7 @@ feature 'Download buttons in branches page', feature: true do
   end
 
   background do
-    login_as(user)
+    gitlab_sign_in(user)
     project.team << [user, role]
   end
 
diff --git a/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb b/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb
index c5e0a0f051710b5f20c2a5741ca2d6a1ececbe4e..406fa52e723d9d397d31c052e433665f2046cbca 100644
--- a/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb
+++ b/spec/features/projects/branches/new_branch_ref_dropdown_spec.rb
@@ -8,7 +8,7 @@ describe 'New Branch Ref Dropdown', :js, :feature do
   before do
     project.add_master(user)
 
-    login_as(user)
+    gitlab_sign_in(user)
     visit new_namespace_project_branch_path(project.namespace, project)
   end
 
diff --git a/spec/features/projects/branches_spec.rb b/spec/features/projects/branches_spec.rb
index 7668ce5f8beb349fc22eae2d9befa907c1526fcf..8694366de35544727635d1bf3a191556bfb60ae6 100644
--- a/spec/features/projects/branches_spec.rb
+++ b/spec/features/projects/branches_spec.rb
@@ -1,6 +1,7 @@
 require 'spec_helper'
 
 describe 'Branches', feature: true do
+  let(:user) { create(:user) }
   let(:project) { create(:project, :public) }
   let(:repository) { project.repository }
 
@@ -12,8 +13,8 @@ describe 'Branches', feature: true do
 
   context 'logged in as developer' do
     before do
-      login_as :user
-      project.team << [@user, :developer]
+      sign_in(user)
+      project.team << [user, :developer]
     end
 
     describe 'Initial branches page' do
@@ -27,7 +28,7 @@ describe 'Branches', feature: true do
       it 'avoids a N+1 query in branches index' do
         control_count = ActiveRecord::QueryRecorder.new { visit namespace_project_branches_path(project.namespace, project) }.count
 
-        %w(one two three four five).each { |ref| repository.add_branch(@user, ref, 'master') }
+        %w(one two three four five).each { |ref| repository.add_branch(user, ref, 'master') }
 
         expect { visit namespace_project_branches_path(project.namespace, project) }.not_to exceed_query_limit(control_count)
       end
@@ -64,14 +65,14 @@ describe 'Branches', feature: true do
 
     describe 'Delete protected branch' do
       before do
-        project.add_user(@user, :master)
+        project.add_user(user, :master)
         visit namespace_project_protected_branches_path(project.namespace, project)
         set_protected_branch_name('fix')
         click_on "Protect"
 
         within(".protected-branches-list") { expect(page).to have_content('fix') }
         expect(ProtectedBranch.count).to eq(1)
-        project.add_user(@user, :developer)
+        project.add_user(user, :developer)
       end
 
       it 'does not allow devleoper to removes protected branch', js: true do
@@ -87,8 +88,8 @@ describe 'Branches', feature: true do
 
   context 'logged in as master' do
     before do
-      login_as :user
-      project.team << [@user, :master]
+      sign_in(user)
+      project.team << [user, :master]
     end
 
     describe 'Delete protected branch' do
diff --git a/spec/features/projects/commit/builds_spec.rb b/spec/features/projects/commit/builds_spec.rb
index 268d420c594bad4189874569c9aa2db99b8f35be..e5b1f95f2b96b67f377a4e5cda6e500cdd64f4d6 100644
--- a/spec/features/projects/commit/builds_spec.rb
+++ b/spec/features/projects/commit/builds_spec.rb
@@ -6,7 +6,7 @@ feature 'project commit pipelines', js: true do
   background do
     user = create(:user)
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   context 'when no builds triggered yet' do
diff --git a/spec/features/projects/commit/cherry_pick_spec.rb b/spec/features/projects/commit/cherry_pick_spec.rb
index bc7ca0ddd389ff29c3054eeace09cd6c70276758..0d3fa72fbf5507044c3207c9423f552dfd2c1ed7 100644
--- a/spec/features/projects/commit/cherry_pick_spec.rb
+++ b/spec/features/projects/commit/cherry_pick_spec.rb
@@ -1,14 +1,15 @@
 require 'spec_helper'
 
 describe 'Cherry-pick Commits' do
+  let(:user) { create(:user) }
   let(:group) { create(:group) }
   let(:project) { create(:project, namespace: group) }
   let(:master_pickable_commit)  { project.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') }
   let(:master_pickable_merge)  { project.commit('e56497bb5f03a90a51293fc6d516788730953899') }
 
   before do
-    login_as :user
-    project.team << [@user, :master]
+    sign_in(user)
+    project.team << [user, :master]
     visit namespace_project_commit_path(project.namespace, project, master_pickable_commit.id)
   end
 
diff --git a/spec/features/projects/commit/mini_pipeline_graph_spec.rb b/spec/features/projects/commit/mini_pipeline_graph_spec.rb
index f2de195eb7fc5ba2512ce5ce01211d99b30f9cb7..570a7ae7b1662b3b868e1dec1a6518579ff3b75a 100644
--- a/spec/features/projects/commit/mini_pipeline_graph_spec.rb
+++ b/spec/features/projects/commit/mini_pipeline_graph_spec.rb
@@ -5,7 +5,7 @@ feature 'Mini Pipeline Graph in Commit View', :js, :feature do
   let(:project) { create(:project, :public) }
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   context 'when commit has pipelines' do
diff --git a/spec/features/projects/commit/rss_spec.rb b/spec/features/projects/commit/rss_spec.rb
index 03b6d560c96f8b9cb61fe0f2627097d69adcff58..f7548a56984a62c639d30c11f59bfc20ea7cb537 100644
--- a/spec/features/projects/commit/rss_spec.rb
+++ b/spec/features/projects/commit/rss_spec.rb
@@ -8,7 +8,7 @@ feature 'Project Commits RSS' do
     before do
       user = create(:user)
       project.team << [user, :developer]
-      login_as(user)
+      gitlab_sign_in(user)
       visit path
     end
 
diff --git a/spec/features/projects/compare_spec.rb b/spec/features/projects/compare_spec.rb
index ee6985ad993dc81552193d5b6c927e113cfbe422..4743d69fb7503b393f2f9e039a318d89c92d74f8 100644
--- a/spec/features/projects/compare_spec.rb
+++ b/spec/features/projects/compare_spec.rb
@@ -6,7 +6,7 @@ describe "Compare", js: true do
 
   before do
     project.team << [user, :master]
-    login_as user
+    gitlab_sign_in user
     visit namespace_project_compare_index_path(project.namespace, project, from: "master", to: "master")
   end
 
diff --git a/spec/features/projects/deploy_keys_spec.rb b/spec/features/projects/deploy_keys_spec.rb
index 06abfbbc86b78ce11958ea0389861f73b0dd5ff5..a31960639fea2a3304be5d6ad99535c0bcac9802 100644
--- a/spec/features/projects/deploy_keys_spec.rb
+++ b/spec/features/projects/deploy_keys_spec.rb
@@ -6,7 +6,7 @@ describe 'Project deploy keys', :js, :feature do
 
   before do
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   describe 'removing key' do
diff --git a/spec/features/projects/developer_views_empty_project_instructions_spec.rb b/spec/features/projects/developer_views_empty_project_instructions_spec.rb
index 0c51fe72ca4e4b97a6c77f65038c9c60ab64f2dd..a943f1e6a088234ff52362e4e4ef9c12859f7d4f 100644
--- a/spec/features/projects/developer_views_empty_project_instructions_spec.rb
+++ b/spec/features/projects/developer_views_empty_project_instructions_spec.rb
@@ -7,7 +7,7 @@ feature 'Developer views empty project instructions', feature: true do
   background do
     project.team << [developer, :developer]
 
-    login_as(developer)
+    gitlab_sign_in(developer)
   end
 
   context 'without an SSH key' do
diff --git a/spec/features/projects/edit_spec.rb b/spec/features/projects/edit_spec.rb
index a263781c43c96a2da51b25281a0c282908c64111..ca202b95a448340c9734d7d1d6e9d903e302903b 100644
--- a/spec/features/projects/edit_spec.rb
+++ b/spec/features/projects/edit_spec.rb
@@ -6,7 +6,7 @@ feature 'Project edit', feature: true, js: true do
 
   before do
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
 
     visit edit_namespace_project_path(project.namespace, project)
   end
diff --git a/spec/features/projects/environments/environment_metrics_spec.rb b/spec/features/projects/environments/environment_metrics_spec.rb
index ee925e811e1b6d3a71d811c9bda15809eb5e3f10..b48dcf6c7747a12ebc1dd9c702ae710a082a3d53 100644
--- a/spec/features/projects/environments/environment_metrics_spec.rb
+++ b/spec/features/projects/environments/environment_metrics_spec.rb
@@ -15,7 +15,7 @@ feature 'Environment > Metrics', :feature do
     create(:deployment, environment: environment, deployable: build)
     stub_all_prometheus_requests(environment.slug)
 
-    login_as(user)
+    gitlab_sign_in(user)
     visit_environment(environment)
   end
 
diff --git a/spec/features/projects/environments/environment_spec.rb b/spec/features/projects/environments/environment_spec.rb
index 18b608c863efe50bdfe0516692986d6d86af835e..7d565555f1f1a111f651afc24282b5cf2abb2857 100644
--- a/spec/features/projects/environments/environment_spec.rb
+++ b/spec/features/projects/environments/environment_spec.rb
@@ -6,7 +6,7 @@ feature 'Environment', :feature do
   given(:role) { :developer }
 
   background do
-    login_as(user)
+    gitlab_sign_in(user)
     project.team << [user, role]
   end
 
diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb
index 613b1edba36eca19c36d673ed5ae0192db39ebf0..83883dba0ba69a6316a434ea90e4e357fa9b40b3 100644
--- a/spec/features/projects/environments/environments_spec.rb
+++ b/spec/features/projects/environments/environments_spec.rb
@@ -7,7 +7,7 @@ feature 'Environments page', :feature, :js do
 
   background do
     project.team << [user, role]
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   given!(:environment) { }
diff --git a/spec/features/projects/features_visibility_spec.rb b/spec/features/projects/features_visibility_spec.rb
index d76b5e4ef1b0dbb6d5d3535e218169a5c1767cb2..db2790a4bced50150f2b7cd676b8f0a836c33941 100644
--- a/spec/features/projects/features_visibility_spec.rb
+++ b/spec/features/projects/features_visibility_spec.rb
@@ -9,7 +9,7 @@ describe 'Edit Project Settings', feature: true do
   describe 'project features visibility selectors', js: true do
     before do
       project.team << [member, :master]
-      login_as(member)
+      gitlab_sign_in(member)
     end
 
     tools = { builds: "pipelines", issues: "issues", wiki: "wiki", snippets: "snippets", merge_requests: "merge_requests" }
@@ -83,7 +83,7 @@ describe 'Edit Project Settings', feature: true do
 
     context 'normal user' do
       before do
-        login_as(member)
+        gitlab_sign_in(member)
       end
 
       it 'renders 200 if tool is enabled' do
@@ -130,7 +130,7 @@ describe 'Edit Project Settings', feature: true do
     context 'admin user' do
       before do
         non_member.update_attribute(:admin, true)
-        login_as(non_member)
+        gitlab_sign_in(non_member)
       end
 
       it 'renders 404 if feature is disabled' do
@@ -156,7 +156,7 @@ describe 'Edit Project Settings', feature: true do
   describe 'repository visibility', js: true do
     before do
       project.team << [member, :master]
-      login_as(member)
+      gitlab_sign_in(member)
       visit edit_namespace_project_path(project.namespace, project)
     end
 
@@ -242,7 +242,7 @@ describe 'Edit Project Settings', feature: true do
 
     before do
       project.team << [member, :guest]
-      login_as(member)
+      gitlab_sign_in(member)
       visit namespace_project_path(project.namespace, project)
     end
 
diff --git a/spec/features/projects/files/browse_files_spec.rb b/spec/features/projects/files/browse_files_spec.rb
index 30a1eedbb485259c217fda06b82073ddc4a173f8..2a82c3ac179996b69323b1846f7303eda687df75 100644
--- a/spec/features/projects/files/browse_files_spec.rb
+++ b/spec/features/projects/files/browse_files_spec.rb
@@ -6,7 +6,7 @@ feature 'user browses project', feature: true, js: true do
 
   before do
     project.team << [user, :master]
-    login_with(user)
+    gitlab_sign_in(user)
     visit namespace_project_tree_path(project.namespace, project, project.default_branch)
   end
 
diff --git a/spec/features/projects/files/creating_a_file_spec.rb b/spec/features/projects/files/creating_a_file_spec.rb
index 69744ac3948080af7da6699fe570dbccc3674acd..2a1cc01fe682c73b8272315ec6de46c37afc8289 100644
--- a/spec/features/projects/files/creating_a_file_spec.rb
+++ b/spec/features/projects/files/creating_a_file_spec.rb
@@ -6,7 +6,7 @@ feature 'User wants to create a file', feature: true do
 
   background do
     project.team << [user, :master]
-    login_as user
+    gitlab_sign_in user
     visit namespace_project_new_blob_path(project.namespace, project, project.default_branch)
   end
 
diff --git a/spec/features/projects/files/dockerfile_dropdown_spec.rb b/spec/features/projects/files/dockerfile_dropdown_spec.rb
index 93909e91d05372d64168fcc6a14b865177e679f7..4f1b858846225b26fd4ea6c519131531b42783ae 100644
--- a/spec/features/projects/files/dockerfile_dropdown_spec.rb
+++ b/spec/features/projects/files/dockerfile_dropdown_spec.rb
@@ -7,7 +7,7 @@ feature 'User wants to add a Dockerfile file', feature: true do
     project = create(:project)
     project.team << [user, :master]
 
-    login_as user
+    gitlab_sign_in user
 
     visit namespace_project_new_blob_path(project.namespace, project, 'master', file_name: 'Dockerfile')
   end
diff --git a/spec/features/projects/files/download_buttons_spec.rb b/spec/features/projects/files/download_buttons_spec.rb
index d7c29a7e07449cc29cbd795f41d52d02ca9f33ac..60182bfebe91af8f1b5937386a50814cd77eeb81 100644
--- a/spec/features/projects/files/download_buttons_spec.rb
+++ b/spec/features/projects/files/download_buttons_spec.rb
@@ -22,7 +22,7 @@ feature 'Download buttons in files tree', feature: true do
   end
 
   background do
-    login_as(user)
+    gitlab_sign_in(user)
     project.team << [user, role]
   end
 
diff --git a/spec/features/projects/files/edit_file_soft_wrap_spec.rb b/spec/features/projects/files/edit_file_soft_wrap_spec.rb
index 012befa7990a55cef0c5bff40d3a88a044687ffd..6e361ac43129760a794eedeaee653bb6e98bd3ee 100644
--- a/spec/features/projects/files/edit_file_soft_wrap_spec.rb
+++ b/spec/features/projects/files/edit_file_soft_wrap_spec.rb
@@ -5,7 +5,7 @@ feature 'User uses soft wrap whilst editing file', feature: true, js: true do
     user = create(:user)
     project = create(:project)
     project.team << [user, :master]
-    login_as user
+    gitlab_sign_in user
     visit namespace_project_new_blob_path(project.namespace, project, 'master', file_name: 'test_file-name')
     editor = find('.file-editor.code')
     editor.click
diff --git a/spec/features/projects/files/editing_a_file_spec.rb b/spec/features/projects/files/editing_a_file_spec.rb
index 7a3afafec29e7a7734e051a777d60cf4fbc143fe..e97ff5fded76e382ef532c6a51cd18f002193d22 100644
--- a/spec/features/projects/files/editing_a_file_spec.rb
+++ b/spec/features/projects/files/editing_a_file_spec.rb
@@ -17,7 +17,7 @@ feature 'User wants to edit a file', feature: true do
 
   background do
     project.team << [user, :master]
-    login_as user
+    gitlab_sign_in user
     visit namespace_project_edit_blob_path(project.namespace, project,
                                            File.join(project.default_branch, '.gitignore'))
   end
diff --git a/spec/features/projects/files/files_sort_submodules_with_folders_spec.rb b/spec/features/projects/files/files_sort_submodules_with_folders_spec.rb
index 5c8105de4cb3f1f31a06914f93e4dc5d9ced565e..83a837fba4471aef3e75642c09d16d04fe8e7c35 100644
--- a/spec/features/projects/files/files_sort_submodules_with_folders_spec.rb
+++ b/spec/features/projects/files/files_sort_submodules_with_folders_spec.rb
@@ -6,7 +6,7 @@ feature 'User views files page', feature: true do
 
   before do
     project.team << [user, :master]
-    login_as user
+    gitlab_sign_in user
     visit namespace_project_tree_path(project.namespace, project, project.repository.root_ref)
   end
 
diff --git a/spec/features/projects/files/find_file_keyboard_spec.rb b/spec/features/projects/files/find_file_keyboard_spec.rb
index ee42bcaec4b6d3245c57480534fe105f40232045..6a914820ac94468c2e9c601036fc28d00f932c0e 100644
--- a/spec/features/projects/files/find_file_keyboard_spec.rb
+++ b/spec/features/projects/files/find_file_keyboard_spec.rb
@@ -6,7 +6,7 @@ feature 'Find file keyboard shortcuts', feature: true, js: true do
 
   before do
     project.team << [user, :master]
-    login_as user
+    gitlab_sign_in user
 
     visit namespace_project_find_file_path(project.namespace, project, project.repository.root_ref)
 
diff --git a/spec/features/projects/files/find_files_spec.rb b/spec/features/projects/files/find_files_spec.rb
index 716b7591b950a1f413781d8a39134de616bc4b90..166ec5c921b025d792798979de3cb223675f96f5 100644
--- a/spec/features/projects/files/find_files_spec.rb
+++ b/spec/features/projects/files/find_files_spec.rb
@@ -5,7 +5,7 @@ feature 'Find files button in the tree header', feature: true do
   given(:project) { create(:project) }
 
   background do
-    login_as(user)
+    gitlab_sign_in(user)
     project.team << [user, :developer]
   end
 
diff --git a/spec/features/projects/files/gitignore_dropdown_spec.rb b/spec/features/projects/files/gitignore_dropdown_spec.rb
index e9f49453121771f84008e07e201e9e687cc1e218..7f02ec6b73d5800e52595c8d3c6cd1d186ce8ad1 100644
--- a/spec/features/projects/files/gitignore_dropdown_spec.rb
+++ b/spec/features/projects/files/gitignore_dropdown_spec.rb
@@ -5,7 +5,7 @@ feature 'User wants to add a .gitignore file', feature: true do
     user = create(:user)
     project = create(:project)
     project.team << [user, :master]
-    login_as user
+    gitlab_sign_in user
     visit namespace_project_new_blob_path(project.namespace, project, 'master', file_name: '.gitignore')
   end
 
diff --git a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb
index 031b89d04996fab5a498092518949d0f84d57e80..f4b17c2518c2f1334365026444b84ca7df4d931e 100644
--- a/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb
+++ b/spec/features/projects/files/gitlab_ci_yml_dropdown_spec.rb
@@ -5,7 +5,7 @@ feature 'User wants to add a .gitlab-ci.yml file', feature: true do
     user = create(:user)
     project = create(:project)
     project.team << [user, :master]
-    login_as user
+    gitlab_sign_in user
     visit namespace_project_new_blob_path(project.namespace, project, 'master', file_name: '.gitlab-ci.yml')
   end
 
diff --git a/spec/features/projects/files/project_owner_creates_license_file_spec.rb b/spec/features/projects/files/project_owner_creates_license_file_spec.rb
index 8d410cc3f2e0eb7dc56613a4970050f85b8685b7..7daf016dd223cb0dbb1bc8d2cf5dc07c3533c4c2 100644
--- a/spec/features/projects/files/project_owner_creates_license_file_spec.rb
+++ b/spec/features/projects/files/project_owner_creates_license_file_spec.rb
@@ -7,7 +7,7 @@ feature 'project owner creates a license file', feature: true, js: true do
     project.repository.delete_file(project_master, 'LICENSE',
       message: 'Remove LICENSE', branch_name: 'master')
     project.team << [project_master, :master]
-    login_as(project_master)
+    gitlab_sign_in(project_master)
     visit namespace_project_path(project.namespace, project)
   end
 
diff --git a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
index 8e197bccabf521c75d8bbd7ffed8eece8ed18fbf..eab19d520300d91dee49151018768e9ff19d3469 100644
--- a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
+++ b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
@@ -5,7 +5,7 @@ feature 'project owner sees a link to create a license file in empty project', f
   let(:project) { create(:empty_project) }
   background do
     project.team << [project_master, :master]
-    login_as(project_master)
+    gitlab_sign_in(project_master)
   end
 
   scenario 'project master creates a license file from a template' do
diff --git a/spec/features/projects/files/template_type_dropdown_spec.rb b/spec/features/projects/files/template_type_dropdown_spec.rb
index 9fcf12e6cb91c983371700c677815ad842008065..028a0919640f0c14b0a6d28cc3941218dc9c9181 100644
--- a/spec/features/projects/files/template_type_dropdown_spec.rb
+++ b/spec/features/projects/files/template_type_dropdown_spec.rb
@@ -6,7 +6,7 @@ feature 'Template type dropdown selector', js: true do
 
   before do
     project.team << [user, :master]
-    login_as user
+    gitlab_sign_in user
   end
 
   context 'editing a non-matching file' do
diff --git a/spec/features/projects/files/undo_template_spec.rb b/spec/features/projects/files/undo_template_spec.rb
index de10eec05577489f6d68215380319a7347536e02..4ccd123f46e4ebf8d8ff7e81d644841cb7b42983 100644
--- a/spec/features/projects/files/undo_template_spec.rb
+++ b/spec/features/projects/files/undo_template_spec.rb
@@ -6,7 +6,7 @@ feature 'Template Undo Button', js: true do
 
   before do
     project.team << [user, :master]
-    login_as user
+    gitlab_sign_in user
   end
 
   context 'editing a matching file and applying a template' do
diff --git a/spec/features/projects/gfm_autocomplete_load_spec.rb b/spec/features/projects/gfm_autocomplete_load_spec.rb
index 67bc9142356fc2b4ccf154dd5772408856483bcd..aa4ed217a34dbf71664bbd203b27da2b291c8206 100644
--- a/spec/features/projects/gfm_autocomplete_load_spec.rb
+++ b/spec/features/projects/gfm_autocomplete_load_spec.rb
@@ -4,7 +4,7 @@ describe 'GFM autocomplete loading', feature: true, js: true do
   let(:project)   { create(:project) }
 
   before do
-    login_as :admin
+    gitlab_sign_in :admin
 
     visit namespace_project_path(project.namespace, project)
   end
diff --git a/spec/features/projects/group_links_spec.rb b/spec/features/projects/group_links_spec.rb
index 1b680a56492a02e446bb2741a7bacb5e2d243244..778f5d61ae37da6c0af75f215707a2f6794005fa 100644
--- a/spec/features/projects/group_links_spec.rb
+++ b/spec/features/projects/group_links_spec.rb
@@ -9,7 +9,7 @@ feature 'Project group links', :feature, :js do
 
   background do
     project.add_master(master)
-    login_as(master)
+    gitlab_sign_in(master)
   end
 
   context 'setting an expiration date for a group link' do
diff --git a/spec/features/projects/guest_navigation_menu_spec.rb b/spec/features/projects/guest_navigation_menu_spec.rb
index b91c3eff478a80579f00e439dfbfeb635238afdf..e1f7f06c113af9866669e0bd17636e4c81d15793 100644
--- a/spec/features/projects/guest_navigation_menu_spec.rb
+++ b/spec/features/projects/guest_navigation_menu_spec.rb
@@ -7,7 +7,7 @@ describe 'Guest navigation menu' do
   before do
     project.team << [guest, :guest]
 
-    login_as(guest)
+    gitlab_sign_in(guest)
   end
 
   it 'shows allowed tabs only' do
diff --git a/spec/features/projects/import_export/export_file_spec.rb b/spec/features/projects/import_export/export_file_spec.rb
index 40caf89dd54c4bdce4b18c3f7ee9fba0455c6867..b5c647779342f7923027b1882c91721c14732ea6 100644
--- a/spec/features/projects/import_export/export_file_spec.rb
+++ b/spec/features/projects/import_export/export_file_spec.rb
@@ -33,7 +33,7 @@ feature 'Import/Export - project export integration test', feature: true, js: tr
 
   context 'admin user' do
     before do
-      login_as(user)
+      gitlab_sign_in(user)
     end
 
     scenario 'exports a project successfully' do
diff --git a/spec/features/projects/import_export/import_file_spec.rb b/spec/features/projects/import_export/import_file_spec.rb
index 583f479ec1824275c0a2ad6c060a2c2222c1c64f..a111aa87c52c41086bf9c05f1d653e07c79ebc43 100644
--- a/spec/features/projects/import_export/import_file_spec.rb
+++ b/spec/features/projects/import_export/import_file_spec.rb
@@ -19,7 +19,7 @@ feature 'Import/Export - project import integration test', feature: true, js: tr
     let!(:namespace) { create(:namespace, name: "asd", owner: user) }
 
     before do
-      login_as(user)
+      gitlab_sign_in(user)
     end
 
     scenario 'user imports an exported project successfully' do
@@ -77,7 +77,7 @@ feature 'Import/Export - project import integration test', feature: true, js: tr
   context 'when limited to the default user namespace' do
     let(:user) { create(:user) }
     before do
-      login_as(user)
+      gitlab_sign_in(user)
     end
 
     scenario 'passes correct namespace ID in the URL' do
diff --git a/spec/features/projects/import_export/namespace_export_file_spec.rb b/spec/features/projects/import_export/namespace_export_file_spec.rb
index cb399ea55df4c46b6c770a8f25f210b900046881..b0a68f0d61f62e12a787ef403a76abdcc6b2b648 100644
--- a/spec/features/projects/import_export/namespace_export_file_spec.rb
+++ b/spec/features/projects/import_export/namespace_export_file_spec.rb
@@ -16,7 +16,7 @@ feature 'Import/Export - Namespace export file cleanup', feature: true, js: true
 
   context 'admin user' do
     before do
-      login_as(:admin)
+      gitlab_sign_in(:admin)
     end
 
     context 'moving the namespace' do
diff --git a/spec/features/projects/import_export/test_project_export.tar.gz b/spec/features/projects/import_export/test_project_export.tar.gz
index 4efd5a26a823ea7a93d6e1c1ce379a05b6171d47..e03e7b881742aab75ca001d26f5c5d4c14c1e040 100644
Binary files a/spec/features/projects/import_export/test_project_export.tar.gz and b/spec/features/projects/import_export/test_project_export.tar.gz differ
diff --git a/spec/features/projects/issuable_templates_spec.rb b/spec/features/projects/issuable_templates_spec.rb
index 3076c863dcb5455afb47be2df7a98cfa24291a27..26a09985312d6a1d254e370556058c79bf23072e 100644
--- a/spec/features/projects/issuable_templates_spec.rb
+++ b/spec/features/projects/issuable_templates_spec.rb
@@ -6,7 +6,7 @@ feature 'issuable templates', feature: true, js: true do
 
   before do
     project.team << [user, :master]
-    login_as user
+    gitlab_sign_in user
   end
 
   context 'user creates an issue using templates' do
@@ -124,11 +124,11 @@ feature 'issuable templates', feature: true, js: true do
     let(:merge_request) { create(:merge_request, :with_diffs, source_project: fork_project, target_project: project) }
 
     background do
-      logout
+      gitlab_sign_out
       project.team << [fork_user, :developer]
       fork_project.team << [fork_user, :master]
       create(:forked_project_link, forked_to_project: fork_project, forked_from_project: project)
-      login_as fork_user
+      gitlab_sign_in fork_user
       project.repository.create_file(
         fork_user,
         '.gitlab/merge_request_templates/feature-proposal.md',
diff --git a/spec/features/projects/issues/list_spec.rb b/spec/features/projects/issues/list_spec.rb
index 3137af074cac5e791d83bdaffe10682025a2a54b..b2db07a75ef3d0e622f1210eb9a033ec4882f726 100644
--- a/spec/features/projects/issues/list_spec.rb
+++ b/spec/features/projects/issues/list_spec.rb
@@ -7,7 +7,7 @@ feature 'Issues List' do
   background do
     project.team << [user, :developer]
 
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   scenario 'user does not see create new list button' do
diff --git a/spec/features/projects/issues/rss_spec.rb b/spec/features/projects/issues/rss_spec.rb
index f6852192aef7e5e57e562cbff9591f2320fb37a0..38733d39932f378d125105dcbf9376eb17e9b689 100644
--- a/spec/features/projects/issues/rss_spec.rb
+++ b/spec/features/projects/issues/rss_spec.rb
@@ -12,7 +12,7 @@ feature 'Project Issues RSS' do
     before do
       user = create(:user)
       project.team << [user, :developer]
-      login_as(user)
+      gitlab_sign_in(user)
       visit path
     end
 
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index 31c93c75d25ceafa5d7c3fcb2c5c900db3b7eebd..070cdbf1ceffce0f0e0f945f09658c342329afb3 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -17,7 +17,7 @@ feature 'Jobs', :feature do
 
   before do
     project.team << [user, user_access_level]
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   describe "GET /:project/jobs" do
@@ -392,8 +392,8 @@ feature 'Jobs', :feature do
         job.cancel!
         project.update(visibility_level: Gitlab::VisibilityLevel::PUBLIC)
 
-        logout_direct
-        login_with(create(:user))
+        gitlab_sign_out_direct
+        gitlab_sign_in(create(:user))
         visit namespace_project_job_path(project.namespace, project, job)
       end
 
diff --git a/spec/features/projects/labels/issues_sorted_by_priority_spec.rb b/spec/features/projects/labels/issues_sorted_by_priority_spec.rb
index e2911a37e408c46ca8e02bcb69d78985e4f6baf0..2c47758f30ea01643b83eb8c6b40db79f787b38f 100644
--- a/spec/features/projects/labels/issues_sorted_by_priority_spec.rb
+++ b/spec/features/projects/labels/issues_sorted_by_priority_spec.rb
@@ -28,7 +28,7 @@ feature 'Issue prioritization', feature: true do
       issue_2.labels << label_4
       issue_1.labels << label_5
 
-      login_as user
+      gitlab_sign_in user
       visit namespace_project_issues_path(project.namespace, project, sort: 'label_priority')
 
       # Ensure we are indicating that issues are sorted by priority
@@ -67,7 +67,7 @@ feature 'Issue prioritization', feature: true do
       issue_4.labels << label_4 # 7
       issue_6.labels << label_5 # 8 - No priority
 
-      login_as user
+      gitlab_sign_in user
       visit namespace_project_issues_path(project.namespace, project, sort: 'label_priority')
 
       expect(page).to have_selector('.dropdown-toggle', text: 'Label priority')
diff --git a/spec/features/projects/labels/subscription_spec.rb b/spec/features/projects/labels/subscription_spec.rb
index 3130d87fba5abbd44fe642f54713c7ccd160961e..584dc294f05c4fada4f24a4867d61a33c1009e59 100644
--- a/spec/features/projects/labels/subscription_spec.rb
+++ b/spec/features/projects/labels/subscription_spec.rb
@@ -10,7 +10,7 @@ feature 'Labels subscription', feature: true do
   context 'when signed in' do
     before do
       project.team << [user, :developer]
-      login_as user
+      gitlab_sign_in user
     end
 
     scenario 'users can subscribe/unsubscribe to labels', js: true do
diff --git a/spec/features/projects/labels/update_prioritization_spec.rb b/spec/features/projects/labels/update_prioritization_spec.rb
index 34fafe072a39ea72abf0d4968f7e3a156ff546a8..589bfb9fbc9773afc96575b2791b04d1e8c9998c 100644
--- a/spec/features/projects/labels/update_prioritization_spec.rb
+++ b/spec/features/projects/labels/update_prioritization_spec.rb
@@ -14,7 +14,7 @@ feature 'Prioritize labels', feature: true do
     before do
       project.team << [user, :developer]
 
-      login_as user
+      gitlab_sign_in user
     end
 
     scenario 'user can prioritize a group label', js: true do
@@ -120,7 +120,7 @@ feature 'Prioritize labels', feature: true do
     it 'does not prioritize labels' do
       guest = create(:user)
 
-      login_as guest
+      gitlab_sign_in guest
 
       visit namespace_project_labels_path(project.namespace, project)
 
diff --git a/spec/features/projects/main/download_buttons_spec.rb b/spec/features/projects/main/download_buttons_spec.rb
index 02198ff3e419f0058c796886d3c6dbabb2a65067..514453db47245645e4566e753a3a4b57e5f3db46 100644
--- a/spec/features/projects/main/download_buttons_spec.rb
+++ b/spec/features/projects/main/download_buttons_spec.rb
@@ -22,7 +22,7 @@ feature 'Download buttons in project main page', feature: true do
   end
 
   background do
-    login_as(user)
+    gitlab_sign_in(user)
     project.team << [user, role]
   end
 
diff --git a/spec/features/projects/main/rss_spec.rb b/spec/features/projects/main/rss_spec.rb
index 53966229a2a28f0a81f22b75fe05788ff81ac328..fee8cfe2c33f2f2385a03ee8346bb50fbda275a0 100644
--- a/spec/features/projects/main/rss_spec.rb
+++ b/spec/features/projects/main/rss_spec.rb
@@ -8,7 +8,7 @@ feature 'Project RSS' do
     before do
       user = create(:user)
       project.team << [user, :developer]
-      login_as(user)
+      gitlab_sign_in(user)
       visit path
     end
 
diff --git a/spec/features/projects/members/group_links_spec.rb b/spec/features/projects/members/group_links_spec.rb
index 3d253f014840765d6c0c1449849be415365601af..00d2a27597bab2cf2efd4130724acf0dfbbd7c96 100644
--- a/spec/features/projects/members/group_links_spec.rb
+++ b/spec/features/projects/members/group_links_spec.rb
@@ -9,7 +9,7 @@ feature 'Projects > Members > Anonymous user sees members', feature: true, js: t
     project.team << [user, :master]
     @group_link = create(:project_group_link, project: project, group: group)
 
-    login_as(user)
+    gitlab_sign_in(user)
     visit namespace_project_settings_members_path(project.namespace, project)
   end
 
diff --git a/spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb b/spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb
index b483ba4c54c6909ba58e5868a99367ceea8e0863..7e71dbc24c08a333cfcdba92b229b6ccd23b9d90 100644
--- a/spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb
+++ b/spec/features/projects/members/group_member_cannot_leave_group_project_spec.rb
@@ -7,7 +7,7 @@ feature 'Projects > Members > Group member cannot leave group project', feature:
 
   background do
     group.add_developer(user)
-    login_as(user)
+    gitlab_sign_in(user)
     visit namespace_project_path(project.namespace, project)
   end
 
diff --git a/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb b/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb
index ff9b60078066a98d6bb4e23469d20e40c964fd91..60a5cd9ec63d449f9d2068da5787c7db4b851dae 100644
--- a/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb
+++ b/spec/features/projects/members/group_member_cannot_request_access_to_his_group_project_spec.rb
@@ -41,7 +41,7 @@ feature 'Projects > Members > Group member cannot request access to his group pr
   end
 
   def login_and_visit_project_page(user)
-    login_as(user)
+    gitlab_sign_in(user)
     visit namespace_project_path(project.namespace, project)
   end
 end
diff --git a/spec/features/projects/members/group_members_spec.rb b/spec/features/projects/members/group_members_spec.rb
index 3385e5972ff7438442263ce56e08cc403ca4bf0e..76fe6a00dabcfe7365b3715aa6c726a1ace21d9b 100644
--- a/spec/features/projects/members/group_members_spec.rb
+++ b/spec/features/projects/members/group_members_spec.rb
@@ -13,7 +13,7 @@ feature 'Projects members', feature: true do
   background do
     project.team << [developer, :developer]
     group.add_owner(user)
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   context 'with a group invitee' do
diff --git a/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb b/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb
index bdeeef572730d272eef97a549b77dda4fd159461..66da28b07fe2abdc273e62e7308758afd5c766e4 100644
--- a/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb
+++ b/spec/features/projects/members/group_requester_cannot_request_access_to_project_spec.rb
@@ -8,7 +8,7 @@ feature 'Projects > Members > Group requester cannot request access to project',
 
   background do
     group.add_owner(owner)
-    login_as(user)
+    gitlab_sign_in(user)
     visit group_path(group)
     perform_enqueued_jobs { click_link 'Request Access' }
     visit namespace_project_path(project.namespace, project)
diff --git a/spec/features/projects/members/list_spec.rb b/spec/features/projects/members/list_spec.rb
index deea34214fb46f7d325e0264d908c70b53a33bde..9fdd7df0ee54ac3913d7e62c4e3e2461e6910a84 100644
--- a/spec/features/projects/members/list_spec.rb
+++ b/spec/features/projects/members/list_spec.rb
@@ -9,7 +9,7 @@ feature 'Project members list', feature: true do
   let(:project) { create(:project, namespace: group) }
 
   background do
-    login_as(user1)
+    gitlab_sign_in(user1)
     group.add_owner(user1)
   end
 
diff --git a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
index 1e6f15d8258b394ee3ea5666df8e14807c1e174f..21b48b7fdd19e83667c3a91952eb2966722946bb 100644
--- a/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
+++ b/spec/features/projects/members/master_adds_member_with_expiration_date_spec.rb
@@ -10,7 +10,7 @@ feature 'Projects > Members > Master adds member with expiration date', feature:
 
   background do
     project.team << [master, :master]
-    login_as(master)
+    gitlab_sign_in(master)
   end
 
   scenario 'expiration date is displayed in the members list' do
diff --git a/spec/features/projects/members/master_manages_access_requests_spec.rb b/spec/features/projects/members/master_manages_access_requests_spec.rb
index 143390b71cdb8fd4a1f85535d26b41407ad4e075..bd445e27243a136435340ac2799a3e5be6577019 100644
--- a/spec/features/projects/members/master_manages_access_requests_spec.rb
+++ b/spec/features/projects/members/master_manages_access_requests_spec.rb
@@ -8,7 +8,7 @@ feature 'Projects > Members > Master manages access requests', feature: true do
   background do
     project.request_access(user)
     project.team << [master, :master]
-    login_as(master)
+    gitlab_sign_in(master)
   end
 
   scenario 'master can see access requests' do
diff --git a/spec/features/projects/members/member_cannot_request_access_to_his_project_spec.rb b/spec/features/projects/members/member_cannot_request_access_to_his_project_spec.rb
index 9564347e7335c1785559b9cd8a371a6e037728e4..703f5dff6b56e29b2569568f5fd036b7f20c8dfa 100644
--- a/spec/features/projects/members/member_cannot_request_access_to_his_project_spec.rb
+++ b/spec/features/projects/members/member_cannot_request_access_to_his_project_spec.rb
@@ -6,7 +6,7 @@ feature 'Projects > Members > Member cannot request access to his project', feat
 
   background do
     project.team << [member, :developer]
-    login_as(member)
+    gitlab_sign_in(member)
     visit namespace_project_path(project.namespace, project)
   end
 
diff --git a/spec/features/projects/members/member_leaves_project_spec.rb b/spec/features/projects/members/member_leaves_project_spec.rb
index 5daa932e4e6e52481f2f01f45eca77d74c823ba3..8e1788f7f2adc266e8e3cc2d145aafc006e1da6e 100644
--- a/spec/features/projects/members/member_leaves_project_spec.rb
+++ b/spec/features/projects/members/member_leaves_project_spec.rb
@@ -6,7 +6,7 @@ feature 'Projects > Members > Member leaves project', feature: true do
 
   background do
     project.team << [user, :developer]
-    login_as(user)
+    gitlab_sign_in(user)
     visit namespace_project_path(project.namespace, project)
   end
 
diff --git a/spec/features/projects/members/owner_cannot_leave_project_spec.rb b/spec/features/projects/members/owner_cannot_leave_project_spec.rb
index b26d55c5d5d186aca1f7a0c2c2ae4a0fea6af00f..70e4bb19c0f81d9784f16f059120415fc298a452 100644
--- a/spec/features/projects/members/owner_cannot_leave_project_spec.rb
+++ b/spec/features/projects/members/owner_cannot_leave_project_spec.rb
@@ -4,7 +4,7 @@ feature 'Projects > Members > Owner cannot leave project', feature: true do
   let(:project) { create(:project) }
 
   background do
-    login_as(project.owner)
+    gitlab_sign_in(project.owner)
     visit namespace_project_path(project.namespace, project)
   end
 
diff --git a/spec/features/projects/members/owner_cannot_request_access_to_his_project_spec.rb b/spec/features/projects/members/owner_cannot_request_access_to_his_project_spec.rb
index 4ca9272b9c10736f4584ea23c280b79bc898a26a..0cd7e3afedafe1b1e6f575f60b9d90efa31a8dff 100644
--- a/spec/features/projects/members/owner_cannot_request_access_to_his_project_spec.rb
+++ b/spec/features/projects/members/owner_cannot_request_access_to_his_project_spec.rb
@@ -4,7 +4,7 @@ feature 'Projects > Members > Owner cannot request access to his project', featu
   let(:project) { create(:project) }
 
   background do
-    login_as(project.owner)
+    gitlab_sign_in(project.owner)
     visit namespace_project_path(project.namespace, project)
   end
 
diff --git a/spec/features/projects/members/sorting_spec.rb b/spec/features/projects/members/sorting_spec.rb
index d428f6fcf2237941810e8228d1b7f229ad5cd95f..66d98ef8b9081348ae878ca5390915c5b6541457 100644
--- a/spec/features/projects/members/sorting_spec.rb
+++ b/spec/features/projects/members/sorting_spec.rb
@@ -8,7 +8,7 @@ feature 'Projects > Members > Sorting', feature: true do
   background do
     create(:project_member, :developer, user: developer, project: project, created_at: 3.days.ago)
 
-    login_as(master)
+    gitlab_sign_in(master)
   end
 
   scenario 'sorts alphabetically by default' do
diff --git a/spec/features/projects/members/user_requests_access_spec.rb b/spec/features/projects/members/user_requests_access_spec.rb
index ec48a4bd726ab29114780ff4285419ecd767a876..081009f232577a94ae50a05cca2f437ca2d30b3e 100644
--- a/spec/features/projects/members/user_requests_access_spec.rb
+++ b/spec/features/projects/members/user_requests_access_spec.rb
@@ -6,7 +6,7 @@ feature 'Projects > Members > User requests access', feature: true do
   let(:master) { project.owner }
 
   background do
-    login_as(user)
+    gitlab_sign_in(user)
     visit namespace_project_path(project.namespace, project)
   end
 
diff --git a/spec/features/projects/merge_request_button_spec.rb b/spec/features/projects/merge_request_button_spec.rb
index 1370ab1c5217b4b2121987ef1ec840e9c96bc72b..6de8855016d0b152af8eac28f0e114648489b8e4 100644
--- a/spec/features/projects/merge_request_button_spec.rb
+++ b/spec/features/projects/merge_request_button_spec.rb
@@ -18,7 +18,7 @@ feature 'Merge Request button', feature: true do
 
     context 'logged in as developer' do
       before do
-        login_as(user)
+        gitlab_sign_in(user)
         project.team << [user, :developer]
       end
 
@@ -52,7 +52,7 @@ feature 'Merge Request button', feature: true do
 
     context 'logged in as non-member' do
       before do
-        login_as(user)
+        gitlab_sign_in(user)
       end
 
       it 'does not show Create merge request button' do
diff --git a/spec/features/projects/merge_requests/list_spec.rb b/spec/features/projects/merge_requests/list_spec.rb
index 7e8a796c55d3c6c7cce01ec61471daf1de997d0b..f2a2fd0311ff17945a7403b8f646c561759cdc09 100644
--- a/spec/features/projects/merge_requests/list_spec.rb
+++ b/spec/features/projects/merge_requests/list_spec.rb
@@ -7,7 +7,7 @@ feature 'Merge Requests List' do
   background do
     project.team << [user, :developer]
 
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   scenario 'user does not see create new list button' do
diff --git a/spec/features/projects/milestones/milestone_spec.rb b/spec/features/projects/milestones/milestone_spec.rb
index b4fc0edbde822b81001546eb0105d71bf5947d33..a02e4118784e4fa1774684740273a82440d07908 100644
--- a/spec/features/projects/milestones/milestone_spec.rb
+++ b/spec/features/projects/milestones/milestone_spec.rb
@@ -6,7 +6,7 @@ feature 'Project milestone', :feature do
   let(:milestone) { create(:milestone, project: project) }
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   context 'when project has enabled issues' do
diff --git a/spec/features/projects/milestones/milestones_sorting_spec.rb b/spec/features/projects/milestones/milestones_sorting_spec.rb
index da3eaed707a85b83d9c5d130835698c477728d7b..2350089255dd0a25149fa560a301960aee99d3b4 100644
--- a/spec/features/projects/milestones/milestones_sorting_spec.rb
+++ b/spec/features/projects/milestones/milestones_sorting_spec.rb
@@ -15,7 +15,7 @@ feature 'Milestones sorting', :feature, :js do
       due_date: 11.days.from_now,
       created_at:  1.hour.ago,
       title: "bbb", project: project)
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   scenario 'visit project milestones and sort by due_date_asc' do
diff --git a/spec/features/projects/milestones/new_spec.rb b/spec/features/projects/milestones/new_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7403822c7fbb5dfa1cd721bff44b9e4630783b5a
--- /dev/null
+++ b/spec/features/projects/milestones/new_spec.rb
@@ -0,0 +1,18 @@
+require 'spec_helper'
+
+feature 'Creating a new project milestone', :feature, :js do
+  let(:user) { create(:user) }
+  let(:project) { create(:empty_project, name: 'test', namespace: user.namespace) }
+
+  before do
+    login_as(user)
+    visit new_namespace_project_milestone_path(project.namespace, project)
+  end
+
+  it 'description has autocomplete' do
+    find('#milestone_description').native.send_keys('')
+    fill_in 'milestone_description', with: '@'
+
+    expect(page).to have_selector('.atwho-view')
+  end
+end
diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb
index b1f9eb1566787b002a10a23729e22a0389316474..37d9a97033bca85b9229ab51e384f54bc8d22e00 100644
--- a/spec/features/projects/new_project_spec.rb
+++ b/spec/features/projects/new_project_spec.rb
@@ -4,7 +4,7 @@ feature "New project", feature: true do
   let(:user) { create(:admin) }
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   context "Visibility level selector" do
diff --git a/spec/features/projects/pages_spec.rb b/spec/features/projects/pages_spec.rb
index 11793c0f3037f1b3bee3677fceffc96ba30d563d..e9a3cfb7f6031e3800b011c879164c4d4d82c839 100644
--- a/spec/features/projects/pages_spec.rb
+++ b/spec/features/projects/pages_spec.rb
@@ -10,7 +10,7 @@ feature 'Pages', feature: true do
 
     project.team << [user, role]
 
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   shared_examples 'no pages deployed' do
diff --git a/spec/features/projects/pipeline_schedules_spec.rb b/spec/features/projects/pipeline_schedules_spec.rb
index 2d43f7a10bc8ed93ef153394e97d8a444fbaf40e..dfb973c37e557519e405d96ae12a555df3f51d7a 100644
--- a/spec/features/projects/pipeline_schedules_spec.rb
+++ b/spec/features/projects/pipeline_schedules_spec.rb
@@ -12,7 +12,7 @@ feature 'Pipeline Schedules', :feature do
   before do
     project.add_master(user)
 
-    login_as(user)
+    gitlab_sign_in(user)
     visit_page
   end
 
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index 12c5ad45baf06ae196bc4259a4574723a281b011..e182995922d88f8dbffaabf0efd02267e41b1f5c 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -7,7 +7,7 @@ describe 'Pipeline', :feature, :js do
   let(:user) { create(:user) }
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
     project.team << [user, :developer]
   end
 
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index db2d1a100a573dc77a478fd468e18fa6b2240ef8..d36d073e02251b84efb5491bf7d3a3684340037a 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -7,7 +7,7 @@ describe 'Pipelines', :feature, :js do
     let(:user) { create(:user) }
 
     before do
-      login_as(user)
+      gitlab_sign_in(user)
       project.team << [user, :developer]
     end
 
diff --git a/spec/features/projects/project_settings_spec.rb b/spec/features/projects/project_settings_spec.rb
index 2a9b32ea07ea6f15df3fd2953325dc1d3debf112..baa38ff8cca20fe22dd8aa6e9d66d81a53207b0a 100644
--- a/spec/features/projects/project_settings_spec.rb
+++ b/spec/features/projects/project_settings_spec.rb
@@ -7,7 +7,7 @@ describe 'Edit Project Settings', feature: true do
   let(:project) { create(:empty_project, namespace: user.namespace, path: 'gitlab', name: 'sample') }
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   describe 'Project settings section', js: true do
diff --git a/spec/features/projects/ref_switcher_spec.rb b/spec/features/projects/ref_switcher_spec.rb
index 04414490571b14eae2a1ff0e0c82229ae30e5eab..016a992bdcfb4c043d39697e1172fbea3a11219b 100644
--- a/spec/features/projects/ref_switcher_spec.rb
+++ b/spec/features/projects/ref_switcher_spec.rb
@@ -6,7 +6,7 @@ feature 'Ref switcher', feature: true, js: true do
 
   before do
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
     visit namespace_project_tree_path(project.namespace, project, 'master')
   end
 
diff --git a/spec/features/projects/services/jira_service_spec.rb b/spec/features/projects/services/jira_service_spec.rb
index c96d87e57083977c5da3455c55fb354b59428aeb..2ea50e8f6723fc96ed5cf31029107595df80d6d7 100644
--- a/spec/features/projects/services/jira_service_spec.rb
+++ b/spec/features/projects/services/jira_service_spec.rb
@@ -20,7 +20,7 @@ feature 'Setup Jira service', :feature, :js do
 
   before do
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
 
     visit namespace_project_settings_integrations_path(project.namespace, project)
   end
diff --git a/spec/features/projects/services/mattermost_slash_command_spec.rb b/spec/features/projects/services/mattermost_slash_command_spec.rb
index 1fe82222e59ba333ce1368a3e57a7b84155ef76e..d87985f1c92215b1b058cc2fe08660710b5f65d8 100644
--- a/spec/features/projects/services/mattermost_slash_command_spec.rb
+++ b/spec/features/projects/services/mattermost_slash_command_spec.rb
@@ -9,7 +9,7 @@ feature 'Setup Mattermost slash commands', :feature, :js do
   before do
     stub_mattermost_setting(enabled: mattermost_enabled)
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
     visit edit_namespace_project_service_path(project.namespace, project, service)
   end
 
diff --git a/spec/features/projects/services/slack_service_spec.rb b/spec/features/projects/services/slack_service_spec.rb
index c0a4a1e4bf5196e14d18a012c211c7007c701722..50707e6a49fe9a18cd720d1157c51d20493f5b0c 100644
--- a/spec/features/projects/services/slack_service_spec.rb
+++ b/spec/features/projects/services/slack_service_spec.rb
@@ -9,7 +9,7 @@ feature 'Projects > Slack service > Setup events', feature: true do
     service.fields
     service.update_attributes(push_channel: 1, issue_channel: 2, merge_request_channel: 3, note_channel: 4, tag_push_channel: 5, pipeline_channel: 6, wiki_page_channel: 7)
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   scenario 'user can filter events by channel' do
diff --git a/spec/features/projects/services/slack_slash_command_spec.rb b/spec/features/projects/services/slack_slash_command_spec.rb
index f53b820c46062d95a53c66e45c50047aafa903a7..3fae38c1799a14442c4c4495d4da0628108f96ce 100644
--- a/spec/features/projects/services/slack_slash_command_spec.rb
+++ b/spec/features/projects/services/slack_slash_command_spec.rb
@@ -7,7 +7,7 @@ feature 'Slack slash commands', feature: true do
 
   background do
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
     visit edit_namespace_project_service_path(project.namespace, project, service)
   end
 
diff --git a/spec/features/projects/settings/integration_settings_spec.rb b/spec/features/projects/settings/integration_settings_spec.rb
index fbaea14a2bedd46a15794dd8c75c728e92c4591b..a59374b37eab7d5c1e00ba228db986ac78fcb00d 100644
--- a/spec/features/projects/settings/integration_settings_spec.rb
+++ b/spec/features/projects/settings/integration_settings_spec.rb
@@ -7,7 +7,7 @@ feature 'Integration settings', feature: true do
   let(:integrations_path) { namespace_project_settings_integrations_path(project.namespace, project) }
 
   background do
-    login_as(user)
+    gitlab_sign_in(user)
     project.team << [user, role]
   end
 
diff --git a/spec/features/projects/settings/merge_requests_settings_spec.rb b/spec/features/projects/settings/merge_requests_settings_spec.rb
index 321af416c9146a32ff6c47c718e3c5592119f24e..f2af14ceab290499f15e3d42afa74111200f20a5 100644
--- a/spec/features/projects/settings/merge_requests_settings_spec.rb
+++ b/spec/features/projects/settings/merge_requests_settings_spec.rb
@@ -8,7 +8,7 @@ feature 'Project settings > Merge Requests', feature: true, js: true do
 
   background do
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   context 'when Merge Request and Pipelines are initially enabled' do
diff --git a/spec/features/projects/settings/pipelines_settings_spec.rb b/spec/features/projects/settings/pipelines_settings_spec.rb
index 035c57eaa47e1d14b72170ebf74401aef6053421..c33fbd49d21994f0375c43a257234e1180bdffa0 100644
--- a/spec/features/projects/settings/pipelines_settings_spec.rb
+++ b/spec/features/projects/settings/pipelines_settings_spec.rb
@@ -8,7 +8,7 @@ feature "Pipelines settings", feature: true do
   let(:role) { :developer }
 
   background do
-    login_as(user)
+    gitlab_sign_in(user)
     project.team << [user, role]
     visit namespace_project_pipelines_settings_path(project.namespace, project)
   end
diff --git a/spec/features/projects/settings/repository_settings_spec.rb b/spec/features/projects/settings/repository_settings_spec.rb
index 4cc38c5286e2d264d5199e4f41bf03ba16fe0df0..35cd0d6e83219da2b6bcde080d5a7a488e311bdb 100644
--- a/spec/features/projects/settings/repository_settings_spec.rb
+++ b/spec/features/projects/settings/repository_settings_spec.rb
@@ -7,7 +7,7 @@ feature 'Repository settings', feature: true do
 
   background do
     project.team << [user, role]
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   context 'for developer' do
@@ -65,6 +65,23 @@ feature 'Repository settings', feature: true do
         expect(page).to have_content('Write access allowed')
       end
 
+      scenario 'edit a deploy key from projects user has access to' do
+        project2 = create(:project_empty_repo)
+        project2.team << [user, role]
+        project2.deploy_keys << private_deploy_key
+
+        visit namespace_project_settings_repository_path(project.namespace, project)
+
+        find('li', text: private_deploy_key.title).click_link('Edit')
+
+        fill_in 'deploy_key_title', with: 'updated_deploy_key'
+        check 'deploy_key_can_push'
+        click_button 'Save changes'
+
+        expect(page).to have_content('updated_deploy_key')
+        expect(page).to have_content('Write access allowed')
+      end
+
       scenario 'remove an existing deploy key' do
         project.deploy_keys << private_deploy_key
         visit namespace_project_settings_repository_path(project.namespace, project)
diff --git a/spec/features/projects/settings/visibility_settings_spec.rb b/spec/features/projects/settings/visibility_settings_spec.rb
index fac4506bdf65cb3fd589706c52af09f8e409e573..18c71dee41b78830dfc5f15b482eb832dd10a8c9 100644
--- a/spec/features/projects/settings/visibility_settings_spec.rb
+++ b/spec/features/projects/settings/visibility_settings_spec.rb
@@ -6,7 +6,7 @@ feature 'Visibility settings', feature: true, js: true do
 
   context 'as owner' do
     before do
-      login_as(user)
+      gitlab_sign_in(user)
       visit edit_namespace_project_path(project.namespace, project)
     end
 
@@ -32,7 +32,7 @@ feature 'Visibility settings', feature: true, js: true do
 
     before do
       project.team << [master_user, :master]
-      login_as(master_user)
+      gitlab_sign_in(master_user)
       visit edit_namespace_project_path(project.namespace, project)
     end
 
diff --git a/spec/features/projects/shortcuts_spec.rb b/spec/features/projects/shortcuts_spec.rb
index 54aa9c66a08cf7ad14b781d4736826fca06398ad..cec79277c3366e38de21b3e3f761486074b6609a 100644
--- a/spec/features/projects/shortcuts_spec.rb
+++ b/spec/features/projects/shortcuts_spec.rb
@@ -7,7 +7,7 @@ feature 'Project shortcuts', feature: true do
   describe 'On a project', js: true do
     before do
       project.team << [user, :master]
-      login_as user
+      gitlab_sign_in user
       visit namespace_project_path(project.namespace, project)
     end
 
diff --git a/spec/features/projects/snippets/create_snippet_spec.rb b/spec/features/projects/snippets/create_snippet_spec.rb
index 5ac1ca45c741f4eb33a77d26cd1816d92c85d066..c75d6dbc30785eade1cfccd27addd62f03c8fb63 100644
--- a/spec/features/projects/snippets/create_snippet_spec.rb
+++ b/spec/features/projects/snippets/create_snippet_spec.rb
@@ -17,7 +17,7 @@ feature 'Create Snippet', :js, feature: true do
   context 'when a user is authenticated' do
     before do
       project.team << [user, :master]
-      login_as(user)
+      gitlab_sign_in(user)
 
       visit namespace_project_snippets_path(project.namespace, project)
 
diff --git a/spec/features/projects/snippets/show_spec.rb b/spec/features/projects/snippets/show_spec.rb
index b844e60e5d5c4cef15bafc162dcd6b8a4c63adb0..9e73ba4123bb776ffb457584b0c08ebe0e226e60 100644
--- a/spec/features/projects/snippets/show_spec.rb
+++ b/spec/features/projects/snippets/show_spec.rb
@@ -7,7 +7,7 @@ feature 'Project snippet', :js, feature: true do
 
   before do
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   context 'Ruby file' do
diff --git a/spec/features/projects/snippets_spec.rb b/spec/features/projects/snippets_spec.rb
index 18689c17fe93c7238bc3653e6e28aabdd6797835..80dbffaffc7f87bc1667967dfe37a6e4791001ec 100644
--- a/spec/features/projects/snippets_spec.rb
+++ b/spec/features/projects/snippets_spec.rb
@@ -1,6 +1,6 @@
 require 'spec_helper'
 
-describe 'Project snippets', feature: true do
+describe 'Project snippets', :js, feature: true do
   context 'when the project has snippets' do
     let(:project) { create(:empty_project, :public) }
     let!(:snippets) { create_list(:project_snippet, 2, :public, author: project.owner, project: project) }
@@ -26,5 +26,19 @@ describe 'Project snippets', feature: true do
         expect(page).to have_content(snippets[1].title)
       end
     end
+
+    context 'when submitting a note' do
+      before do
+        gitlab_sign_in :admin
+        visit namespace_project_snippet_path(project.namespace, project, snippets[0])
+      end
+
+      it 'should have autocomplete' do
+        find('#note_note').native.send_keys('')
+        fill_in 'note[note]', with: '@'
+
+        expect(page).to have_selector('.atwho-view')
+      end
+    end
   end
 end
diff --git a/spec/features/projects/sub_group_issuables_spec.rb b/spec/features/projects/sub_group_issuables_spec.rb
index e88907b801611216bc75a4d8dfb4a6ef3b002315..63eb97d5a927f715b2edff0c804fe60873150ed0 100644
--- a/spec/features/projects/sub_group_issuables_spec.rb
+++ b/spec/features/projects/sub_group_issuables_spec.rb
@@ -8,7 +8,7 @@ describe 'Subgroup Issuables', :feature, :js, :nested_groups do
 
   before do
     project.add_master(user)
-    login_as user
+    gitlab_sign_in user
   end
 
   it 'shows the full subgroup title when issues index page is empty' do
diff --git a/spec/features/projects/tags/download_buttons_spec.rb b/spec/features/projects/tags/download_buttons_spec.rb
index dd93d25c2c6b8e15e57749a2767447a8f8c76579..ca00a51aa3cf368357b5a52484b222d9f9cbe118 100644
--- a/spec/features/projects/tags/download_buttons_spec.rb
+++ b/spec/features/projects/tags/download_buttons_spec.rb
@@ -23,7 +23,7 @@ feature 'Download buttons in tags page', feature: true do
   end
 
   background do
-    login_as(user)
+    gitlab_sign_in(user)
     project.team << [user, role]
   end
 
diff --git a/spec/features/projects/tree/rss_spec.rb b/spec/features/projects/tree/rss_spec.rb
index 9bf59c4139c9d47a592ddb198dc50281f44c355b..135584e5bf876ed1a8d2ca73b308f8566de3f48f 100644
--- a/spec/features/projects/tree/rss_spec.rb
+++ b/spec/features/projects/tree/rss_spec.rb
@@ -8,7 +8,7 @@ feature 'Project Tree RSS' do
     before do
       user = create(:user)
       project.team << [user, :developer]
-      login_as(user)
+      gitlab_sign_in(user)
       visit path
     end
 
diff --git a/spec/features/projects/user_create_dir_spec.rb b/spec/features/projects/user_create_dir_spec.rb
index aeb7e0b7c33aa13ee7f4f1984d23c53604c0e23c..f375e1215db2d68349cc789a1b9ec0dffcfe7602 100644
--- a/spec/features/projects/user_create_dir_spec.rb
+++ b/spec/features/projects/user_create_dir_spec.rb
@@ -6,7 +6,7 @@ feature 'New directory creation', feature: true, js: true do
   given(:project) { create(:project) }
 
   background do
-    login_as(user)
+    gitlab_sign_in(user)
     project.team << [user, role]
     visit namespace_project_tree_path(project.namespace, project, 'master')
     open_new_directory_modal
diff --git a/spec/features/projects/user_creates_project_spec.rb b/spec/features/projects/user_creates_project_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..29f1eb8d73edfa769b6a05818359209a45df698a
--- /dev/null
+++ b/spec/features/projects/user_creates_project_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+feature 'User creates a project', js: true do
+  let(:user) { create(:user) }
+
+  before do
+    sign_in(user)
+    create(:personal_key, user: user)
+    visit(new_project_path)
+  end
+
+  it 'creates a new project' do
+    fill_in(:project_path, with: 'Empty')
+
+    page.within('#content-body') do
+      click_button('Create project')
+    end
+
+    project = Project.last
+
+    expect(current_path).to eq(namespace_project_path(project.namespace, project))
+    expect(page).to have_content('Empty')
+    expect(page).to have_content('git init')
+    expect(page).to have_content('git remote')
+    expect(page).to have_content(project.url_to_repo)
+  end
+end
diff --git a/spec/features/projects/view_on_env_spec.rb b/spec/features/projects/view_on_env_spec.rb
index 640f1376548696e515c86dc1ab89e1a7f61d1897..f6a640b90b4947881a9568c058cc5caf1fb1c614 100644
--- a/spec/features/projects/view_on_env_spec.rb
+++ b/spec/features/projects/view_on_env_spec.rb
@@ -50,7 +50,7 @@ describe 'View on environment', js: true do
         let(:merge_request) { create(:merge_request, :simple, source_project: project, source_branch: branch_name) }
 
         before do
-          login_as(user)
+          gitlab_sign_in(user)
 
           visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request)
 
@@ -66,7 +66,7 @@ describe 'View on environment', js: true do
 
       context 'when visiting a comparison for the branch' do
         before do
-          login_as(user)
+          gitlab_sign_in(user)
 
           visit namespace_project_compare_path(project.namespace, project, from: 'master', to: branch_name)
 
@@ -80,7 +80,7 @@ describe 'View on environment', js: true do
 
       context 'when visiting a comparison for the commit' do
         before do
-          login_as(user)
+          gitlab_sign_in(user)
 
           visit namespace_project_compare_path(project.namespace, project, from: 'master', to: sha)
 
@@ -94,7 +94,7 @@ describe 'View on environment', js: true do
 
       context 'when visiting a blob on the branch' do
         before do
-          login_as(user)
+          gitlab_sign_in(user)
 
           visit namespace_project_blob_path(project.namespace, project, File.join(branch_name, file_path))
 
@@ -108,7 +108,7 @@ describe 'View on environment', js: true do
 
       context 'when visiting a blob on the commit' do
         before do
-          login_as(user)
+          gitlab_sign_in(user)
 
           visit namespace_project_blob_path(project.namespace, project, File.join(sha, file_path))
 
@@ -122,7 +122,7 @@ describe 'View on environment', js: true do
 
       context 'when visiting the commit' do
         before do
-          login_as(user)
+          gitlab_sign_in(user)
 
           visit namespace_project_commit_path(project.namespace, project, sha)
 
diff --git a/spec/features/projects/wiki/markdown_preview_spec.rb b/spec/features/projects/wiki/markdown_preview_spec.rb
index 94f6bb167309f1046007d049f12bd783a3097387..fd6c09943e3c9417ebc8a38ae8d29002cfbf2dbc 100644
--- a/spec/features/projects/wiki/markdown_preview_spec.rb
+++ b/spec/features/projects/wiki/markdown_preview_spec.rb
@@ -16,7 +16,7 @@ feature 'Projects > Wiki > User previews markdown changes', feature: true, js: t
     project.team << [user, :master]
     WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute
 
-    login_as(user)
+    gitlab_sign_in(user)
 
     visit namespace_project_path(project.namespace, project)
     find('.shortcuts-wiki').trigger('click')
diff --git a/spec/features/projects/wiki/shortcuts_spec.rb b/spec/features/projects/wiki/shortcuts_spec.rb
index c1f6b0cce3bb6c97ce41fd2913d2444767a73fc6..ab0ed9b82040b6c94ea0cb42c72624221eae64c9 100644
--- a/spec/features/projects/wiki/shortcuts_spec.rb
+++ b/spec/features/projects/wiki/shortcuts_spec.rb
@@ -8,7 +8,7 @@ feature 'Wiki shortcuts', :feature, :js do
   end
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
     visit namespace_project_wiki_path(project.namespace, project, wiki_page)
   end
 
diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
index 8912d5758785c9e67044681863a92d7562596de8..a477dcf7ee927dd13be7277e97e6154134eeb1f7 100644
--- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
@@ -5,7 +5,7 @@ feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do
 
   background do
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
 
     visit namespace_project_path(project.namespace, project)
     find('.shortcuts-wiki').trigger('click')
@@ -133,6 +133,22 @@ feature 'Projects > Wiki > User creates wiki page', js: true, feature: true do
           expect(page).to have_content('My awesome wiki!')
         end
       end
+
+      scenario 'content has autocomplete', :js do
+        click_link 'New page'
+
+        page.within '#modal-new-wiki' do
+          fill_in :new_wiki_path, with: 'test-autocomplete'
+          click_button 'Create page'
+        end
+
+        page.within '.wiki-form' do
+          find('#wiki_content').native.send_keys('')
+          fill_in :wiki_content, with: '@'
+        end
+
+        expect(page).to have_selector('.atwho-view')
+      end
     end
   end
 
diff --git a/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb b/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb
index 95826e7e5be0cc902fbf78fa817df8dff7e55bce..7d31122af352255ba5f909ed19db563f9c531dda 100644
--- a/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_git_access_wiki_page_spec.rb
@@ -13,7 +13,7 @@ describe 'Projects > Wiki > User views Git access wiki page', :feature do
   end
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   scenario 'Visit Wiki Page Current Commit' do
diff --git a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
index 86cf520ea80ecf634d3cb7f4264ad33ac5527c59..64a3043868127fe8df77625c8329628ba5714d34 100644
--- a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb
@@ -5,11 +5,10 @@ feature 'Projects > Wiki > User updates wiki page', feature: true do
 
   background do
     project.team << [user, :master]
-    login_as(user)
-
-    visit namespace_project_path(project.namespace, project)
     WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute
-    click_link 'Wiki'
+    gitlab_sign_in(user)
+
+    visit namespace_project_wikis_path(project.namespace, project)
   end
 
   context 'in the user namespace' do
@@ -42,6 +41,15 @@ feature 'Projects > Wiki > User updates wiki page', feature: true do
         expect(page).to have_content('Content can\'t be blank')
         expect(find('textarea#wiki_content').value).to eq ''
       end
+
+      scenario 'content has autocomplete', :js do
+        click_link 'Edit'
+
+        find('#wiki_content').native.send_keys('')
+        fill_in :wiki_content, with: '@'
+
+        expect(page).to have_selector('.atwho-view')
+      end
     end
   end
 
diff --git a/spec/features/projects/wiki/user_views_project_wiki_page_spec.rb b/spec/features/projects/wiki/user_views_project_wiki_page_spec.rb
index c17e06612de383f645c8efaeaa0233bc97718ea6..8a88ab247f3717189052f4cbee8e4b4ec4719dae 100644
--- a/spec/features/projects/wiki/user_views_project_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_views_project_wiki_page_spec.rb
@@ -15,7 +15,7 @@ feature 'Projects > Wiki > User views the wiki page', feature: true do
 
   background do
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
     WikiPages::UpdateService.new(
       project,
       user,
diff --git a/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb b/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb
index 20219f3cc9a9fb00f596de081e5f2e783eab6ade..3679992516745b1476f9a2ffd7507dc910add2a7 100644
--- a/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb
+++ b/spec/features/projects/wiki/user_views_wiki_in_project_page_spec.rb
@@ -5,7 +5,7 @@ describe 'Projects > Wiki > User views wiki in project page', feature: true do
 
   before do
     project.team << [user, :master]
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   context 'when repository is disabled for project' do
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index 060e19596aeb66443e4b9fd99fc1f2544c16a815..7e8a703db934b78d8fa4e010a5e2a96f1e4be87a 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -6,7 +6,7 @@ feature 'Project', feature: true do
     let(:path)    { namespace_project_path(project.namespace, project) }
 
     before do
-      login_as(:admin)
+      gitlab_sign_in(:admin)
     end
 
     it 'parses Markdown' do
@@ -39,7 +39,7 @@ feature 'Project', feature: true do
     let(:project) { create(:empty_project, namespace: user.namespace) }
 
     before do
-      login_with user
+      gitlab_sign_in user
       create(:forked_project_link, forked_to_project: project)
       visit edit_namespace_project_path(project.namespace, project)
     end
@@ -60,7 +60,7 @@ feature 'Project', feature: true do
     let(:project) { create(:empty_project, namespace: user.namespace, name: 'project1') }
 
     before do
-      login_with(user)
+      gitlab_sign_in(user)
       project.team << [user, :master]
       visit edit_namespace_project_path(project.namespace, project)
     end
@@ -79,7 +79,7 @@ feature 'Project', feature: true do
     let(:project) { create(:empty_project, namespace: user.namespace) }
 
     before do
-      login_with(user)
+      gitlab_sign_in(user)
       project.add_user(user, Gitlab::Access::MASTER)
       visit namespace_project_path(project.namespace, project)
     end
@@ -98,7 +98,7 @@ feature 'Project', feature: true do
 
     context 'on issues page', js: true do
       before do
-        login_with(user)
+        gitlab_sign_in(user)
         project.add_user(user, Gitlab::Access::MASTER)
         project2.add_user(user, Gitlab::Access::MASTER)
         visit namespace_project_issue_path(project.namespace, project, issue)
@@ -123,7 +123,7 @@ feature 'Project', feature: true do
 
     before do
       project.team << [user, :master]
-      login_as user
+      gitlab_sign_in user
       visit namespace_project_path(project.namespace, project)
     end
 
diff --git a/spec/features/protected_branches_spec.rb b/spec/features/protected_branches_spec.rb
index aa9164dd979fc2f0444667f4d7fe580c766b842c..20b8e10f0f7d097e8514fe923231b7b174ebeb67 100644
--- a/spec/features/protected_branches_spec.rb
+++ b/spec/features/protected_branches_spec.rb
@@ -5,7 +5,7 @@ feature 'Protected Branches', feature: true, js: true do
   let(:project) { create(:project, :repository) }
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   def set_protected_branch_name(branch_name)
diff --git a/spec/features/protected_tags_spec.rb b/spec/features/protected_tags_spec.rb
index 63a2058577669ee94f1379251fb1ab18f4c19445..73a8069215404cbc8c131f243054dc7b191d9dff 100644
--- a/spec/features/protected_tags_spec.rb
+++ b/spec/features/protected_tags_spec.rb
@@ -5,7 +5,7 @@ feature 'Projected Tags', feature: true, js: true do
   let(:project) { create(:project, :repository) }
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   def set_protected_tag_name(tag_name)
diff --git a/spec/features/reportable_note/commit_spec.rb b/spec/features/reportable_note/commit_spec.rb
index 39b1c4acf52a7f5cb8ef644a4697978044e4acc0..12049822753b627d707b09371732c6a2e579f8e9 100644
--- a/spec/features/reportable_note/commit_spec.rb
+++ b/spec/features/reportable_note/commit_spec.rb
@@ -8,7 +8,7 @@ describe 'Reportable note on commit', :feature, :js do
 
   before do
     project.add_master(user)
-    login_as user
+    gitlab_sign_in(user)
   end
 
   context 'a normal note' do
diff --git a/spec/features/reportable_note/issue_spec.rb b/spec/features/reportable_note/issue_spec.rb
index 5f526818994b79af5655a991cdfcee994bb71d9d..ca2a7f414967b94de644bf9a8b30191fbaee639d 100644
--- a/spec/features/reportable_note/issue_spec.rb
+++ b/spec/features/reportable_note/issue_spec.rb
@@ -8,7 +8,7 @@ describe 'Reportable note on issue', :feature, :js do
 
   before do
     project.add_master(user)
-    login_as user
+    gitlab_sign_in(user)
 
     visit namespace_project_issue_path(project.namespace, project, issue)
   end
diff --git a/spec/features/reportable_note/merge_request_spec.rb b/spec/features/reportable_note/merge_request_spec.rb
index 6d053d26626f16701f59d848477cacc4a8bd7538..8e75b4af3ebfdcb25238a6bdcc0a8858424187ce 100644
--- a/spec/features/reportable_note/merge_request_spec.rb
+++ b/spec/features/reportable_note/merge_request_spec.rb
@@ -7,7 +7,7 @@ describe 'Reportable note on merge request', :feature, :js do
 
   before do
     project.add_master(user)
-    login_as user
+    gitlab_sign_in(user)
 
     visit namespace_project_merge_request_path(project.namespace, project, merge_request)
   end
diff --git a/spec/features/reportable_note/snippets_spec.rb b/spec/features/reportable_note/snippets_spec.rb
index 3f1e0cf9097108066978a48591baf49c0f03ab6e..5bee4a31379a10e5251ea7b8b0f3358f4ca9274c 100644
--- a/spec/features/reportable_note/snippets_spec.rb
+++ b/spec/features/reportable_note/snippets_spec.rb
@@ -6,7 +6,7 @@ describe 'Reportable note on snippets', :feature, :js do
 
   before do
     project.add_master(user)
-    login_as user
+    gitlab_sign_in(user)
   end
 
   describe 'on project snippet' do
@@ -19,15 +19,4 @@ describe 'Reportable note on snippets', :feature, :js do
 
     it_behaves_like 'reportable note'
   end
-
-  describe 'on personal snippet' do
-    let(:snippet) { create(:personal_snippet, :public, author: user) }
-    let!(:note) { create(:note_on_personal_snippet, noteable: snippet, author: user) }
-
-    before do
-      visit snippet_path(snippet)
-    end
-
-    it_behaves_like 'reportable note'
-  end
 end
diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb
index e87d52f5c8fe0e216d3f36f7468fe84b3394672a..ea18879b4bfa2b028b5fcf7cfbfb0568c9d22d90 100644
--- a/spec/features/runners_spec.rb
+++ b/spec/features/runners_spec.rb
@@ -6,7 +6,7 @@ describe "Runners" do
   let(:user) { create(:user) }
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
   end
 
   describe "specific runners" do
diff --git a/spec/features/search_spec.rb b/spec/features/search_spec.rb
index 89d4f536b201713b7769fc17e7504949f2057a75..64469f999afde796c82d6daafea3704144b3f424 100644
--- a/spec/features/search_spec.rb
+++ b/spec/features/search_spec.rb
@@ -9,7 +9,7 @@ describe "Search", feature: true  do
   let!(:issue2) { create(:issue, project: project, author: user) }
 
   before do
-    login_with(user)
+    gitlab_sign_in(user)
     project.team << [user, :reporter]
     visit search_path
   end
diff --git a/spec/features/snippets/create_snippet_spec.rb b/spec/features/snippets/create_snippet_spec.rb
index ddd31ede064de8323c6a899f4a6cfa7b4b31a99c..ac5c14ed42767cae3657976f90dd4ab1a127f10c 100644
--- a/spec/features/snippets/create_snippet_spec.rb
+++ b/spec/features/snippets/create_snippet_spec.rb
@@ -4,7 +4,7 @@ feature 'Create Snippet', :js, feature: true do
   include DropzoneHelper
 
   before do
-    login_as :user
+    gitlab_sign_in :user
     visit new_snippet_path
   end
 
diff --git a/spec/features/snippets/edit_snippet_spec.rb b/spec/features/snippets/edit_snippet_spec.rb
index 89ae593db88454b754707ddd1ae6c29c2bae3b47..860e1b156d6b24e0192bd93e7175a00accf9ac15 100644
--- a/spec/features/snippets/edit_snippet_spec.rb
+++ b/spec/features/snippets/edit_snippet_spec.rb
@@ -10,7 +10,7 @@ feature 'Edit Snippet', :js, feature: true do
   let(:snippet) { create(:personal_snippet, :public, file_name: file_name, content: content, author: user) }
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
 
     visit edit_snippet_path(snippet)
     wait_for_requests
diff --git a/spec/features/snippets/explore_spec.rb b/spec/features/snippets/explore_spec.rb
index fd097fe2e7484bc292f976c56df47f69fee12650..ec75817b942cfc1bce43763b46c02543bb0df7e7 100644
--- a/spec/features/snippets/explore_spec.rb
+++ b/spec/features/snippets/explore_spec.rb
@@ -6,7 +6,7 @@ feature 'Explore Snippets', feature: true do
   let!(:private_snippet) { create(:personal_snippet, :private) }
 
   scenario 'User should see snippets that are not private' do
-    login_as create(:user)
+    gitlab_sign_in create(:user)
     visit explore_snippets_path
 
     expect(page).to have_content(public_snippet.title)
@@ -15,7 +15,7 @@ feature 'Explore Snippets', feature: true do
   end
 
   scenario 'External user should see only public snippets' do
-    login_as create(:user, :external)
+    gitlab_sign_in create(:user, :external)
     visit explore_snippets_path
 
     expect(page).to have_content(public_snippet.title)
diff --git a/spec/features/snippets/internal_snippet_spec.rb b/spec/features/snippets/internal_snippet_spec.rb
index 93382f4c3591bd9d3539fd45e782bf05191480e7..3babb1c02ccc29774cb307f674ed1ab290da3523 100644
--- a/spec/features/snippets/internal_snippet_spec.rb
+++ b/spec/features/snippets/internal_snippet_spec.rb
@@ -5,7 +5,7 @@ feature 'Internal Snippets', feature: true, js: true do
 
   describe 'normal user' do
     before do
-      login_as :user
+      gitlab_sign_in :user
     end
 
     scenario 'sees internal snippets' do
diff --git a/spec/features/snippets/notes_on_personal_snippets_spec.rb b/spec/features/snippets/notes_on_personal_snippets_spec.rb
index 44b0c89fac720f52cf71dfddeeb38ee38a452f92..d310e7501ec352961285df56683fa916cf453a6b 100644
--- a/spec/features/snippets/notes_on_personal_snippets_spec.rb
+++ b/spec/features/snippets/notes_on_personal_snippets_spec.rb
@@ -14,7 +14,7 @@ describe 'Comments on personal snippets', :js, feature: true do
   let!(:other_note) { create(:note_on_personal_snippet) }
 
   before do
-    login_as user
+    gitlab_sign_in user
     visit snippet_path(snippet)
   end
 
@@ -46,8 +46,8 @@ describe 'Comments on personal snippets', :js, feature: true do
   context 'when submitting a note' do
     it 'shows a valid form' do
       is_expected.to have_css('.js-main-target-form', visible: true, count: 1)
-      expect(find('.js-main-target-form .js-comment-button').value).
-        to eq('Comment')
+      expect(find('.js-main-target-form .js-comment-button').value)
+        .to eq('Comment')
 
       page.within('.js-main-target-form') do
         expect(page).not_to have_link('Cancel')
@@ -70,6 +70,22 @@ describe 'Comments on personal snippets', :js, feature: true do
 
       expect(find('div#notes')).to have_content('This is awesome!')
     end
+
+    it 'should not have autocomplete' do
+      wait_for_requests
+      request_count_before = page.driver.network_traffic.count
+
+      find('#note_note').native.send_keys('')
+      fill_in 'note[note]', with: '@'
+
+      wait_for_requests
+      request_count_after = page.driver.network_traffic.count
+
+      # This selector probably won't be in place even if autocomplete was enabled
+      # but we want to make sure
+      expect(page).not_to have_selector('.atwho-view')
+      expect(request_count_before).to eq(request_count_after)
+    end
   end
 
   context 'when editing a note' do
diff --git a/spec/features/snippets/search_snippets_spec.rb b/spec/features/snippets/search_snippets_spec.rb
index 146cd3af848741eccffd8b9073d976736af557be..4c21e7321f4e16690aea2f58536805288867c407 100644
--- a/spec/features/snippets/search_snippets_spec.rb
+++ b/spec/features/snippets/search_snippets_spec.rb
@@ -5,7 +5,7 @@ feature 'Search Snippets', feature: true do
     public_snippet = create(:personal_snippet, :public, title: 'Beginning and Middle')
     private_snippet = create(:personal_snippet, :private, title: 'Middle and End')
 
-    login_as private_snippet.author
+    gitlab_sign_in private_snippet.author
     visit dashboard_snippets_path
 
     page.within '.search' do
@@ -41,7 +41,7 @@ feature 'Search Snippets', feature: true do
            CONTENT
           )
 
-    login_as create(:user)
+    gitlab_sign_in create(:user)
     visit dashboard_snippets_path
 
     page.within '.search' do
diff --git a/spec/features/snippets/user_snippets_spec.rb b/spec/features/snippets/user_snippets_spec.rb
index 191c2fb9a22eb91995faad831c22552484a6aa16..b971c6aab530e5b1481ff8deffedd183722512b8 100644
--- a/spec/features/snippets/user_snippets_spec.rb
+++ b/spec/features/snippets/user_snippets_spec.rb
@@ -7,7 +7,7 @@ feature 'User Snippets', feature: true do
   let!(:private_snippet) { create(:personal_snippet, :private, author: author, title: "This is a private snippet") }
 
   background do
-    login_as author
+    gitlab_sign_in author
     visit dashboard_snippets_path
   end
 
diff --git a/spec/features/tags/master_creates_tag_spec.rb b/spec/features/tags/master_creates_tag_spec.rb
index af25eebed1395b62f0ad1fbd84a2b40dcd818090..52db3583dac7bcc10902e20d5bcade1f585a0f2e 100644
--- a/spec/features/tags/master_creates_tag_spec.rb
+++ b/spec/features/tags/master_creates_tag_spec.rb
@@ -6,62 +6,80 @@ feature 'Master creates tag', feature: true do
 
   before do
     project.team << [user, :master]
-    login_with(user)
-    visit namespace_project_tags_path(project.namespace, project)
+    gitlab_sign_in(user)
   end
 
-  scenario 'with an invalid name displays an error' do
-    create_tag_in_form(tag: 'v 1.0', ref: 'master')
+  context 'from tag list' do
+    before do
+      visit namespace_project_tags_path(project.namespace, project)
+    end
 
-    expect(page).to have_content 'Tag name invalid'
-  end
+    scenario 'with an invalid name displays an error' do
+      create_tag_in_form(tag: 'v 1.0', ref: 'master')
 
-  scenario 'with an invalid reference displays an error' do
-    create_tag_in_form(tag: 'v2.0', ref: 'foo')
+      expect(page).to have_content 'Tag name invalid'
+    end
 
-    expect(page).to have_content 'Target foo is invalid'
-  end
+    scenario 'with an invalid reference displays an error' do
+      create_tag_in_form(tag: 'v2.0', ref: 'foo')
 
-  scenario 'that already exists displays an error' do
-    create_tag_in_form(tag: 'v1.1.0', ref: 'master')
+      expect(page).to have_content 'Target foo is invalid'
+    end
 
-    expect(page).to have_content 'Tag v1.1.0 already exists'
-  end
+    scenario 'that already exists displays an error' do
+      create_tag_in_form(tag: 'v1.1.0', ref: 'master')
+
+      expect(page).to have_content 'Tag v1.1.0 already exists'
+    end
 
-  scenario 'with multiline message displays the message in a <pre> block' do
-    create_tag_in_form(tag: 'v3.0', ref: 'master', message: "Awesome tag message\n\n- hello\n- world")
+    scenario 'with multiline message displays the message in a <pre> block' do
+      create_tag_in_form(tag: 'v3.0', ref: 'master', message: "Awesome tag message\n\n- hello\n- world")
 
-    expect(current_path).to eq(
-      namespace_project_tag_path(project.namespace, project, 'v3.0'))
-    expect(page).to have_content 'v3.0'
-    page.within 'pre.wrap' do
-      expect(page).to have_content "Awesome tag message\n\n- hello\n- world"
+      expect(current_path).to eq(
+        namespace_project_tag_path(project.namespace, project, 'v3.0'))
+      expect(page).to have_content 'v3.0'
+      page.within 'pre.wrap' do
+        expect(page).to have_content "Awesome tag message\n\n- hello\n- world"
+      end
     end
-  end
 
-  scenario 'with multiline release notes parses the release note as Markdown' do
-    create_tag_in_form(tag: 'v4.0', ref: 'master', desc: "Awesome release notes\n\n- hello\n- world")
+    scenario 'with multiline release notes parses the release note as Markdown' do
+      create_tag_in_form(tag: 'v4.0', ref: 'master', desc: "Awesome release notes\n\n- hello\n- world")
 
-    expect(current_path).to eq(
-      namespace_project_tag_path(project.namespace, project, 'v4.0'))
-    expect(page).to have_content 'v4.0'
-    page.within '.description' do
-      expect(page).to have_content 'Awesome release notes'
-      expect(page).to have_selector('ul li', count: 2)
+      expect(current_path).to eq(
+        namespace_project_tag_path(project.namespace, project, 'v4.0'))
+      expect(page).to have_content 'v4.0'
+      page.within '.description' do
+        expect(page).to have_content 'Awesome release notes'
+        expect(page).to have_selector('ul li', count: 2)
+      end
+    end
+
+    scenario 'opens dropdown for ref', js: true do
+      click_link 'New tag'
+      ref_row = find('.form-group:nth-of-type(2) .col-sm-10')
+      page.within ref_row do
+        ref_input = find('[name="ref"]', visible: false)
+        expect(ref_input.value).to eq 'master'
+        expect(find('.dropdown-toggle-text')).to have_content 'master'
+
+        find('.js-branch-select').trigger('click')
+
+        expect(find('.dropdown-menu')).to have_content 'empty-branch'
+      end
     end
   end
 
-  scenario 'opens dropdown for ref', js: true do
-    click_link 'New tag'
-    ref_row = find('.form-group:nth-of-type(2) .col-sm-10')
-    page.within ref_row do
-      ref_input = find('[name="ref"]', visible: false)
-      expect(ref_input.value).to eq 'master'
-      expect(find('.dropdown-toggle-text')).to have_content 'master'
+  context 'from new tag page' do
+    before do
+      visit new_namespace_project_tag_path(project.namespace, project)
+    end
 
-      find('.js-branch-select').trigger('click')
+    it 'description has autocomplete', :js do
+      find('#release_description').native.send_keys('')
+      fill_in 'release_description', with: '@'
 
-      expect(find('.dropdown-menu')).to have_content 'empty-branch'
+      expect(page).to have_selector('.atwho-view')
     end
   end
 
diff --git a/spec/features/tags/master_deletes_tag_spec.rb b/spec/features/tags/master_deletes_tag_spec.rb
index ccfafe6db7d5d3427adad86d079ab7bd91b4f89f..58f33e954f9cb7daf588662ff054588c5d30c68b 100644
--- a/spec/features/tags/master_deletes_tag_spec.rb
+++ b/spec/features/tags/master_deletes_tag_spec.rb
@@ -6,7 +6,7 @@ feature 'Master deletes tag', feature: true do
 
   before do
     project.team << [user, :master]
-    login_with(user)
+    gitlab_sign_in(user)
     visit namespace_project_tags_path(project.namespace, project)
   end
 
diff --git a/spec/features/tags/master_updates_tag_spec.rb b/spec/features/tags/master_updates_tag_spec.rb
index 6b5b3122f725279c68ea2c779cbe0bf37914eecf..18c8c4c511c25933389ca364f5406a1396363404 100644
--- a/spec/features/tags/master_updates_tag_spec.rb
+++ b/spec/features/tags/master_updates_tag_spec.rb
@@ -6,7 +6,7 @@ feature 'Master updates tag', feature: true do
 
   before do
     project.team << [user, :master]
-    login_with(user)
+    gitlab_sign_in(user)
     visit namespace_project_tags_path(project.namespace, project)
   end
 
@@ -24,6 +24,17 @@ feature 'Master updates tag', feature: true do
       expect(page).to have_content 'v1.1.0'
       expect(page).to have_content 'Awesome release notes'
     end
+
+    scenario 'description has autocomplete', :js do
+      page.within(first('.content-list .controls')) do
+        click_link 'Edit release notes'
+      end
+
+      find('#release_description').native.send_keys('')
+      fill_in 'release_description', with: '@'
+
+      expect(page).to have_selector('.atwho-view')
+    end
   end
 
   context 'from a specific tag page' do
diff --git a/spec/features/tags/master_views_tags_spec.rb b/spec/features/tags/master_views_tags_spec.rb
index 922ac15a2ebcd2974a23de7e68bac3484f83ec39..3c21fa066946449b8c6fd1ebc0e82f090ddb9665 100644
--- a/spec/features/tags/master_views_tags_spec.rb
+++ b/spec/features/tags/master_views_tags_spec.rb
@@ -5,7 +5,7 @@ feature 'Master views tags', feature: true do
 
   before do
     project.team << [user, :master]
-    login_with(user)
+    gitlab_sign_in(user)
   end
 
   context 'when project has no tags' do
diff --git a/spec/features/todos/target_state_spec.rb b/spec/features/todos/target_state_spec.rb
index 32fa88a2b21cbdae47a004d0889439ed29c0b66c..99b70b3d3a16e805243fcf56e3234e5b08a17b87 100644
--- a/spec/features/todos/target_state_spec.rb
+++ b/spec/features/todos/target_state_spec.rb
@@ -6,7 +6,7 @@ feature 'Todo target states', feature: true do
   let(:project) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) }
 
   before do
-    login_as user
+    gitlab_sign_in user
   end
 
   scenario 'on a closed issue todo has closed label' do
diff --git a/spec/features/todos/todos_filtering_spec.rb b/spec/features/todos/todos_filtering_spec.rb
index bbfa4e08379574bfedf2c66f7d60c88f5c2391e7..032fb4790761fcf2eb1a3ba03c88760137a39973 100644
--- a/spec/features/todos/todos_filtering_spec.rb
+++ b/spec/features/todos/todos_filtering_spec.rb
@@ -17,7 +17,7 @@ describe 'Dashboard > User filters todos', feature: true, js: true do
 
     project_1.team << [user_1, :developer]
     project_2.team << [user_1, :developer]
-    login_as(user_1)
+    gitlab_sign_in(user_1)
     visit dashboard_todos_path
   end
 
diff --git a/spec/features/todos/todos_sorting_spec.rb b/spec/features/todos/todos_sorting_spec.rb
index f012d2508873be1b7201b9dc861bdfb24bbd3ad0..498bbac6d141abef8f11b07867a029c045a6250b 100644
--- a/spec/features/todos/todos_sorting_spec.rb
+++ b/spec/features/todos/todos_sorting_spec.rb
@@ -32,7 +32,7 @@ describe "Dashboard > User sorts todos", feature: true do
       issue_2.labels         << label_3
       issue_1.labels         << label_2
 
-      login_as(user)
+      gitlab_sign_in(user)
       visit dashboard_todos_path
     end
 
@@ -83,7 +83,7 @@ describe "Dashboard > User sorts todos", feature: true do
       create(:todo, user: user, project: project, target: issue_2)
       create(:todo, user: user, project: project, target: merge_request_1)
 
-      login_as(user)
+      gitlab_sign_in(user)
       visit dashboard_todos_path
     end
 
diff --git a/spec/features/todos/todos_spec.rb b/spec/features/todos/todos_spec.rb
index feb2fe8a7d1af25568c337397748248b67dec317..41b32bdedc3b79f47611c1ea419998691cd5c9c5 100644
--- a/spec/features/todos/todos_spec.rb
+++ b/spec/features/todos/todos_spec.rb
@@ -9,7 +9,7 @@ describe 'Dashboard Todos', feature: true do
   describe 'GET /dashboard/todos' do
     context 'User does not have todos' do
       before do
-        login_as(user)
+        gitlab_sign_in(user)
         visit dashboard_todos_path
       end
       it 'shows "All done" message' do
@@ -20,7 +20,7 @@ describe 'Dashboard Todos', feature: true do
     context 'User has a todo', js: true do
       before do
         create(:todo, :mentioned, user: user, project: project, target: issue, author: author)
-        login_as(user)
+        gitlab_sign_in(user)
         visit dashboard_todos_path
       end
 
@@ -101,7 +101,7 @@ describe 'Dashboard Todos', feature: true do
 
     context 'User created todos for themself' do
       before do
-        login_as(user)
+        gitlab_sign_in(user)
       end
 
       context 'issue assigned todo' do
@@ -179,7 +179,7 @@ describe 'Dashboard Todos', feature: true do
     context 'User has done todos', js: true do
       before do
         create(:todo, :mentioned, :done, user: user, project: project, target: issue, author: author)
-        login_as(user)
+        gitlab_sign_in(user)
         visit dashboard_todos_path(state: :done)
       end
 
@@ -217,7 +217,7 @@ describe 'Dashboard Todos', feature: true do
         note2 = create(:note_on_issue, note: "Test #{label2.to_reference(format: :name)}", noteable_id: issue2.id, noteable_type: 'Issue', project: project2)
         create(:todo, :mentioned, project: project2, target: issue2, user: user, note_id: note2.id)
 
-        login_as(user)
+        gitlab_sign_in(user)
         visit dashboard_todos_path
       end
 
@@ -233,7 +233,7 @@ describe 'Dashboard Todos', feature: true do
         # Create just enough records to cause us to paginate
         create_list(:todo, 2, :mentioned, user: user, project: project, target: issue, author: author)
 
-        login_as(user)
+        gitlab_sign_in(user)
       end
 
       it 'is paginated' do
@@ -321,7 +321,7 @@ describe 'Dashboard Todos', feature: true do
         deleted_project = create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC, pending_delete: true)
         create(:todo, :mentioned, user: user, project: deleted_project, target: issue, author: author)
         create(:todo, :mentioned, user: user, project: deleted_project, target: issue, author: author, state: :done)
-        login_as(user)
+        gitlab_sign_in(user)
         visit dashboard_todos_path
       end
 
@@ -337,7 +337,7 @@ describe 'Dashboard Todos', feature: true do
       let!(:todo) { create(:todo, :build_failed, user: user, project: project, author: author) }
 
       before do
-        login_as user
+        gitlab_sign_in user
         visit dashboard_todos_path
       end
 
diff --git a/spec/features/triggers_spec.rb b/spec/features/triggers_spec.rb
index 2ea9992173d223b8a029c38117a67e4eab260444..5af2c0e9035da296a1840009ff183b253d7f67c6 100644
--- a/spec/features/triggers_spec.rb
+++ b/spec/features/triggers_spec.rb
@@ -7,14 +7,13 @@ feature 'Triggers', feature: true, js: true do
   let(:guest_user) { create(:user) }
 
   before do
-    login_as(user)
-  end
+    sign_in(user)
 
-  before do
     @project = create(:empty_project)
     @project.team << [user, :master]
     @project.team << [user2, :master]
     @project.team << [guest_user, :guest]
+
     visit namespace_project_settings_ci_cd_path(@project.namespace, @project)
   end
 
@@ -34,7 +33,7 @@ feature 'Triggers', feature: true, js: true do
       # See if "trigger creation successful" message displayed and description and owner are correct
       expect(page.find('.flash-notice')).to have_content 'Trigger was created successfully.'
       expect(page.find('.triggers-list')).to have_content 'trigger desc'
-      expect(page.find('.triggers-list .trigger-owner')).to have_content @user.name
+      expect(page.find('.triggers-list .trigger-owner')).to have_content user.name
     end
   end
 
@@ -62,7 +61,7 @@ feature 'Triggers', feature: true, js: true do
       # See if "trigger updated successfully" message displayed and description and owner are correct
       expect(page.find('.flash-notice')).to have_content 'Trigger was successfully updated.'
       expect(page.find('.triggers-list')).to have_content new_trigger_title
-      expect(page.find('.triggers-list .trigger-owner')).to have_content @user.name
+      expect(page.find('.triggers-list .trigger-owner')).to have_content user.name
     end
 
     scenario 'edit "legacy" trigger and save' do
@@ -99,7 +98,7 @@ feature 'Triggers', feature: true, js: true do
       page.accept_confirm do
         expect(page.find('.flash-notice')).to have_content 'Trigger was re-assigned.'
         expect(page.find('.triggers-list')).to have_content trigger_title
-        expect(page.find('.triggers-list .trigger-owner')).to have_content @user.name
+        expect(page.find('.triggers-list .trigger-owner')).to have_content user.name
       end
     end
   end
@@ -158,7 +157,7 @@ feature 'Triggers', feature: true, js: true do
       expect(page.find('.triggers-list')).not_to have_selector('button.btn-clipboard')
 
       # See if trigger owner name doesn't match with current_user and trigger is non-editable
-      expect(page.find('.triggers-list .trigger-owner')).not_to have_content @user.name
+      expect(page.find('.triggers-list .trigger-owner')).not_to have_content user.name
       expect(page.find('.triggers-list')).not_to have_selector('a[title="Edit"]')
     end
 
@@ -171,7 +170,7 @@ feature 'Triggers', feature: true, js: true do
       expect(page.find('.triggers-list')).to have_selector('button.btn-clipboard')
 
       # See if trigger owner name matches with current_user and is editable
-      expect(page.find('.triggers-list .trigger-owner')).to have_content @user.name
+      expect(page.find('.triggers-list .trigger-owner')).to have_content user.name
       expect(page.find('.triggers-list')).to have_selector('a[title="Edit"]')
     end
   end
diff --git a/spec/features/u2f_spec.rb b/spec/features/u2f_spec.rb
index dc21637967fdd56494553f04c0457eb174fca5f3..f3662cb184ff718233719ab841f1939c79dcd474 100644
--- a/spec/features/u2f_spec.rb
+++ b/spec/features/u2f_spec.rb
@@ -25,7 +25,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
     let(:user) { create(:user) }
 
     before do
-      login_as(user)
+      gitlab_sign_in(user)
       user.update_attribute(:otp_required_for_login, true)
     end
 
@@ -93,10 +93,10 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
       manage_two_factor_authentication
       u2f_device = register_u2f_device
       expect(page).to have_content('Your U2F device was registered')
-      logout
+      gitlab_sign_out
 
       # Second user
-      user = login_as(:user)
+      user = gitlab_sign_in(:user)
       user.update_attribute(:otp_required_for_login, true)
       visit profile_account_path
       manage_two_factor_authentication
@@ -147,18 +147,18 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
 
     before do
       # Register and logout
-      login_as(user)
+      gitlab_sign_in(user)
       user.update_attribute(:otp_required_for_login, true)
       visit profile_account_path
       manage_two_factor_authentication
       @u2f_device = register_u2f_device
-      logout
+      gitlab_sign_out
     end
 
     describe "when 2FA via OTP is disabled" do
       it "allows logging in with the U2F device" do
         user.update_attribute(:otp_required_for_login, false)
-        login_with(user)
+        gitlab_sign_in(user)
 
         @u2f_device.respond_to_u2f_authentication
 
@@ -170,7 +170,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
     describe "when 2FA via OTP is enabled" do
       it "allows logging in with the U2F device" do
         user.update_attribute(:otp_required_for_login, true)
-        login_with(user)
+        gitlab_sign_in(user)
 
         @u2f_device.respond_to_u2f_authentication
 
@@ -180,7 +180,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
     end
 
     it 'persists remember_me value via hidden field' do
-      login_with(user, remember: true)
+      gitlab_sign_in(user, remember: true)
 
       @u2f_device.respond_to_u2f_authentication
       expect(page).to have_content('We heard back from your U2F device')
@@ -195,15 +195,15 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
       describe "but not the current user" do
         it "does not allow logging in with that particular device" do
           # Register current user with the different U2F device
-          current_user = login_as(:user)
+          current_user = gitlab_sign_in(:user)
           current_user.update_attribute(:otp_required_for_login, true)
           visit profile_account_path
           manage_two_factor_authentication
           register_u2f_device(name: 'My other device')
-          logout
+          gitlab_sign_out
 
           # Try authenticating user with the old U2F device
-          login_as(current_user)
+          gitlab_sign_in(current_user)
           @u2f_device.respond_to_u2f_authentication
           expect(page).to have_content('We heard back from your U2F device')
           expect(page).to have_content('Authentication via U2F device failed')
@@ -213,15 +213,15 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
       describe "and also the current user" do
         it "allows logging in with that particular device" do
           # Register current user with the same U2F device
-          current_user = login_as(:user)
+          current_user = gitlab_sign_in(:user)
           current_user.update_attribute(:otp_required_for_login, true)
           visit profile_account_path
           manage_two_factor_authentication
           register_u2f_device(@u2f_device)
-          logout
+          gitlab_sign_out
 
           # Try authenticating user with the same U2F device
-          login_as(current_user)
+          gitlab_sign_in(current_user)
           @u2f_device.respond_to_u2f_authentication
           expect(page).to have_content('We heard back from your U2F device')
 
@@ -233,7 +233,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
     describe "when a given U2F device has not been registered" do
       it "does not allow logging in with that particular device" do
         unregistered_device = FakeU2fDevice.new(page, 'My device')
-        login_as(user)
+        gitlab_sign_in(user)
         unregistered_device.respond_to_u2f_authentication
         expect(page).to have_content('We heard back from your U2F device')
 
@@ -244,7 +244,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
     describe "when more than one device has been registered by the same user" do
       it "allows logging in with either device" do
         # Register first device
-        user = login_as(:user)
+        user = gitlab_sign_in(:user)
         user.update_attribute(:otp_required_for_login, true)
         visit profile_two_factor_auth_path
         expect(page).to have_content("Your U2F device needs to be set up.")
@@ -254,17 +254,17 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
         visit profile_two_factor_auth_path
         expect(page).to have_content("Your U2F device needs to be set up.")
         second_device = register_u2f_device(name: 'My other device')
-        logout
+        gitlab_sign_out
 
         # Authenticate as both devices
         [first_device, second_device].each do |device|
-          login_as(user)
+          gitlab_sign_in(user)
           device.respond_to_u2f_authentication
           expect(page).to have_content('We heard back from your U2F device')
 
           expect(page).to have_css('.sign-out-link', visible: false)
 
-          logout
+          gitlab_sign_out
         end
       end
     end
@@ -273,7 +273,7 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
       let(:user) { create(:user) }
 
       before do
-        user = login_as(:user)
+        user = gitlab_sign_in(:user)
         user.update_attribute(:otp_required_for_login, true)
         visit profile_account_path
         manage_two_factor_authentication
@@ -300,15 +300,15 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
 
     before do
       # Register and logout
-      login_as(user)
+      gitlab_sign_in(user)
       user.update_attribute(:otp_required_for_login, true)
       visit profile_account_path
     end
 
     describe 'when no u2f device is registered' do
       before do
-        logout
-        login_with(user)
+        gitlab_sign_out
+        gitlab_sign_in(user)
       end
 
       it 'shows the fallback otp code UI' do
@@ -320,8 +320,8 @@ feature 'Using U2F (Universal 2nd Factor) Devices for Authentication', :js do
       before do
         manage_two_factor_authentication
         @u2f_device = register_u2f_device
-        logout
-        login_with(user)
+        gitlab_sign_out
+        gitlab_sign_in(user)
       end
 
       it 'provides a button that shows the fallback otp code UI' do
diff --git a/spec/features/unsubscribe_links_spec.rb b/spec/features/unsubscribe_links_spec.rb
index 0a8db15c75f331989b9c86e0284ae0f58b29598a..352f8ba70ac45c451fbee480c5f0c9c403a26f3e 100644
--- a/spec/features/unsubscribe_links_spec.rb
+++ b/spec/features/unsubscribe_links_spec.rb
@@ -57,7 +57,7 @@ describe 'Unsubscribe links', feature: true do
 
   context 'when logged in' do
     before do
-      login_as(recipient)
+      sign_in(recipient)
     end
 
     it 'unsubscribes from the issue when visiting the link from the email body' do
diff --git a/spec/features/uploads/user_uploads_avatar_to_group_spec.rb b/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
index d9d6f2e2382d6fd2f37b6ce58c0c467723346cc1..797b7b3d50d0d2f07f21340a3f5dbe1d48441578 100644
--- a/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
+++ b/spec/features/uploads/user_uploads_avatar_to_group_spec.rb
@@ -5,7 +5,7 @@ feature 'User uploads avatar to group', feature: true do
     user = create(:user)
     group = create(:group)
     group.add_owner(user)
-    login_as(user)
+    gitlab_sign_in(user)
 
     visit edit_group_path(group)
     attach_file(
diff --git a/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb b/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb
index eb8dbd76aab8c9d04f0284a466b75d2e90e9a248..a3f8027f4da582d8bdeebb3d1ca722a3c7dc8f51 100644
--- a/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb
+++ b/spec/features/uploads/user_uploads_avatar_to_profile_spec.rb
@@ -3,7 +3,7 @@ require 'rails_helper'
 feature 'User uploads avatar to profile', feature: true do
   scenario 'they see their new avatar' do
     user = create(:user)
-    login_as(user)
+    gitlab_sign_in(user)
 
     visit profile_path
     attach_file(
diff --git a/spec/features/uploads/user_uploads_file_to_note_spec.rb b/spec/features/uploads/user_uploads_file_to_note_spec.rb
index 9332d3b88d2c72a2be922579bab488e10399642c..77a1012762d1b3a1f506439a1d5128632340a114 100644
--- a/spec/features/uploads/user_uploads_file_to_note_spec.rb
+++ b/spec/features/uploads/user_uploads_file_to_note_spec.rb
@@ -8,7 +8,7 @@ feature 'User uploads file to note', feature: true do
   let(:issue) { create(:issue, project: project, author: user) }
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
     visit namespace_project_issue_path(project.namespace, project, issue)
   end
 
diff --git a/spec/features/user_callout_spec.rb b/spec/features/user_callout_spec.rb
index b84f834ff1e6ab2961ba02d7c429281ca5072dd0..7538a6e4a04d329cc2f0863f7e133e64c2ef8775 100644
--- a/spec/features/user_callout_spec.rb
+++ b/spec/features/user_callout_spec.rb
@@ -6,7 +6,7 @@ describe 'User Callouts', js: true do
   let(:project) { create(:empty_project, path: 'gitlab', name: 'sample') }
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
     project.team << [user, :master]
   end
 
diff --git a/spec/features/user_can_display_performance_bar_spec.rb b/spec/features/user_can_display_performance_bar_spec.rb
index c2842255b861d2b538361292931d85b3933b8152..1bd7e0389393c20dcb783b4e0ef009133f822d8a 100644
--- a/spec/features/user_can_display_performance_bar_spec.rb
+++ b/spec/features/user_can_display_performance_bar_spec.rb
@@ -57,7 +57,7 @@ describe 'User can display performacne bar', :js do
 
   context 'when user is logged-in' do
     before do
-      login_as :user
+      gitlab_sign_in(create(:user))
 
       visit root_path
     end
diff --git a/spec/features/users/projects_spec.rb b/spec/features/users/projects_spec.rb
index 67ce4b444645c168efa0f1153a7b44c8d877d906..377b1a0148f11c863851ff7cfe0215fe59978336 100644
--- a/spec/features/users/projects_spec.rb
+++ b/spec/features/users/projects_spec.rb
@@ -8,7 +8,7 @@ describe 'Projects tab on a user profile', :feature, :js do
   before do
     allow(Project).to receive(:default_per_page).and_return(1)
 
-    login_as(user)
+    gitlab_sign_in(user)
 
     visit user_path(user)
 
diff --git a/spec/features/users/rss_spec.rb b/spec/features/users/rss_spec.rb
index dbd5f66b55e3f614fad1e42e2bbb7e01bf92ae94..797b317a9bb803c4befcbf8afdf483481ea42a66 100644
--- a/spec/features/users/rss_spec.rb
+++ b/spec/features/users/rss_spec.rb
@@ -5,7 +5,7 @@ feature 'User RSS' do
 
   context 'when signed in' do
     before do
-      login_as(create(:user))
+      gitlab_sign_in(create(:user))
       visit path
     end
 
diff --git a/spec/features/users/snippets_spec.rb b/spec/features/users/snippets_spec.rb
index 2e388115633f406d1e1d22727ab0ce071a18c973..74c5cbd7887d7a3cc541594fc43dfdbb4066d54a 100644
--- a/spec/features/users/snippets_spec.rb
+++ b/spec/features/users/snippets_spec.rb
@@ -24,7 +24,7 @@ describe 'Snippets tab on a user profile', feature: true, js: true do
       let!(:other_snippet) { create(:snippet, :public) }
 
       it 'contains only internal and public snippets of a user when a user is logged in' do
-        login_as(:user)
+        gitlab_sign_in(:user)
         visit user_path(user)
         page.within('.user-profile-nav') { click_link 'Snippets' }
         wait_for_requests
diff --git a/spec/features/users_spec.rb b/spec/features/users_spec.rb
index c241dae12cff0aff84ba1a3d2094059a36f40cdd..84af13d3e49829aa92a01a443d1257576cfcdb59 100644
--- a/spec/features/users_spec.rb
+++ b/spec/features/users_spec.rb
@@ -24,7 +24,7 @@ feature 'Users', feature: true, js: true do
     user.reload
     expect(user.reset_password_token).not_to be_nil
 
-    login_with(user)
+    gitlab_sign_in(user)
     expect(current_path).to eq root_path
 
     user.reload
diff --git a/spec/features/variables_spec.rb b/spec/features/variables_spec.rb
index d0c982919db570fc1177b9ded6860dc1ec082d83..85085bf305a4991cade748f58ee60864517044d0 100644
--- a/spec/features/variables_spec.rb
+++ b/spec/features/variables_spec.rb
@@ -6,7 +6,7 @@ describe 'Project variables', js: true do
   let(:variable) { create(:ci_variable, key: 'test_key', value: 'test value') }
 
   before do
-    login_as(user)
+    gitlab_sign_in(user)
     project.team << [user, :master]
     project.variables << variable
 
diff --git a/spec/finders/groups_finder_spec.rb b/spec/finders/groups_finder_spec.rb
index 5b3591550c130b19f42658266f606a46931c1ace..9e70cccc3c4deac7db1ff5f1e7ba5e5878b4b6b0 100644
--- a/spec/finders/groups_finder_spec.rb
+++ b/spec/finders/groups_finder_spec.rb
@@ -38,28 +38,79 @@ describe GroupsFinder do
       end
     end
 
-    context 'subgroups' do
+    context 'subgroups', :nested_groups do
       let!(:parent_group) { create(:group, :public) }
       let!(:public_subgroup) { create(:group, :public, parent: parent_group) }
       let!(:internal_subgroup) { create(:group, :internal, parent: parent_group) }
       let!(:private_subgroup) { create(:group, :private, parent: parent_group) }
 
       context 'without a user' do
-        it 'only returns public subgroups' do
-          expect(described_class.new(nil, parent: parent_group).execute).to contain_exactly(public_subgroup)
+        it 'only returns parent and public subgroups' do
+          expect(described_class.new(nil).execute).to contain_exactly(parent_group, public_subgroup)
         end
       end
 
       context 'with a user' do
-        it 'returns public and internal subgroups' do
-          expect(described_class.new(user, parent: parent_group).execute).to contain_exactly(public_subgroup, internal_subgroup)
+        subject { described_class.new(user).execute }
+
+        it 'returns parent, public, and internal subgroups' do
+          is_expected.to contain_exactly(parent_group, public_subgroup, internal_subgroup)
         end
 
         context 'being member' do
-          it 'returns public subgroups, internal subgroups, and private subgroups user is member of' do
+          it 'returns parent, public subgroups, internal subgroups, and private subgroups user is member of' do
             private_subgroup.add_guest(user)
 
-            expect(described_class.new(user, parent: parent_group).execute).to contain_exactly(public_subgroup, internal_subgroup, private_subgroup)
+            is_expected.to contain_exactly(parent_group, public_subgroup, internal_subgroup, private_subgroup)
+          end
+        end
+
+        context 'parent group private' do
+          before do
+            parent_group.update_attribute(:visibility_level, Gitlab::VisibilityLevel::PRIVATE)
+          end
+
+          context 'being member of parent group' do
+            it 'returns all subgroups' do
+              parent_group.add_guest(user)
+
+              is_expected.to contain_exactly(parent_group, public_subgroup, internal_subgroup, private_subgroup)
+            end
+          end
+
+          context 'authorized to private project' do
+            context 'project one level deep' do
+              let!(:subproject) { create(:empty_project, :private, namespace: private_subgroup) }
+              before do
+                subproject.add_guest(user)
+              end
+
+              it 'includes the subgroup of the project' do
+                is_expected.to include(private_subgroup)
+              end
+
+              it 'does not include private subgroups deeper down' do
+                subsubgroup = create(:group, :private, parent: private_subgroup)
+
+                is_expected.not_to include(subsubgroup)
+              end
+            end
+
+            context 'project two levels deep' do
+              let!(:private_subsubgroup) { create(:group, :private, parent: private_subgroup) }
+              let!(:subsubproject) { create(:empty_project, :private, namespace: private_subsubgroup) }
+              before do
+                subsubproject.add_guest(user)
+              end
+
+              it 'returns all the ancestor groups' do
+                is_expected.to include(private_subsubgroup, private_subgroup, parent_group)
+              end
+
+              it 'returns the groups for a given parent' do
+                expect(described_class.new(user, parent: parent_group).execute).to include(private_subgroup)
+              end
+            end
           end
         end
       end
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index 8f2d60f2f1b369818f670766f9ac8cb444a40bf5..8ace1fb57514f56c61c0240f4b6c40f7bced11e6 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -7,9 +7,9 @@ describe IssuesFinder do
   set(:project2) { create(:empty_project) }
   set(:milestone) { create(:milestone, project: project1) }
   set(:label) { create(:label, project: project2) }
-  set(:issue1) { create(:issue, author: user, assignees: [user], project: project1, milestone: milestone, title: 'gitlab') }
+  set(:issue1) { create(:issue, author: user, assignees: [user], project: project1, milestone: milestone, title: 'gitlab', created_at: 1.week.ago) }
   set(:issue2) { create(:issue, author: user, assignees: [user], project: project2, description: 'gitlab') }
-  set(:issue3) { create(:issue, author: user2, assignees: [user2], project: project2, title: 'tanuki', description: 'tanuki') }
+  set(:issue3) { create(:issue, author: user2, assignees: [user2], project: project2, title: 'tanuki', description: 'tanuki', created_at: 1.week.from_now) }
 
   describe '#execute' do
     set(:closed_issue) { create(:issue, author: user2, assignees: [user2], project: project2, state: 'closed') }
@@ -215,6 +215,24 @@ describe IssuesFinder do
         end
       end
 
+      context 'filtering by created_at' do
+        context 'through created_after' do
+          let(:params) { { created_after: issue3.created_at } }
+
+          it 'returns issues created on or after the given date' do
+            expect(issues).to contain_exactly(issue3)
+          end
+        end
+
+        context 'through created_before' do
+          let(:params) { { created_before: issue1.created_at + 1.second } }
+
+          it 'returns issues created on or before the given date' do
+            expect(issues).to contain_exactly(issue1)
+          end
+        end
+      end
+
       context 'when the user is unauthorized' do
         let(:search_user) { nil }
 
diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb
index 58b7cd5e09840f376151b67664086528b5ad51a7..5eb26de6c92114355ab9358740f4a89b34a70560 100644
--- a/spec/finders/merge_requests_finder_spec.rb
+++ b/spec/finders/merge_requests_finder_spec.rb
@@ -46,5 +46,47 @@ describe MergeRequestsFinder do
 
       expect(merge_requests).to contain_exactly(merge_request1)
     end
+
+    context 'with created_after and created_before params' do
+      let(:project4) { create(:empty_project, forked_from_project: project1) }
+
+      let!(:new_merge_request) do
+        create(:merge_request,
+               :simple,
+               author: user,
+               created_at: 1.week.from_now,
+               source_project: project4,
+               target_project: project1)
+      end
+
+      let!(:old_merge_request) do
+        create(:merge_request,
+               :simple,
+               author: user,
+               created_at: 1.week.ago,
+               source_project: project4,
+               target_project: project4)
+      end
+
+      before do
+        project4.add_master(user)
+      end
+
+      it 'filters by created_after' do
+        params = { project_id: project1.id, created_after: new_merge_request.created_at }
+
+        merge_requests = described_class.new(user, params).execute
+
+        expect(merge_requests).to contain_exactly(new_merge_request)
+      end
+
+      it 'filters by created_before' do
+        params = { project_id: project4.id, created_before: old_merge_request.created_at + 1.second }
+
+        merge_requests = described_class.new(user, params).execute
+
+        expect(merge_requests).to contain_exactly(old_merge_request)
+      end
+    end
   end
 end
diff --git a/spec/fixtures/emails/html_empty_link.eml b/spec/fixtures/emails/html_empty_link.eml
new file mode 100644
index 0000000000000000000000000000000000000000..1672b98b92580356bddb2a2d62ac2201e7ec8ac3
--- /dev/null
+++ b/spec/fixtures/emails/html_empty_link.eml
@@ -0,0 +1,26 @@
+
+MIME-Version: 1.0
+Received: by 10.25.161.144 with HTTP; Tue, 7 Oct 2014 22:17:17 -0700 (PDT)
+X-Originating-IP: [117.207.85.84]
+In-Reply-To: <5434c8b52bb3a_623ff09fec70f049749@discourse-app.mail>
+References: <topic/35@discourse.techapj.com>
+  <5434c8b52bb3a_623ff09fec70f049749@discourse-app.mail>
+Date: Wed, 8 Oct 2014 10:47:17 +0530
+Delivered-To: arpit@techapj.com
+Message-ID: <CAOJeqne=SJ_LwN4sb-0Y95ejc2OpreVhdmcPn0TnmwSvTCYzzQ@mail.gmail.com>
+Subject: Re: [Discourse] [Meta] Welcome to techAPJ's Discourse!
+From: Arpit Jalan <arpit@techapj.com>
+To: Discourse <mail+e1c7f2a380e33840aeb654f075490bad@arpitjalan.com>Accept-Language: en-US
+Content-Language: en-US
+X-MS-Has-Attach:
+X-MS-TNEF-Correlator:
+x-originating-ip: [134.68.31.227]
+Content-Type: multipart/alternative;
+        boundary="_000_B0DFE1BEB3739743BC9B639D0E6BC8FF217A6341IUMSSGMBX104ads_"
+MIME-Version: 1.0
+
+--_000_B0DFE1BEB3739743BC9B639D0E6BC8FF217A6341IUMSSGMBX104ads_
+Content-Type: text/html; charset="utf-8"
+
+<a name="_MailEndCompose">no brackets!</a>
+--_000_B0DFE1BEB3739743BC9B639D0E6BC8FF217A6341IUMSSGMBX104ads_--
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index cc7f889b92719120481a4dd3ebaf5b5d909a88a9..56daeffde27e3943b8410121ff2a1d69f81889c0 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -61,14 +61,14 @@ describe ApplicationHelper do
       project = create(:empty_project, avatar: File.open(uploaded_image_temp_path))
       avatar_url = "/uploads/system/project/avatar/#{project.id}/banana_sample.gif"
 
-      expect(helper.project_icon(project.full_path).to_s).
-        to eq "<img src=\"#{avatar_url}\" alt=\"Banana sample\" />"
+      expect(helper.project_icon(project.full_path).to_s)
+        .to eq "<img src=\"#{avatar_url}\" alt=\"Banana sample\" />"
 
       allow(ActionController::Base).to receive(:asset_host).and_return(gitlab_host)
       avatar_url = "#{gitlab_host}/uploads/system/project/avatar/#{project.id}/banana_sample.gif"
 
-      expect(helper.project_icon(project.full_path).to_s).
-        to eq "<img src=\"#{avatar_url}\" alt=\"Banana sample\" />"
+      expect(helper.project_icon(project.full_path).to_s)
+        .to eq "<img src=\"#{avatar_url}\" alt=\"Banana sample\" />"
     end
 
     it 'gives uploaded icon when present' do
@@ -82,42 +82,71 @@ describe ApplicationHelper do
   end
 
   describe 'avatar_icon' do
-    it 'returns an url for the avatar' do
-      user = create(:user, avatar: File.open(uploaded_image_temp_path))
-
-      avatar_url = "/uploads/system/user/avatar/#{user.id}/banana_sample.gif"
-
-      expect(helper.avatar_icon(user.email).to_s).to match(avatar_url)
-
-      allow(ActionController::Base).to receive(:asset_host).and_return(gitlab_host)
-      avatar_url = "#{gitlab_host}/uploads/system/user/avatar/#{user.id}/banana_sample.gif"
-
-      expect(helper.avatar_icon(user.email).to_s).to match(avatar_url)
-    end
-
-    it 'returns an url for the avatar with relative url' do
-      stub_config_setting(relative_url_root: '/gitlab')
-      # Must be stubbed after the stub above, and separately
-      stub_config_setting(url: Settings.send(:build_gitlab_url))
-
-      user = create(:user, avatar: File.open(uploaded_image_temp_path))
-
-      expect(helper.avatar_icon(user.email).to_s).
-        to match("/gitlab/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
-    end
+    let(:user) { create(:user, avatar: File.open(uploaded_image_temp_path)) }
+
+    context 'using an email' do
+      context 'when there is a matching user' do
+        it 'returns a relative URL for the avatar' do
+          expect(helper.avatar_icon(user.email).to_s)
+            .to eq("/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
+        end
+
+        context 'when an asset_host is set in the config' do
+          let(:asset_host) { 'http://assets' }
+
+          before do
+            allow(ActionController::Base).to receive(:asset_host).and_return(asset_host)
+          end
+
+          it 'returns an absolute URL on that asset host' do
+            expect(helper.avatar_icon(user.email, only_path: false).to_s)
+              .to eq("#{asset_host}/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
+          end
+        end
+
+        context 'when only_path is set to false' do
+          it 'returns an absolute URL for the avatar' do
+            expect(helper.avatar_icon(user.email, only_path: false).to_s)
+              .to eq("#{gitlab_host}/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
+          end
+        end
+
+        context 'when the GitLab instance is at a relative URL' do
+          before do
+            stub_config_setting(relative_url_root: '/gitlab')
+            # Must be stubbed after the stub above, and separately
+            stub_config_setting(url: Settings.send(:build_gitlab_url))
+          end
+
+          it 'returns a relative URL with the correct prefix' do
+            expect(helper.avatar_icon(user.email).to_s)
+              .to eq("/gitlab/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
+          end
+        end
+      end
 
-    it 'calls gravatar_icon when no User exists with the given email' do
-      expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20, 2)
+      context 'when no user exists for the email' do
+        it 'calls gravatar_icon' do
+          expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20, 2)
 
-      helper.avatar_icon('foo@example.com', 20, 2)
+          helper.avatar_icon('foo@example.com', 20, 2)
+        end
+      end
     end
 
-    describe 'using a User' do
-      it 'returns an URL for the avatar' do
-        user = create(:user, avatar: File.open(uploaded_image_temp_path))
+    describe 'using a user' do
+      context 'when only_path is true' do
+        it 'returns a relative URL for the avatar' do
+          expect(helper.avatar_icon(user, only_path: true).to_s)
+            .to eq("/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
+        end
+      end
 
-        expect(helper.avatar_icon(user).to_s).
-          to match("/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
+      context 'when only_path is false' do
+        it 'returns an absolute URL for the avatar' do
+          expect(helper.avatar_icon(user, only_path: false).to_s)
+            .to eq("#{gitlab_host}/uploads/system/user/avatar/#{user.id}/banana_sample.gif")
+        end
       end
     end
   end
@@ -147,22 +176,22 @@ describe ApplicationHelper do
       it 'returns a valid Gravatar URL' do
         stub_config_setting(https: false)
 
-        expect(helper.gravatar_icon(user_email)).
-          to match('http://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118')
+        expect(helper.gravatar_icon(user_email))
+          .to match('http://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118')
       end
 
       it 'uses HTTPs when configured' do
         stub_config_setting(https: true)
 
-        expect(helper.gravatar_icon(user_email)).
-          to match('https://secure.gravatar.com')
+        expect(helper.gravatar_icon(user_email))
+          .to match('https://secure.gravatar.com')
       end
 
       it 'returns custom gravatar path when gravatar_url is set' do
         stub_gravatar_setting(plain_url: 'http://example.local/?s=%{size}&hash=%{hash}')
 
-        expect(gravatar_icon(user_email, 20)).
-          to eq('http://example.local/?s=40&hash=b58c6f14d292556214bd64909bcdb118')
+        expect(gravatar_icon(user_email, 20))
+          .to eq('http://example.local/?s=40&hash=b58c6f14d292556214bd64909bcdb118')
       end
 
       it 'accepts a custom size argument' do
@@ -234,8 +263,8 @@ describe ApplicationHelper do
     end
 
     it 'accepts a custom html_class' do
-      expect(element(html_class: 'custom_class').attr('class')).
-        to eq 'js-timeago custom_class'
+      expect(element(html_class: 'custom_class').attr('class'))
+        .to eq 'js-timeago custom_class'
     end
 
     it 'accepts a custom tooltip placement' do
diff --git a/spec/helpers/broadcast_messages_helper_spec.rb b/spec/helpers/broadcast_messages_helper_spec.rb
index c6e3c5c2368caddc87d3bd14811a989c03dedcb7..9bec0f9f432f5bd3ec67690b0e6d69f00881f3cf 100644
--- a/spec/helpers/broadcast_messages_helper_spec.rb
+++ b/spec/helpers/broadcast_messages_helper_spec.rb
@@ -33,8 +33,8 @@ describe BroadcastMessagesHelper do
     it 'allows custom style' do
       broadcast_message = double(color: '#f2dede', font: '#b94a48')
 
-      expect(helper.broadcast_message_style(broadcast_message)).
-        to match('background-color: #f2dede; color: #b94a48')
+      expect(helper.broadcast_message_style(broadcast_message))
+        .to match('background-color: #f2dede; color: #b94a48')
     end
   end
 
diff --git a/spec/helpers/commits_helper_spec.rb b/spec/helpers/commits_helper_spec.rb
index a2c008790f9bf1c396d4e0116523bde237941ee0..c245bb439db0b11c25783f32b70f34cb50f61296 100644
--- a/spec/helpers/commits_helper_spec.rb
+++ b/spec/helpers/commits_helper_spec.rb
@@ -9,8 +9,8 @@ describe CommitsHelper do
         author_email: 'my@email.com" onmouseover="alert(1)'
       )
 
-      expect(helper.commit_author_link(commit)).
-        not_to include('onmouseover="alert(1)"')
+      expect(helper.commit_author_link(commit))
+        .not_to include('onmouseover="alert(1)"')
     end
   end
 
@@ -22,8 +22,8 @@ describe CommitsHelper do
         committer_email: 'my@email.com" onmouseover="alert(1)'
       )
 
-      expect(helper.commit_committer_link(commit)).
-        not_to include('onmouseover="alert(1)"')
+      expect(helper.commit_committer_link(commit))
+        .not_to include('onmouseover="alert(1)"')
     end
   end
 
diff --git a/spec/helpers/diff_helper_spec.rb b/spec/helpers/diff_helper_spec.rb
index 0ac030d317143d3d47462c6d1eaa135ab4678796..0d909e6e1400f2b21d5bc0dc488155667eb20910 100644
--- a/spec/helpers/diff_helper_spec.rb
+++ b/spec/helpers/diff_helper_spec.rb
@@ -148,12 +148,21 @@ describe DiffHelper do
 
     it 'puts comments on added lines' do
       left = Gitlab::Diff::Line.new('\\nonewline', 'old-nonewline', 3, 3, 3)
-      right = Gitlab::Diff::Line.new('new line', 'add', 3, 3, 3)
+      right = Gitlab::Diff::Line.new('new line', 'new', 3, 3, 3)
 
       result = helper.parallel_diff_discussions(left, right, diff_file)
 
       expect(result).to eq([nil, 'comment'])
     end
+
+    it 'puts comments on unchanged lines' do
+      left = Gitlab::Diff::Line.new('unchanged line', nil, 3, 3, 3)
+      right = Gitlab::Diff::Line.new('unchanged line', nil, 3, 3, 3)
+
+      result = helper.parallel_diff_discussions(left, right, diff_file)
+
+      expect(result).to eq(['comment', nil])
+    end
   end
 
   describe "#diff_match_line" do
diff --git a/spec/helpers/form_helper_spec.rb b/spec/helpers/form_helper_spec.rb
index b20373a96fb4bef8a218882d4b84ab0d6916a2e3..18cf0031d5fd89a2b9f96193dc1e1d01809aaee3 100644
--- a/spec/helpers/form_helper_spec.rb
+++ b/spec/helpers/form_helper_spec.rb
@@ -11,18 +11,18 @@ describe FormHelper do
     it 'renders an alert div' do
       model = double(errors: errors_stub('Error 1'))
 
-      expect(helper.form_errors(model)).
-        to include('<div class="alert alert-danger" id="error_explanation">')
+      expect(helper.form_errors(model))
+        .to include('<div class="alert alert-danger" id="error_explanation">')
     end
 
     it 'contains a summary message' do
       single_error = double(errors: errors_stub('A'))
       multi_errors = double(errors: errors_stub('A', 'B', 'C'))
 
-      expect(helper.form_errors(single_error)).
-        to include('<h4>The form contains the following error:')
-      expect(helper.form_errors(multi_errors)).
-        to include('<h4>The form contains the following errors:')
+      expect(helper.form_errors(single_error))
+        .to include('<h4>The form contains the following error:')
+      expect(helper.form_errors(multi_errors))
+        .to include('<h4>The form contains the following errors:')
     end
 
     it 'renders each message' do
diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb
index 0337afa44529834b039a2b6d1fed5a273c8031a9..a7c06e577a2cf6e08a72ee4c3ab7c58764b74c0e 100644
--- a/spec/helpers/groups_helper_spec.rb
+++ b/spec/helpers/groups_helper_spec.rb
@@ -8,8 +8,8 @@ describe GroupsHelper do
       group = create(:group)
       group.avatar = fixture_file_upload(avatar_file_path)
       group.save!
-      expect(group_icon(group.path).to_s).
-        to match("/uploads/system/group/avatar/#{group.id}/banana_sample.gif")
+      expect(group_icon(group.path).to_s)
+        .to match("/uploads/system/group/avatar/#{group.id}/banana_sample.gif")
     end
 
     it 'gives default avatar_icon when no avatar is present' do
diff --git a/spec/helpers/import_helper_spec.rb b/spec/helpers/import_helper_spec.rb
index 10f293cddf599cf6fe5d2faf122e52cc776617ef..9afff47f4e9e1ec3de2b4cf58a2eae4dfd074db8 100644
--- a/spec/helpers/import_helper_spec.rb
+++ b/spec/helpers/import_helper_spec.rb
@@ -29,21 +29,21 @@ describe ImportHelper do
     context 'when provider is "github"' do
       context 'when provider does not specify a custom URL' do
         it 'uses default GitHub URL' do
-          allow(Gitlab.config.omniauth).to receive(:providers).
-          and_return([Settingslogic.new('name' => 'github')])
+          allow(Gitlab.config.omniauth).to receive(:providers)
+          .and_return([Settingslogic.new('name' => 'github')])
 
-          expect(helper.provider_project_link('github', 'octocat/Hello-World')).
-          to include('href="https://github.com/octocat/Hello-World"')
+          expect(helper.provider_project_link('github', 'octocat/Hello-World'))
+          .to include('href="https://github.com/octocat/Hello-World"')
         end
       end
 
       context 'when provider specify a custom URL' do
         it 'uses custom URL' do
-          allow(Gitlab.config.omniauth).to receive(:providers).
-          and_return([Settingslogic.new('name' => 'github', 'url' => 'https://github.company.com')])
+          allow(Gitlab.config.omniauth).to receive(:providers)
+          .and_return([Settingslogic.new('name' => 'github', 'url' => 'https://github.company.com')])
 
-          expect(helper.provider_project_link('github', 'octocat/Hello-World')).
-          to include('href="https://github.company.com/octocat/Hello-World"')
+          expect(helper.provider_project_link('github', 'octocat/Hello-World'))
+          .to include('href="https://github.company.com/octocat/Hello-World"')
         end
       end
     end
@@ -54,8 +54,8 @@ describe ImportHelper do
       end
 
       it 'uses given host' do
-        expect(helper.provider_project_link('gitea', 'octocat/Hello-World')).
-        to include('href="https://try.gitea.io/octocat/Hello-World"')
+        expect(helper.provider_project_link('gitea', 'octocat/Hello-World'))
+        .to include('href="https://try.gitea.io/octocat/Hello-World"')
       end
     end
   end
diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb
index 8fcf7f5fa15535df7b15b4b2563c9081baaf6e8f..15cb620199dfa5cfeeff368d896ad3e45ef13501 100644
--- a/spec/helpers/issuables_helper_spec.rb
+++ b/spec/helpers/issuables_helper_spec.rb
@@ -40,23 +40,23 @@ describe IssuablesHelper do
       end
 
       it 'returns "Open" when state is :opened' do
-        expect(helper.issuables_state_counter_text(:issues, :opened)).
-          to eq('<span>Open</span> <span class="badge">42</span>')
+        expect(helper.issuables_state_counter_text(:issues, :opened))
+          .to eq('<span>Open</span> <span class="badge">42</span>')
       end
 
       it 'returns "Closed" when state is :closed' do
-        expect(helper.issuables_state_counter_text(:issues, :closed)).
-          to eq('<span>Closed</span> <span class="badge">42</span>')
+        expect(helper.issuables_state_counter_text(:issues, :closed))
+          .to eq('<span>Closed</span> <span class="badge">42</span>')
       end
 
       it 'returns "Merged" when state is :merged' do
-        expect(helper.issuables_state_counter_text(:merge_requests, :merged)).
-          to eq('<span>Merged</span> <span class="badge">42</span>')
+        expect(helper.issuables_state_counter_text(:merge_requests, :merged))
+          .to eq('<span>Merged</span> <span class="badge">42</span>')
       end
 
       it 'returns "All" when state is :all' do
-        expect(helper.issuables_state_counter_text(:merge_requests, :all)).
-          to eq('<span>All</span> <span class="badge">42</span>')
+        expect(helper.issuables_state_counter_text(:merge_requests, :all))
+          .to eq('<span>All</span> <span class="badge">42</span>')
       end
     end
 
@@ -81,13 +81,13 @@ describe IssuablesHelper do
         expect(helper).to receive(:params).twice.and_return(params)
         expect(helper).to receive(:issuables_count_for_state).with(:issues, :opened).and_return(42)
 
-        expect(helper.issuables_state_counter_text(:issues, :opened)).
-          to eq('<span>Open</span> <span class="badge">42</span>')
+        expect(helper.issuables_state_counter_text(:issues, :opened))
+          .to eq('<span>Open</span> <span class="badge">42</span>')
 
         expect(helper).not_to receive(:issuables_count_for_state)
 
-        expect(helper.issuables_state_counter_text(:issues, :opened)).
-          to eq('<span>Open</span> <span class="badge">42</span>')
+        expect(helper.issuables_state_counter_text(:issues, :opened))
+          .to eq('<span>Open</span> <span class="badge">42</span>')
       end
 
       it 'does not take some keys into account in the cache key' do
@@ -100,8 +100,8 @@ describe IssuablesHelper do
         }.with_indifferent_access)
         expect(helper).to receive(:issuables_count_for_state).with(:issues, :opened).and_return(42)
 
-        expect(helper.issuables_state_counter_text(:issues, :opened)).
-          to eq('<span>Open</span> <span class="badge">42</span>')
+        expect(helper.issuables_state_counter_text(:issues, :opened))
+          .to eq('<span>Open</span> <span class="badge">42</span>')
 
         expect(helper).to receive(:params).and_return({
           author_id: '11',
@@ -112,22 +112,22 @@ describe IssuablesHelper do
         }.with_indifferent_access)
         expect(helper).not_to receive(:issuables_count_for_state)
 
-        expect(helper.issuables_state_counter_text(:issues, :opened)).
-          to eq('<span>Open</span> <span class="badge">42</span>')
+        expect(helper.issuables_state_counter_text(:issues, :opened))
+          .to eq('<span>Open</span> <span class="badge">42</span>')
       end
 
       it 'does not take params order into account in the cache key' do
         expect(helper).to receive(:params).and_return('author_id' => '11', 'state' => 'opened')
         expect(helper).to receive(:issuables_count_for_state).with(:issues, :opened).and_return(42)
 
-        expect(helper.issuables_state_counter_text(:issues, :opened)).
-          to eq('<span>Open</span> <span class="badge">42</span>')
+        expect(helper.issuables_state_counter_text(:issues, :opened))
+          .to eq('<span>Open</span> <span class="badge">42</span>')
 
         expect(helper).to receive(:params).and_return('state' => 'opened', 'author_id' => '11')
         expect(helper).not_to receive(:issuables_count_for_state)
 
-        expect(helper.issuables_state_counter_text(:issues, :opened)).
-          to eq('<span>Open</span> <span class="badge">42</span>')
+        expect(helper.issuables_state_counter_text(:issues, :opened))
+          .to eq('<span>Open</span> <span class="badge">42</span>')
       end
     end
   end
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
index 540cb0ab1e025f633dd3dcd4f93aec961ef6cebc..00db98fd9d21f302f94c7b663814e2caac6b6be0 100644
--- a/spec/helpers/issues_helper_spec.rb
+++ b/spec/helpers/issues_helper_spec.rb
@@ -93,8 +93,8 @@ describe IssuesHelper do
       award = build_stubbed(:award_emoji, user: build_stubbed(:user, name: 'Jane'))
       awards = Array.new(5, award).push(my_award)
 
-      expect(award_user_list(awards, current_user, limit: 2)).
-        to eq("You, Jane, and 4 more.")
+      expect(award_user_list(awards, current_user, limit: 2))
+        .to eq("You, Jane, and 4 more.")
     end
   end
 
diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb
index 7cf535fadae18c96c729aec385099adafec34dd4..a8d6044fda7edd3248a6d80080bf6219054704ac 100644
--- a/spec/helpers/labels_helper_spec.rb
+++ b/spec/helpers/labels_helper_spec.rb
@@ -55,8 +55,8 @@ describe LabelsHelper do
 
     context 'without block' do
       it 'uses render_colored_label as the link content' do
-        expect(self).to receive(:render_colored_label).
-          with(label, tooltip: true).and_return('Foo')
+        expect(self).to receive(:render_colored_label)
+          .with(label, tooltip: true).and_return('Foo')
         expect(link_to_label(label)).to match('Foo')
       end
     end
diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb
index 2a0de0b06569f6877da14a2c7165813e0cd33ca8..b4226f96a04bd32882b8a645ff3ca0b916791f0e 100644
--- a/spec/helpers/markup_helper_spec.rb
+++ b/spec/helpers/markup_helper_spec.rb
@@ -68,8 +68,8 @@ describe MarkupHelper do
       expect(doc.css('a')[0].text).to eq 'This should finally fix '
 
       # First issue link
-      expect(doc.css('a')[1].attr('href')).
-        to eq namespace_project_issue_path(project.namespace, project, issues[0])
+      expect(doc.css('a')[1].attr('href'))
+        .to eq namespace_project_issue_path(project.namespace, project, issues[0])
       expect(doc.css('a')[1].text).to eq issues[0].to_reference
 
       # Internal commit link
@@ -77,8 +77,8 @@ describe MarkupHelper do
       expect(doc.css('a')[2].text).to eq ' and '
 
       # Second issue link
-      expect(doc.css('a')[3].attr('href')).
-        to eq namespace_project_issue_path(project.namespace, project, issues[1])
+      expect(doc.css('a')[3].attr('href'))
+        .to eq namespace_project_issue_path(project.namespace, project, issues[1])
       expect(doc.css('a')[3].text).to eq issues[1].to_reference
 
       # Trailing commit link
@@ -98,8 +98,8 @@ describe MarkupHelper do
 
     it "escapes HTML passed in as the body" do
       actual = "This is a <h1>test</h1> - see #{issues[0].to_reference}"
-      expect(helper.link_to_gfm(actual, link)).
-        to match('&lt;h1&gt;test&lt;/h1&gt;')
+      expect(helper.link_to_gfm(actual, link))
+        .to match('&lt;h1&gt;test&lt;/h1&gt;')
     end
 
     it 'ignores reference links when they are the entire body' do
@@ -110,8 +110,8 @@ describe MarkupHelper do
 
     it 'replaces commit message with emoji to link' do
       actual = link_to_gfm(':book: Book', '/foo')
-      expect(actual).
-        to eq '<gl-emoji title="open book" data-name="book" data-unicode-version="6.0">📖</gl-emoji><a href="/foo"> Book</a>'
+      expect(actual)
+        .to eq '<gl-emoji title="open book" data-name="book" data-unicode-version="6.0">📖</gl-emoji><a href="/foo"> Book</a>'
     end
   end
 
diff --git a/spec/helpers/merge_requests_helper_spec.rb b/spec/helpers/merge_requests_helper_spec.rb
index f2c9d9273886471093e2d288e0676bf940ce05fa..493a4ff9a93c076f40ec1bdcf96a3ccd5345e110 100644
--- a/spec/helpers/merge_requests_helper_spec.rb
+++ b/spec/helpers/merge_requests_helper_spec.rb
@@ -15,8 +15,8 @@ describe MergeRequestsHelper do
     end
 
     it 'does not include api credentials in a link' do
-      allow(ci_service).
-        to receive(:build_page).and_return("http://secretuser:secretpass@jenkins.example.com:8888/job/test1/scm/bySHA1/12d65c")
+      allow(ci_service)
+        .to receive(:build_page).and_return("http://secretuser:secretpass@jenkins.example.com:8888/job/test1/scm/bySHA1/12d65c")
       expect(helper.ci_build_details_path(merge_request)).not_to match("secret")
     end
   end
diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb
index dff2784f21ff1d27908f69a7e6ba3c82a605f11c..95b4032616e079fe1a3f28b2f389bea87d17888f 100644
--- a/spec/helpers/page_layout_helper_spec.rb
+++ b/spec/helpers/page_layout_helper_spec.rb
@@ -86,8 +86,8 @@ describe PageLayoutHelper do
     it 'raises ArgumentError when given more than two attributes' do
       map = { foo: 'foo', bar: 'bar', baz: 'baz' }
 
-      expect { helper.page_card_attributes(map) }.
-        to raise_error(ArgumentError, /more than two attributes/)
+      expect { helper.page_card_attributes(map) }
+        .to raise_error(ArgumentError, /more than two attributes/)
     end
 
     it 'rejects blank values' do
diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb
index 2c0e9975f73f8d933c65dbdf10ccfd4025e5c365..a04c87b08eb77c1f8c06411c2c5d4c622bb2d125 100644
--- a/spec/helpers/preferences_helper_spec.rb
+++ b/spec/helpers/preferences_helper_spec.rb
@@ -29,15 +29,15 @@ describe PreferencesHelper do
   describe 'user_color_scheme' do
     context 'with a user' do
       it "returns user's scheme's css_class" do
-        allow(helper).to receive(:current_user).
-          and_return(double(color_scheme_id: 3))
+        allow(helper).to receive(:current_user)
+          .and_return(double(color_scheme_id: 3))
 
         expect(helper.user_color_scheme).to eq 'solarized-light'
       end
 
       it 'returns the default when id is invalid' do
-        allow(helper).to receive(:current_user).
-          and_return(double(color_scheme_id: Gitlab::ColorSchemes.count + 5))
+        allow(helper).to receive(:current_user)
+          .and_return(double(color_scheme_id: Gitlab::ColorSchemes.count + 5))
       end
     end
 
@@ -45,8 +45,8 @@ describe PreferencesHelper do
       it 'returns the default theme' do
         stub_user
 
-        expect(helper.user_color_scheme).
-          to eq Gitlab::ColorSchemes.default.css_class
+        expect(helper.user_color_scheme)
+          .to eq Gitlab::ColorSchemes.default.css_class
       end
     end
   end
@@ -55,8 +55,8 @@ describe PreferencesHelper do
     if messages.empty?
       allow(helper).to receive(:current_user).and_return(nil)
     else
-      allow(helper).to receive(:current_user).
-        and_return(double('user', messages))
+      allow(helper).to receive(:current_user)
+        .and_return(double('user', messages))
     end
   end
 
diff --git a/spec/javascripts/commit/pipelines/pipelines_spec.js b/spec/javascripts/commit/pipelines/pipelines_spec.js
index ebfd60198b28e4d28345af3c808f7caaf1780835..694f94efcffcb9e415ea7e04f67574b3393fd78f 100644
--- a/spec/javascripts/commit/pipelines/pipelines_spec.js
+++ b/spec/javascripts/commit/pipelines/pipelines_spec.js
@@ -1,15 +1,15 @@
 import Vue from 'vue';
-import PipelinesTable from '~/commit/pipelines/pipelines_table';
+import pipelinesTable from '~/commit/pipelines/pipelines_table.vue';
 
 describe('Pipelines table in Commits and Merge requests', () => {
   const jsonFixtureName = 'pipelines/pipelines.json';
   let pipeline;
+  let PipelinesTable;
 
-  preloadFixtures('static/pipelines_table.html.raw');
   preloadFixtures(jsonFixtureName);
 
   beforeEach(() => {
-    loadFixtures('static/pipelines_table.html.raw');
+    PipelinesTable = Vue.extend(pipelinesTable);
     const pipelines = getJSONFixture(jsonFixtureName).pipelines;
     pipeline = pipelines.find(p => p.id === 1);
   });
@@ -26,8 +26,11 @@ describe('Pipelines table in Commits and Merge requests', () => {
         Vue.http.interceptors.push(pipelinesEmptyResponse);
 
         this.component = new PipelinesTable({
-          el: document.querySelector('#commit-pipeline-table-view'),
-        });
+          propsData: {
+            endpoint: 'endpoint',
+            helpPagePath: 'foo',
+          },
+        }).$mount();
       });
 
       afterEach(function () {
@@ -58,8 +61,11 @@ describe('Pipelines table in Commits and Merge requests', () => {
         Vue.http.interceptors.push(pipelinesResponse);
 
         this.component = new PipelinesTable({
-          el: document.querySelector('#commit-pipeline-table-view'),
-        });
+          propsData: {
+            endpoint: 'endpoint',
+            helpPagePath: 'foo',
+          },
+        }).$mount();
       });
 
       afterEach(() => {
@@ -92,8 +98,11 @@ describe('Pipelines table in Commits and Merge requests', () => {
       Vue.http.interceptors.push(pipelinesErrorResponse);
 
       this.component = new PipelinesTable({
-        el: document.querySelector('#commit-pipeline-table-view'),
-      });
+        propsData: {
+          endpoint: 'endpoint',
+          helpPagePath: 'foo',
+        },
+      }).$mount();
     });
 
     afterEach(function () {
diff --git a/spec/javascripts/datetime_utility_spec.js b/spec/javascripts/datetime_utility_spec.js
index e54ea11b08caa2fa7a3c4714a7992b54dffe93b9..3391cade5415d5cc98dd1784781292e78a6c8ca7 100644
--- a/spec/javascripts/datetime_utility_spec.js
+++ b/spec/javascripts/datetime_utility_spec.js
@@ -16,6 +16,10 @@ import { timeIntervalInWords } from '~/lib/utils/datetime_utility';
         const date = new Date();
         date.setFullYear(date.getFullYear() + 1);
 
+        // Add a day to prevent a transient error. If date is even 1 second
+        // short of a full year, timeFor will return '11 months remaining'
+        date.setDate(date.getDate() + 1);
+
         expect(
           gl.utils.timeFor(date),
         ).toBe('1 year remaining');
diff --git a/spec/javascripts/deploy_keys/components/key_spec.js b/spec/javascripts/deploy_keys/components/key_spec.js
index a4b98f6140ddf0b86fc934a5879002f1f3598be0..5b64cbb2dfc6f06723ffe86251afb8944f332099 100644
--- a/spec/javascripts/deploy_keys/components/key_spec.js
+++ b/spec/javascripts/deploy_keys/components/key_spec.js
@@ -14,6 +14,7 @@ describe('Deploy keys key', () => {
       propsData: {
         deployKey,
         store,
+        endpoint: 'https://test.host/dummy/endpoint',
       },
     }).$mount();
   };
diff --git a/spec/javascripts/deploy_keys/components/keys_panel_spec.js b/spec/javascripts/deploy_keys/components/keys_panel_spec.js
index a69b39c35c4f1a3a14317ef26731958a1a84ddf8..08357d2b547207c09c9b205489b1f63e2ca9bd0e 100644
--- a/spec/javascripts/deploy_keys/components/keys_panel_spec.js
+++ b/spec/javascripts/deploy_keys/components/keys_panel_spec.js
@@ -17,6 +17,7 @@ describe('Deploy keys panel', () => {
         keys: data.enabled_keys,
         showHelpBox: true,
         store,
+        endpoint: 'https://test.host/dummy/endpoint',
       },
     }).$mount();
 
diff --git a/spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js
index c92a147b937c1e5540e2670f0d000c1c8ae79f45..9e2076dc383128685805a8bb84e04c262e2196b5 100644
--- a/spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js
@@ -4,6 +4,10 @@ import '~/filtered_search/filtered_search_tokenizer';
 import '~/filtered_search/filtered_search_dropdown_manager';
 
 describe('Filtered Search Dropdown Manager', () => {
+  beforeEach(() => {
+    spyOn(jQuery, 'ajax');
+  });
+
   describe('addWordToInput', () => {
     function getInputValue() {
       return document.querySelector('.filtered-search').value;
diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
index 6d00d71f145476e77a48312cd6e5e5d4818b76c9..8d239c9cc3f0206f98fceb3267713c6b54bed157 100644
--- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
@@ -1,6 +1,7 @@
 import * as recentSearchesStoreSrc from '~/filtered_search/stores/recent_searches_store';
 import RecentSearchesService from '~/filtered_search/services/recent_searches_service';
 import RecentSearchesServiceError from '~/filtered_search/services/recent_searches_service_error';
+import RecentSearchesRoot from '~/filtered_search/recent_searches_root';
 import '~/lib/utils/url_utility';
 import '~/lib/utils/common_utils';
 import '~/filtered_search/filtered_search_token_keys';
@@ -71,6 +72,7 @@ describe('Filtered Search Manager', () => {
     beforeEach(() => {
       spyOn(RecentSearchesService, 'isAvailable').and.returnValue(isLocalStorageAvailable);
       spyOn(recentSearchesStoreSrc, 'default');
+      spyOn(RecentSearchesRoot.prototype, 'render');
 
       filteredSearchManager = new gl.FilteredSearchManager();
       filteredSearchManager.setup();
@@ -104,6 +106,7 @@ describe('Filtered Search Manager', () => {
 
     it('should blur button', () => {
       const e = {
+        preventDefault: () => {},
         currentTarget: {
           blur: () => {},
         },
@@ -116,6 +119,7 @@ describe('Filtered Search Manager', () => {
 
     it('should not call search if there is no state', () => {
       const e = {
+        preventDefault: () => {},
         currentTarget: {
           blur: () => {},
         },
@@ -127,6 +131,7 @@ describe('Filtered Search Manager', () => {
 
     it('should call search when there is state', () => {
       const e = {
+        preventDefault: () => {},
         currentTarget: {
           blur: () => {},
           dataset: {
diff --git a/spec/javascripts/fixtures/merge_requests.rb b/spec/javascripts/fixtures/merge_requests.rb
index a746a77654887a978b25c4b26f621a97c1656508..0715f4d5f6b372a128a8067cf3bae2f6bb71ece2 100644
--- a/spec/javascripts/fixtures/merge_requests.rb
+++ b/spec/javascripts/fixtures/merge_requests.rb
@@ -55,13 +55,20 @@ describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :cont
     render_merge_request(example.description, merge_request)
   end
 
+  it 'merge_requests/changes_tab_with_comments.json' do |example|
+    create(:diff_note_on_merge_request, project: project, author: admin, position: position, noteable: merge_request)
+    create(:note_on_merge_request, author: admin, project: project, noteable: merge_request)
+    render_merge_request(example.description, merge_request, action: :diffs, format: :json)
+  end
+
   private
 
-  def render_merge_request(fixture_file_name, merge_request)
-    get :show,
+  def render_merge_request(fixture_file_name, merge_request, action: :show, format: :html)
+    get action,
       namespace_id: project.namespace.to_param,
       project_id: project,
-      id: merge_request.to_param
+      id: merge_request.to_param,
+      format: format
 
     expect(response).to be_success
     store_frontend_fixture(response, fixture_file_name)
diff --git a/spec/javascripts/fixtures/pipelines_table.html.haml b/spec/javascripts/fixtures/pipelines_table.html.haml
deleted file mode 100644
index ad1682704bbec8e0260cf25b7dc07dc99fb6bf31..0000000000000000000000000000000000000000
--- a/spec/javascripts/fixtures/pipelines_table.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-#commit-pipeline-table-view{ data: { endpoint: "endpoint", "help-page-path": "foo" } }
diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js
index 2ccc4f161925fea06d6cb8531b67ed8b75a4eb80..276e01fc82ffa64c13c2af6ef911be8e6e449039 100644
--- a/spec/javascripts/issue_show/components/app_spec.js
+++ b/spec/javascripts/issue_show/components/app_spec.js
@@ -51,7 +51,6 @@ describe('Issuable output', () => {
   });
 
   afterEach(() => {
-    Vue.http.interceptors = _.without(Vue.http.interceptors, issueShowInterceptor);
   });
 
   it('should render a title/description/edited and update title/description/edited on update', (done) => {
diff --git a/spec/javascripts/issue_show/components/description_spec.js b/spec/javascripts/issue_show/components/description_spec.js
index 408349cc42da656899715de46aff006cd6b43d1f..f3fdbff01a6070d8bac360933b6a16b4d8fe72d6 100644
--- a/spec/javascripts/issue_show/components/description_spec.js
+++ b/spec/javascripts/issue_show/components/description_spec.js
@@ -95,5 +95,33 @@ describe('Description component', () => {
         done();
       });
     });
+
+    it('clears task status text when no tasks are present', (done) => {
+      vm.taskStatus = '0 of 0';
+
+      setTimeout(() => {
+        expect(
+          document.querySelector('.issuable-meta #task_status').textContent.trim(),
+        ).toBe('');
+
+        done();
+      });
+    });
+  });
+
+  it('applies syntax highlighting and math when description changed', (done) => {
+    spyOn(vm, 'renderGFM').and.callThrough();
+    spyOn($.prototype, 'renderGFM').and.callThrough();
+    vm.descriptionHtml = 'changed';
+
+    Vue.nextTick(() => {
+      setTimeout(() => {
+        expect(vm.$refs['gfm-content']).toBeDefined();
+        expect(vm.renderGFM).toHaveBeenCalled();
+        expect($.prototype.renderGFM).toHaveBeenCalled();
+
+        done();
+      });
+    });
   });
 });
diff --git a/spec/javascripts/merge_request_notes_spec.js b/spec/javascripts/merge_request_notes_spec.js
index e54acfa8e4403c80453ca9f08621475c40a73648..b6d0ce02c4f0474aeb78fd0b2910d5f98908e8bf 100644
--- a/spec/javascripts/merge_request_notes_spec.js
+++ b/spec/javascripts/merge_request_notes_spec.js
@@ -7,54 +7,92 @@ import '~/render_gfm';
 import '~/render_math';
 import '~/notes';
 
+const upArrowKeyCode = 38;
+
 describe('Merge request notes', () => {
   window.gon = window.gon || {};
   window.gl = window.gl || {};
   gl.utils = gl.utils || {};
 
-  const fixture = 'merge_requests/diff_comment.html.raw';
-  preloadFixtures(fixture);
+  const discussionTabFixture = 'merge_requests/diff_comment.html.raw';
+  const changesTabJsonFixture = 'merge_requests/changes_tab_with_comments.json';
+  preloadFixtures(discussionTabFixture, changesTabJsonFixture);
 
-  beforeEach(() => {
-    loadFixtures(fixture);
-    gl.utils.disableButtonIfEmptyField = _.noop;
-    window.project_uploads_path = 'http://test.host/uploads';
-    $('body').data('page', 'projects:merge_requests:show');
-    window.gon.current_user_id = $('.note:last').data('author-id');
+  describe('Discussion tab with diff comments', () => {
+    beforeEach(() => {
+      loadFixtures(discussionTabFixture);
+      gl.utils.disableButtonIfEmptyField = _.noop;
+      window.project_uploads_path = 'http://test.host/uploads';
+      $('body').data('page', 'projects:merge_requests:show');
+      window.gon.current_user_id = $('.note:last').data('author-id');
 
-    return new Notes('', []);
-  });
+      return new Notes('', []);
+    });
+
+    describe('up arrow', () => {
+      it('edits last comment when triggered in main form', () => {
+        const upArrowEvent = $.Event('keydown');
+        upArrowEvent.which = upArrowKeyCode;
+
+        spyOnEvent('.note:last .js-note-edit', 'click');
+
+        $('.js-note-text').trigger(upArrowEvent);
+
+        expect('click').toHaveBeenTriggeredOn('.note:last .js-note-edit');
+      });
+
+      it('edits last comment in discussion when triggered in discussion form', (done) => {
+        const upArrowEvent = $.Event('keydown');
+        upArrowEvent.which = upArrowKeyCode;
+
+        spyOnEvent('.note-discussion .js-note-edit', 'click');
+
+        $('.js-discussion-reply-button').click();
 
-  describe('up arrow', () => {
-    it('edits last comment when triggered in main form', () => {
-      const upArrowEvent = $.Event('keydown');
-      upArrowEvent.which = 38;
+        setTimeout(() => {
+          expect(
+            $('.note-discussion .js-note-text'),
+          ).toExist();
 
-      spyOnEvent('.note:last .js-note-edit', 'click');
+          $('.note-discussion .js-note-text').trigger(upArrowEvent);
 
-      $('.js-note-text').trigger(upArrowEvent);
+          expect('click').toHaveBeenTriggeredOn('.note-discussion .js-note-edit');
 
-      expect('click').toHaveBeenTriggeredOn('.note:last .js-note-edit');
+          done();
+        });
+      });
     });
+  });
 
-    it('edits last comment in discussion when triggered in discussion form', (done) => {
-      const upArrowEvent = $.Event('keydown');
-      upArrowEvent.which = 38;
+  describe('Changes tab with diff comments', () => {
+    beforeEach(() => {
+      const diffsResponse = getJSONFixture(changesTabJsonFixture);
+      const noteFormHtml = `<form class="js-new-note-form">
+        <textarea class="js-note-text"></textarea>
+      </form>`;
+      setFixtures(diffsResponse.html + noteFormHtml);
+      $('body').data('page', 'projects:merge_requests:show');
+      window.gon.current_user_id = $('.note:last').data('author-id');
+
+      return new Notes('', []);
+    });
 
-      spyOnEvent('.note-discussion .js-note-edit', 'click');
+    describe('up arrow', () => {
+      it('edits last comment in discussion when triggered in discussion form', (done) => {
+        const upArrowEvent = $.Event('keydown');
+        upArrowEvent.which = upArrowKeyCode;
 
-      $('.js-discussion-reply-button').click();
+        spyOnEvent('.note:last .js-note-edit', 'click');
 
-      setTimeout(() => {
-        expect(
-          $('.note-discussion .js-note-text'),
-        ).toExist();
+        $('.js-discussion-reply-button').trigger('click');
 
-        $('.note-discussion .js-note-text').trigger(upArrowEvent);
+        setTimeout(() => {
+          $('.js-note-text').trigger(upArrowEvent);
 
-        expect('click').toHaveBeenTriggeredOn('.note-discussion .js-note-edit');
+          expect('click').toHaveBeenTriggeredOn('.note:last .js-note-edit');
 
-        done();
+          done();
+        });
       });
     });
   });
diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js
index c6f218e4dacf697542882d9ec8f1a5f807e1c5fa..5ece4ed080b3d12cce8f0f1460dccf8014edc508 100644
--- a/spec/javascripts/notes_spec.js
+++ b/spec/javascripts/notes_spec.js
@@ -176,7 +176,7 @@ import '~/notes';
 
         Notes.updateNoteTargetSelector($note);
 
-        expect($note.toggleClass).toHaveBeenCalledWith('target', null);
+        expect($note.toggleClass).toHaveBeenCalledWith('target', false);
       });
     });
 
@@ -595,46 +595,46 @@ import '~/notes';
       });
     });
 
-    describe('hasSlashCommands', () => {
+    describe('hasQuickActions', () => {
       beforeEach(() => {
         this.notes = new Notes('', []);
       });
 
-      it('should return true when comment begins with a slash command', () => {
+      it('should return true when comment begins with a quick action', () => {
         const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign Merging this';
-        const hasSlashCommands = this.notes.hasSlashCommands(sampleComment);
+        const hasQuickActions = this.notes.hasQuickActions(sampleComment);
 
-        expect(hasSlashCommands).toBeTruthy();
+        expect(hasQuickActions).toBeTruthy();
       });
 
-      it('should return false when comment does NOT begin with a slash command', () => {
+      it('should return false when comment does NOT begin with a quick action', () => {
         const sampleComment = 'Hey, /unassign Merging this';
-        const hasSlashCommands = this.notes.hasSlashCommands(sampleComment);
+        const hasQuickActions = this.notes.hasQuickActions(sampleComment);
 
-        expect(hasSlashCommands).toBeFalsy();
+        expect(hasQuickActions).toBeFalsy();
       });
 
-      it('should return false when comment does NOT have any slash commands', () => {
+      it('should return false when comment does NOT have any quick actions', () => {
         const sampleComment = 'Looking good, Awesome!';
-        const hasSlashCommands = this.notes.hasSlashCommands(sampleComment);
+        const hasQuickActions = this.notes.hasQuickActions(sampleComment);
 
-        expect(hasSlashCommands).toBeFalsy();
+        expect(hasQuickActions).toBeFalsy();
       });
     });
 
-    describe('stripSlashCommands', () => {
-      it('should strip slash commands from the comment which begins with a slash command', () => {
+    describe('stripQuickActions', () => {
+      it('should strip quick actions from the comment which begins with a quick action', () => {
         this.notes = new Notes();
         const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign Merging this';
-        const stripedComment = this.notes.stripSlashCommands(sampleComment);
+        const stripedComment = this.notes.stripQuickActions(sampleComment);
 
         expect(stripedComment).toBe('');
       });
 
-      it('should strip slash commands from the comment but leaves plain comment if it is present', () => {
+      it('should strip quick actions from the comment but leaves plain comment if it is present', () => {
         this.notes = new Notes();
         const sampleComment = '/wip\n/milestone %1.0\n/merge\n/unassign\nMerging this';
-        const stripedComment = this.notes.stripSlashCommands(sampleComment);
+        const stripedComment = this.notes.stripQuickActions(sampleComment);
 
         expect(stripedComment).toBe('Merging this');
       });
@@ -642,14 +642,14 @@ import '~/notes';
       it('should NOT strip string that has slashes within', () => {
         this.notes = new Notes();
         const sampleComment = 'http://127.0.0.1:3000/root/gitlab-shell/issues/1';
-        const stripedComment = this.notes.stripSlashCommands(sampleComment);
+        const stripedComment = this.notes.stripQuickActions(sampleComment);
 
         expect(stripedComment).toBe(sampleComment);
       });
     });
 
-    describe('getSlashCommandDescription', () => {
-      const availableSlashCommands = [
+    describe('getQuickActionDescription', () => {
+      const availableQuickActions = [
         { name: 'close', description: 'Close this issue', params: [] },
         { name: 'title', description: 'Change title', params: [{}] },
         { name: 'estimate', description: 'Set time estimate', params: [{}] }
@@ -659,19 +659,19 @@ import '~/notes';
         this.notes = new Notes();
       });
 
-      it('should return executing slash command description when note has single slash command', () => {
+      it('should return executing quick action description when note has single quick action', () => {
         const sampleComment = '/close';
-        expect(this.notes.getSlashCommandDescription(sampleComment, availableSlashCommands)).toBe('Applying command to close this issue');
+        expect(this.notes.getQuickActionDescription(sampleComment, availableQuickActions)).toBe('Applying command to close this issue');
       });
 
-      it('should return generic multiple slash command description when note has multiple slash commands', () => {
+      it('should return generic multiple quick action description when note has multiple quick actions', () => {
         const sampleComment = '/close\n/title [Duplicate] Issue foobar';
-        expect(this.notes.getSlashCommandDescription(sampleComment, availableSlashCommands)).toBe('Applying multiple commands');
+        expect(this.notes.getQuickActionDescription(sampleComment, availableQuickActions)).toBe('Applying multiple commands');
       });
 
-      it('should return generic slash command description when available slash commands list is not populated', () => {
+      it('should return generic quick action description when available quick actions list is not populated', () => {
         const sampleComment = '/close\n/title [Duplicate] Issue foobar';
-        expect(this.notes.getSlashCommandDescription(sampleComment)).toBe('Applying command');
+        expect(this.notes.getQuickActionDescription(sampleComment)).toBe('Applying command');
       });
     });
 
diff --git a/spec/javascripts/pipelines/async_button_spec.js b/spec/javascripts/pipelines/async_button_spec.js
index 28c9c7ab2829c800e6adfa7101da62641702e690..486208983579869ff87b631928dae01e4aa6eec3 100644
--- a/spec/javascripts/pipelines/async_button_spec.js
+++ b/spec/javascripts/pipelines/async_button_spec.js
@@ -1,25 +1,20 @@
 import Vue from 'vue';
 import asyncButtonComp from '~/pipelines/components/async_button.vue';
+import eventHub from '~/pipelines/event_hub';
 
 describe('Pipelines Async Button', () => {
   let component;
-  let spy;
   let AsyncButtonComponent;
 
   beforeEach(() => {
     AsyncButtonComponent = Vue.extend(asyncButtonComp);
 
-    spy = jasmine.createSpy('spy').and.returnValue(Promise.resolve());
-
     component = new AsyncButtonComponent({
       propsData: {
         endpoint: '/foo',
         title: 'Foo',
         icon: 'fa fa-foo',
         cssClass: 'bar',
-        service: {
-          postAction: spy,
-        },
       },
     }).$mount();
   });
@@ -33,7 +28,7 @@ describe('Pipelines Async Button', () => {
   });
 
   it('should render the provided title', () => {
-    expect(component.$el.getAttribute('title')).toContain('Foo');
+    expect(component.$el.getAttribute('data-original-title')).toContain('Foo');
     expect(component.$el.getAttribute('aria-label')).toContain('Foo');
   });
 
@@ -41,37 +36,12 @@ describe('Pipelines Async Button', () => {
     expect(component.$el.getAttribute('class')).toContain('bar');
   });
 
-  it('should call the service when it is clicked with the provided endpoint', () => {
-    component.$el.click();
-    expect(spy).toHaveBeenCalledWith('/foo');
-  });
-
-  it('should hide loading if request fails', () => {
-    spy = jasmine.createSpy('spy').and.returnValue(Promise.reject());
-
-    component = new AsyncButtonComponent({
-      propsData: {
-        endpoint: '/foo',
-        title: 'Foo',
-        icon: 'fa fa-foo',
-        cssClass: 'bar',
-        dataAttributes: {
-          'data-foo': 'foo',
-        },
-        service: {
-          postAction: spy,
-        },
-      },
-    }).$mount();
-
-    component.$el.click();
-    expect(component.$el.querySelector('.fa-spinner')).toBe(null);
-  });
-
   describe('With confirm dialog', () => {
     it('should call the service when confimation is positive', () => {
       spyOn(window, 'confirm').and.returnValue(true);
-      spy = jasmine.createSpy('spy').and.returnValue(Promise.resolve());
+      eventHub.$on('postAction', (endpoint) => {
+        expect(endpoint).toEqual('/foo');
+      });
 
       component = new AsyncButtonComponent({
         propsData: {
@@ -79,15 +49,11 @@ describe('Pipelines Async Button', () => {
           title: 'Foo',
           icon: 'fa fa-foo',
           cssClass: 'bar',
-          service: {
-            postAction: spy,
-          },
           confirmActionMessage: 'bar',
         },
       }).$mount();
 
       component.$el.click();
-      expect(spy).toHaveBeenCalledWith('/foo');
     });
   });
 });
diff --git a/spec/javascripts/pipelines/pipelines_actions_spec.js b/spec/javascripts/pipelines/pipelines_actions_spec.js
index 8a58b77f1e3e25b441fcb7486e45e05b68f8d9cf..72fb0a8f9efc52536cbcf154e83363419f60fde1 100644
--- a/spec/javascripts/pipelines/pipelines_actions_spec.js
+++ b/spec/javascripts/pipelines/pipelines_actions_spec.js
@@ -3,7 +3,6 @@ import pipelinesActionsComp from '~/pipelines/components/pipelines_actions.vue';
 
 describe('Pipelines Actions dropdown', () => {
   let component;
-  let spy;
   let actions;
   let ActionsComponent;
 
@@ -22,14 +21,9 @@ describe('Pipelines Actions dropdown', () => {
       },
     ];
 
-    spy = jasmine.createSpy('spy').and.returnValue(Promise.resolve());
-
     component = new ActionsComponent({
       propsData: {
         actions,
-        service: {
-          postAction: spy,
-        },
       },
     }).$mount();
   });
@@ -40,31 +34,6 @@ describe('Pipelines Actions dropdown', () => {
     ).toEqual(actions.length);
   });
 
-  it('should call the service when an action is clicked', () => {
-    component.$el.querySelector('.js-pipeline-dropdown-manual-actions').click();
-    component.$el.querySelector('.js-pipeline-action-link').click();
-
-    expect(spy).toHaveBeenCalledWith(actions[0].path);
-  });
-
-  it('should hide loading if request fails', () => {
-    spy = jasmine.createSpy('spy').and.returnValue(Promise.reject());
-
-    component = new ActionsComponent({
-      propsData: {
-        actions,
-        service: {
-          postAction: spy,
-        },
-      },
-    }).$mount();
-
-    component.$el.querySelector('.js-pipeline-dropdown-manual-actions').click();
-    component.$el.querySelector('.js-pipeline-action-link').click();
-
-    expect(component.$el.querySelector('.fa-spinner')).toEqual(null);
-  });
-
   it('should render a disabled action when it\'s not playable', () => {
     expect(
       component.$el.querySelector('.dropdown-menu li:last-child button').getAttribute('disabled'),
diff --git a/spec/javascripts/vue_shared/components/pipelines_table_row_spec.js b/spec/javascripts/pipelines/pipelines_table_row_spec.js
similarity index 98%
rename from spec/javascripts/vue_shared/components/pipelines_table_row_spec.js
rename to spec/javascripts/pipelines/pipelines_table_row_spec.js
index 9475ee28a03e2d6c3b92a4bb2fcd389b44bb739d..7ce39dca112432126451df3bd014c637d694831a 100644
--- a/spec/javascripts/vue_shared/components/pipelines_table_row_spec.js
+++ b/spec/javascripts/pipelines/pipelines_table_row_spec.js
@@ -1,5 +1,5 @@
 import Vue from 'vue';
-import tableRowComp from '~/vue_shared/components/pipelines_table_row.vue';
+import tableRowComp from '~/pipelines/components/pipelines_table_row.vue';
 
 describe('Pipelines Table Row', () => {
   const jsonFixtureName = 'pipelines/pipelines.json';
diff --git a/spec/javascripts/vue_shared/components/pipelines_table_spec.js b/spec/javascripts/pipelines/pipelines_table_spec.js
similarity index 90%
rename from spec/javascripts/vue_shared/components/pipelines_table_spec.js
rename to spec/javascripts/pipelines/pipelines_table_spec.js
index 4c35d702004af85d93488a6a1eaaed04ba100e2c..3afe89c8db4d61ed96a6f1dfc9b15f23fa97298c 100644
--- a/spec/javascripts/vue_shared/components/pipelines_table_spec.js
+++ b/spec/javascripts/pipelines/pipelines_table_spec.js
@@ -1,5 +1,5 @@
 import Vue from 'vue';
-import pipelinesTableComp from '~/vue_shared/components/pipelines_table.vue';
+import pipelinesTableComp from '~/pipelines/components/pipelines_table.vue';
 import '~/lib/utils/datetime_utility';
 
 describe('Pipelines Table', () => {
@@ -22,7 +22,6 @@ describe('Pipelines Table', () => {
       component = new PipelinesTableComponent({
         propsData: {
           pipelines: [],
-          service: {},
         },
       }).$mount();
     });
@@ -48,7 +47,6 @@ describe('Pipelines Table', () => {
       const component = new PipelinesTableComponent({
         propsData: {
           pipelines: [],
-          service: {},
         },
       }).$mount();
       expect(component.$el.querySelectorAll('.commit.gl-responsive-table-row').length).toEqual(0);
@@ -58,10 +56,8 @@ describe('Pipelines Table', () => {
   describe('with data', () => {
     it('should render rows', () => {
       const component = new PipelinesTableComponent({
-        el: document.querySelector('.test-dom-element'),
         propsData: {
           pipelines: [pipeline],
-          service: {},
         },
       }).$mount();
 
diff --git a/spec/javascripts/project_title_spec.js b/spec/javascripts/project_title_spec.js
index 3dba2e817ffcb10d1cb7c1d0727bf07c87aa81ac..cc336180ff77fd90c400e4eae850a6f9c0e20e3b 100644
--- a/spec/javascripts/project_title_spec.js
+++ b/spec/javascripts/project_title_spec.js
@@ -1,4 +1,3 @@
-/* eslint-disable space-before-function-paren, no-unused-expressions, no-return-assign, no-param-reassign, no-var, new-cap, wrap-iife, no-unused-vars, quotes, jasmine/no-expect-in-setup-teardown, max-len */
 /* global Project */
 
 import 'select2/select2';
@@ -7,47 +6,52 @@ import '~/api';
 import '~/project_select';
 import '~/project';
 
-(function() {
-  describe('Project Title', function() {
-    preloadFixtures('issues/open-issue.html.raw');
-    loadJSONFixtures('projects.json');
+describe('Project Title', () => {
+  preloadFixtures('issues/open-issue.html.raw');
+  loadJSONFixtures('projects.json');
 
-    beforeEach(function() {
-      loadFixtures('issues/open-issue.html.raw');
+  beforeEach(() => {
+    loadFixtures('issues/open-issue.html.raw');
 
-      window.gon = {};
-      window.gon.api_version = 'v3';
+    window.gon = {};
+    window.gon.api_version = 'v3';
 
-      return this.project = new Project();
-    });
+    // eslint-disable-next-line no-new
+    new Project();
+  });
 
-    describe('project list', function() {
-      var fakeAjaxResponse = function fakeAjaxResponse(req) {
-        var d;
-        expect(req.url).toBe('/api/v3/projects.json?simple=true');
-        expect(req.data).toEqual({ search: '', order_by: 'last_activity_at', per_page: 20, membership: true });
-        d = $.Deferred();
-        d.resolve(this.projects_data);
-        return d.promise();
-      };
-
-      beforeEach((function(_this) {
-        return function() {
-          _this.projects_data = getJSONFixture('projects.json');
-          return spyOn(jQuery, 'ajax').and.callFake(fakeAjaxResponse.bind(_this));
-        };
-      })(this));
-      it('toggles dropdown', function() {
-        var menu = $('.js-dropdown-menu-projects');
-        $('.js-projects-dropdown-toggle').click();
-        expect(menu).toHaveClass('open');
-        menu.find('.dropdown-menu-close-icon').click();
-        expect(menu).not.toHaveClass('open');
+  describe('project list', () => {
+    let reqUrl;
+    let reqData;
+
+    beforeEach(() => {
+      const fakeResponseData = getJSONFixture('projects.json');
+      spyOn(jQuery, 'ajax').and.callFake((req) => {
+        const def = $.Deferred();
+        reqUrl = req.url;
+        reqData = req.data;
+        def.resolve(fakeResponseData);
+        return def.promise();
       });
     });
 
-    afterEach(() => {
-      window.gon = {};
+    it('toggles dropdown', () => {
+      const $menu = $('.js-dropdown-menu-projects');
+      $('.js-projects-dropdown-toggle').click();
+      expect($menu).toHaveClass('open');
+      expect(reqUrl).toBe('/api/v3/projects.json?simple=true');
+      expect(reqData).toEqual({
+        search: '',
+        order_by: 'last_activity_at',
+        per_page: 20,
+        membership: true,
+      });
+      $menu.find('.dropdown-menu-close-icon').click();
+      expect($menu).not.toHaveClass('open');
     });
   });
-}).call(window);
+
+  afterEach(() => {
+    window.gon = {};
+  });
+});
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
index 2c34402576bb7497131c0257ef1a79544b7a5100..f0d51bd0902b64e12c8b30ca9867982dcf3e2efe 100644
--- a/spec/javascripts/test_bundle.js
+++ b/spec/javascripts/test_bundle.js
@@ -1,8 +1,14 @@
+/* eslint-disable jasmine/no-global-setup */
 import $ from 'jquery';
 import _ from 'underscore';
 import 'jasmine-jquery';
 import '~/commons';
 
+import Vue from 'vue';
+import VueResource from 'vue-resource';
+
+Vue.use(VueResource);
+
 // enable test fixtures
 jasmine.getFixtures().fixturesPath = '/base/spec/javascripts/fixtures';
 jasmine.getJSONFixtures().fixturesPath = '/base/spec/javascripts/fixtures';
@@ -16,6 +22,32 @@ window.gl = window.gl || {};
 window.gl.TEST_HOST = 'http://test.host';
 window.gon = window.gon || {};
 
+// HACK: Chrome 59 disconnects if there are too many synchronous tests in a row
+// because it appears to lock up the thread that communicates to Karma's socket
+// This async beforeEach gets called on every spec and releases the JS thread long
+// enough for the socket to continue to communicate.
+// The downside is that it creates a minor performance penalty in the time it takes
+// to run our unit tests.
+beforeEach(done => done());
+
+beforeAll(() => {
+  const origError = console.error;
+  spyOn(console, 'error').and.callFake((message) => {
+    if (/^\[Vue warn\]/.test(message)) {
+      fail(message);
+    } else {
+      origError(message);
+    }
+  });
+});
+
+const builtinVueHttpInterceptors = Vue.http.interceptors.slice();
+
+beforeEach(() => {
+  // restore interceptors so we have no remaining ones from previous tests
+  Vue.http.interceptors = builtinVueHttpInterceptors.slice();
+});
+
 // render all of our tests
 const testsContext = require.context('.', true, /_spec$/);
 testsContext.keys().forEach(function (path) {
diff --git a/spec/lib/banzai/cross_project_reference_spec.rb b/spec/lib/banzai/cross_project_reference_spec.rb
index deaabceef1cb670d74a68fed1024bed4d3ed7196..787212581e2e6146e72d13f11a31572e9554a6f7 100644
--- a/spec/lib/banzai/cross_project_reference_spec.rb
+++ b/spec/lib/banzai/cross_project_reference_spec.rb
@@ -24,8 +24,8 @@ describe Banzai::CrossProjectReference, lib: true do
       it 'returns the referenced project' do
         project2 = double('referenced project')
 
-        expect(Project).to receive(:find_by_full_path).
-          with('cross/reference').and_return(project2)
+        expect(Project).to receive(:find_by_full_path)
+          .with('cross/reference').and_return(project2)
 
         expect(project_from_ref('cross/reference')).to eq project2
       end
diff --git a/spec/lib/banzai/filter/abstract_reference_filter_spec.rb b/spec/lib/banzai/filter/abstract_reference_filter_spec.rb
index 787c2372c5b9f908ea2c6d822e65d65a1be52475..27532f96f5635bf411928592a839bf1a072922f5 100644
--- a/spec/lib/banzai/filter/abstract_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/abstract_reference_filter_spec.rb
@@ -23,11 +23,11 @@ describe Banzai::Filter::AbstractReferenceFilter do
       doc = Nokogiri::HTML.fragment('')
       filter = described_class.new(doc, project: project)
 
-      expect(filter).to receive(:references_per_project).
-        and_return({ project.path_with_namespace => Set.new(%w[1]) })
+      expect(filter).to receive(:references_per_project)
+        .and_return({ project.path_with_namespace => Set.new(%w[1]) })
 
-      expect(filter.projects_per_reference).
-        to eq({ project.path_with_namespace => project })
+      expect(filter.projects_per_reference)
+        .to eq({ project.path_with_namespace => project })
     end
   end
 
@@ -37,26 +37,26 @@ describe Banzai::Filter::AbstractReferenceFilter do
 
     context 'with RequestStore disabled' do
       it 'returns a list of Projects for a list of paths' do
-        expect(filter.find_projects_for_paths([project.path_with_namespace])).
-          to eq([project])
+        expect(filter.find_projects_for_paths([project.path_with_namespace]))
+          .to eq([project])
       end
 
       it "return an empty array for paths that don't exist" do
-        expect(filter.find_projects_for_paths(['nonexistent/project'])).
-          to eq([])
+        expect(filter.find_projects_for_paths(['nonexistent/project']))
+          .to eq([])
       end
     end
 
     context 'with RequestStore enabled', :request_store do
       it 'returns a list of Projects for a list of paths' do
-        expect(filter.find_projects_for_paths([project.path_with_namespace])).
-          to eq([project])
+        expect(filter.find_projects_for_paths([project.path_with_namespace]))
+          .to eq([project])
       end
 
       context "when no project with that path exists" do
         it "returns no value" do
-          expect(filter.find_projects_for_paths(['nonexistent/project'])).
-            to eq([])
+          expect(filter.find_projects_for_paths(['nonexistent/project']))
+            .to eq([])
         end
 
         it "adds the ref to the project refs cache" do
@@ -75,8 +75,8 @@ describe Banzai::Filter::AbstractReferenceFilter do
           end
 
           it "return an empty array for paths that don't exist" do
-            expect(filter.find_projects_for_paths(['nonexistent/project'])).
-              to eq([])
+            expect(filter.find_projects_for_paths(['nonexistent/project']))
+              .to eq([])
           end
         end
       end
diff --git a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
index deadc36524cc72ba76069231d7c8f00bd195c290..fc67c7ec3c4e1869ab07ff985abccc311b9242e1 100644
--- a/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_range_reference_filter_spec.rb
@@ -28,15 +28,15 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
     it 'links to a valid two-dot reference' do
       doc = reference_filter("See #{reference2}")
 
-      expect(doc.css('a').first.attr('href')).
-        to eq urls.namespace_project_compare_url(project.namespace, project, range2.to_param)
+      expect(doc.css('a').first.attr('href'))
+        .to eq urls.namespace_project_compare_url(project.namespace, project, range2.to_param)
     end
 
     it 'links to a valid three-dot reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).
-        to eq urls.namespace_project_compare_url(project.namespace, project, range.to_param)
+      expect(doc.css('a').first.attr('href'))
+        .to eq urls.namespace_project_compare_url(project.namespace, project, range.to_param)
     end
 
     it 'links to a valid short ID' do
@@ -105,15 +105,15 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).
-        to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
+      expect(doc.css('a').first.attr('href'))
+        .to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
     end
 
     it 'link has valid text' do
       doc = reference_filter("Fixed (#{reference}.)")
 
-      expect(doc.css('a').first.text).
-        to eql("#{project2.path_with_namespace}@#{commit1.short_id}...#{commit2.short_id}")
+      expect(doc.css('a').first.text)
+        .to eql("#{project2.path_with_namespace}@#{commit1.short_id}...#{commit2.short_id}")
     end
 
     it 'has valid text' do
@@ -140,15 +140,15 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).
-        to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
+      expect(doc.css('a').first.attr('href'))
+        .to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
     end
 
     it 'link has valid text' do
       doc = reference_filter("Fixed (#{reference}.)")
 
-      expect(doc.css('a').first.text).
-        to eql("#{project2.path}@#{commit1.short_id}...#{commit2.short_id}")
+      expect(doc.css('a').first.text)
+        .to eql("#{project2.path}@#{commit1.short_id}...#{commit2.short_id}")
     end
 
     it 'has valid text' do
@@ -175,15 +175,15 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).
-        to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
+      expect(doc.css('a').first.attr('href'))
+        .to eq urls.namespace_project_compare_url(project2.namespace, project2, range.to_param)
     end
 
     it 'link has valid text' do
       doc = reference_filter("Fixed (#{reference}.)")
 
-      expect(doc.css('a').first.text).
-        to eql("#{project2.path}@#{commit1.short_id}...#{commit2.short_id}")
+      expect(doc.css('a').first.text)
+        .to eql("#{project2.path}@#{commit1.short_id}...#{commit2.short_id}")
     end
 
     it 'has valid text' do
@@ -214,8 +214,8 @@ describe Banzai::Filter::CommitRangeReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).
-        to eq reference
+      expect(doc.css('a').first.attr('href'))
+        .to eq reference
     end
 
     it 'links with adjacent text' do
diff --git a/spec/lib/banzai/filter/commit_reference_filter_spec.rb b/spec/lib/banzai/filter/commit_reference_filter_spec.rb
index a19aac61229cd2d08e4027e65a96a64a8c425747..c4d8d3b66826f46025ec64b5a91a479c54246e1a 100644
--- a/spec/lib/banzai/filter/commit_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_reference_filter_spec.rb
@@ -26,8 +26,8 @@ describe Banzai::Filter::CommitReferenceFilter, lib: true do
         doc = reference_filter("See #{reference[0...size]}")
 
         expect(doc.css('a').first.text).to eq commit.short_id
-        expect(doc.css('a').first.attr('href')).
-          to eq urls.namespace_project_commit_url(project.namespace, project, reference)
+        expect(doc.css('a').first.attr('href'))
+          .to eq urls.namespace_project_commit_url(project.namespace, project, reference)
       end
     end
 
@@ -180,8 +180,8 @@ describe Banzai::Filter::CommitReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).
-        to eq urls.namespace_project_commit_url(project2.namespace, project2, commit.id)
+      expect(doc.css('a').first.attr('href'))
+        .to eq urls.namespace_project_commit_url(project2.namespace, project2, commit.id)
     end
 
     it 'links with adjacent text' do
diff --git a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
index 76cefe112fb163173d848544bde53d13aa1a8a82..a4bb043f8f182e1f075f6a3608575223c9d2f893 100644
--- a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
@@ -58,8 +58,8 @@ describe Banzai::Filter::ExternalIssueReferenceFilter, lib: true do
     end
 
     it 'escapes the title attribute' do
-      allow(project.external_issue_tracker).to receive(:title).
-        and_return(%{"></a>whatever<a title="})
+      allow(project.external_issue_tracker).to receive(:title)
+        .and_return(%{"></a>whatever<a title="})
 
       doc = filter("Issue #{reference}")
       expect(doc.text).to eq "Issue #{reference}"
diff --git a/spec/lib/banzai/filter/issue_reference_filter_spec.rb b/spec/lib/banzai/filter/issue_reference_filter_spec.rb
index f1082495fcc4c921d982dae86965f82baa8d3738..e5c1deb338bcc09d2edc7d9e0f48f506c3eab275 100644
--- a/spec/lib/banzai/filter/issue_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/issue_reference_filter_spec.rb
@@ -49,8 +49,8 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("Fixed #{reference}")
 
-      expect(doc.css('a').first.attr('href')).
-        to eq helper.url_for_issue(issue.iid, project)
+      expect(doc.css('a').first.attr('href'))
+        .to eq helper.url_for_issue(issue.iid, project)
     end
 
     it 'links with adjacent text' do
@@ -137,9 +137,9 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
     let(:reference) { "#{project2.path_with_namespace}##{issue.iid}" }
 
     it 'ignores valid references when cross-reference project uses external tracker' do
-      expect_any_instance_of(described_class).to receive(:find_object).
-        with(project2, issue.iid).
-        and_return(nil)
+      expect_any_instance_of(described_class).to receive(:find_object)
+        .with(project2, issue.iid)
+        .and_return(nil)
 
       exp = act = "Issue #{reference}"
       expect(reference_filter(act).to_html).to eq exp
@@ -148,8 +148,8 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).
-        to eq helper.url_for_issue(issue.iid, project2)
+      expect(doc.css('a').first.attr('href'))
+        .to eq helper.url_for_issue(issue.iid, project2)
     end
 
     it 'link has valid text' do
@@ -181,9 +181,9 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
     let(:reference) { "#{project2.path_with_namespace}##{issue.iid}" }
 
     it 'ignores valid references when cross-reference project uses external tracker' do
-      expect_any_instance_of(described_class).to receive(:find_object).
-        with(project2, issue.iid).
-        and_return(nil)
+      expect_any_instance_of(described_class).to receive(:find_object)
+        .with(project2, issue.iid)
+        .and_return(nil)
 
       exp = act = "Issue #{reference}"
       expect(reference_filter(act).to_html).to eq exp
@@ -192,8 +192,8 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).
-        to eq helper.url_for_issue(issue.iid, project2)
+      expect(doc.css('a').first.attr('href'))
+        .to eq helper.url_for_issue(issue.iid, project2)
     end
 
     it 'link has valid text' do
@@ -225,9 +225,9 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
     let(:reference) { "#{project2.path}##{issue.iid}" }
 
     it 'ignores valid references when cross-reference project uses external tracker' do
-      expect_any_instance_of(described_class).to receive(:find_object).
-        with(project2, issue.iid).
-        and_return(nil)
+      expect_any_instance_of(described_class).to receive(:find_object)
+        .with(project2, issue.iid)
+        .and_return(nil)
 
       exp = act = "Issue #{reference}"
       expect(reference_filter(act).to_html).to eq exp
@@ -236,8 +236,8 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).
-        to eq helper.url_for_issue(issue.iid, project2)
+      expect(doc.css('a').first.attr('href'))
+        .to eq helper.url_for_issue(issue.iid, project2)
     end
 
     it 'link has valid text' do
@@ -270,8 +270,8 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).
-        to eq reference
+      expect(doc.css('a').first.attr('href'))
+        .to eq reference
     end
 
     it 'links with adjacent text' do
@@ -292,8 +292,8 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference_link}")
 
-      expect(doc.css('a').first.attr('href')).
-        to eq helper.url_for_issue(issue.iid, project2)
+      expect(doc.css('a').first.attr('href'))
+        .to eq helper.url_for_issue(issue.iid, project2)
     end
 
     it 'links with adjacent text' do
@@ -314,8 +314,8 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference_link}")
 
-      expect(doc.css('a').first.attr('href')).
-        to eq helper.url_for_issue(issue.iid, project2) + "#note_123"
+      expect(doc.css('a').first.attr('href'))
+        .to eq helper.url_for_issue(issue.iid, project2) + "#note_123"
     end
 
     it 'links with adjacent text' do
@@ -330,14 +330,14 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
         doc = Nokogiri::HTML.fragment('')
         filter = described_class.new(doc, project: project)
 
-        expect(filter).to receive(:projects_per_reference).
-          and_return({ project.path_with_namespace => project })
+        expect(filter).to receive(:projects_per_reference)
+          .and_return({ project.path_with_namespace => project })
 
-        expect(filter).to receive(:references_per_project).
-          and_return({ project.path_with_namespace => Set.new([issue.iid]) })
+        expect(filter).to receive(:references_per_project)
+          .and_return({ project.path_with_namespace => Set.new([issue.iid]) })
 
-        expect(filter.issues_per_project).
-          to eq({ project => { issue.iid => issue } })
+        expect(filter.issues_per_project)
+          .to eq({ project => { issue.iid => issue } })
       end
     end
 
@@ -348,14 +348,14 @@ describe Banzai::Filter::IssueReferenceFilter, lib: true do
 
         expect(project).to receive(:default_issues_tracker?).and_return(false)
 
-        expect(filter).to receive(:projects_per_reference).
-          and_return({ project.path_with_namespace => project })
+        expect(filter).to receive(:projects_per_reference)
+          .and_return({ project.path_with_namespace => project })
 
-        expect(filter).to receive(:references_per_project).
-          and_return({ project.path_with_namespace => Set.new([1]) })
+        expect(filter).to receive(:references_per_project)
+          .and_return({ project.path_with_namespace => Set.new([1]) })
 
-        expect(filter.issues_per_project[project][1]).
-          to be_an_instance_of(ExternalIssue)
+        expect(filter.issues_per_project[project][1])
+          .to be_an_instance_of(ExternalIssue)
       end
     end
   end
diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb
index 284641fb20a9216e7e308b28dc38e24a38981087..cb3cf982351f564c06774f705844bf132ce880da 100644
--- a/spec/lib/banzai/filter/label_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb
@@ -72,8 +72,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).to eq urls.
-        namespace_project_issues_url(project.namespace, project, label_name: label.name)
+      expect(doc.css('a').first.attr('href')).to eq urls
+        .namespace_project_issues_url(project.namespace, project, label_name: label.name)
     end
 
     it 'links with adjacent text' do
@@ -95,8 +95,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).to eq urls.
-        namespace_project_issues_url(project.namespace, project, label_name: label.name)
+      expect(doc.css('a').first.attr('href')).to eq urls
+        .namespace_project_issues_url(project.namespace, project, label_name: label.name)
       expect(doc.text).to eq 'See gfm'
     end
 
@@ -119,8 +119,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).to eq urls.
-        namespace_project_issues_url(project.namespace, project, label_name: label.name)
+      expect(doc.css('a').first.attr('href')).to eq urls
+        .namespace_project_issues_url(project.namespace, project, label_name: label.name)
       expect(doc.text).to eq 'See 2fa'
     end
 
@@ -143,8 +143,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).to eq urls.
-        namespace_project_issues_url(project.namespace, project, label_name: label.name)
+      expect(doc.css('a').first.attr('href')).to eq urls
+        .namespace_project_issues_url(project.namespace, project, label_name: label.name)
       expect(doc.text).to eq 'See ?g.fm&'
     end
 
@@ -168,8 +168,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).to eq urls.
-        namespace_project_issues_url(project.namespace, project, label_name: label.name)
+      expect(doc.css('a').first.attr('href')).to eq urls
+        .namespace_project_issues_url(project.namespace, project, label_name: label.name)
       expect(doc.text).to eq 'See gfm references'
     end
 
@@ -192,8 +192,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).to eq urls.
-        namespace_project_issues_url(project.namespace, project, label_name: label.name)
+      expect(doc.css('a').first.attr('href')).to eq urls
+        .namespace_project_issues_url(project.namespace, project, label_name: label.name)
       expect(doc.text).to eq 'See 2 factor authentication'
     end
 
@@ -216,8 +216,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).to eq urls.
-        namespace_project_issues_url(project.namespace, project, label_name: label.name)
+      expect(doc.css('a').first.attr('href')).to eq urls
+        .namespace_project_issues_url(project.namespace, project, label_name: label.name)
       expect(doc.text).to eq 'See g.fm & references?'
     end
 
@@ -287,8 +287,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).to eq urls.
-        namespace_project_issues_url(project.namespace, project, label_name: label.name)
+      expect(doc.css('a').first.attr('href')).to eq urls
+        .namespace_project_issues_url(project.namespace, project, label_name: label.name)
     end
 
     it 'links with adjacent text' do
@@ -324,8 +324,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
       it 'links to a valid reference' do
         doc = reference_filter("See #{reference}", project: project)
 
-        expect(doc.css('a').first.attr('href')).to eq urls.
-          namespace_project_issues_url(project.namespace, project, label_name: group_label.name)
+        expect(doc.css('a').first.attr('href')).to eq urls
+          .namespace_project_issues_url(project.namespace, project, label_name: group_label.name)
         expect(doc.text).to eq 'See gfm references'
       end
 
@@ -347,8 +347,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
       it 'links to a valid reference' do
         doc = reference_filter("See #{reference}", project: project)
 
-        expect(doc.css('a').first.attr('href')).to eq urls.
-          namespace_project_issues_url(project.namespace, project, label_name: group_label.name)
+        expect(doc.css('a').first.attr('href')).to eq urls
+          .namespace_project_issues_url(project.namespace, project, label_name: group_label.name)
         expect(doc.text).to eq "See gfm references"
       end
 
@@ -447,8 +447,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
     end
 
     it 'has valid color' do
-      expect(result.css('a span').first.attr('style')).
-        to match /background-color: #00ff00/
+      expect(result.css('a span').first.attr('style'))
+        .to match /background-color: #00ff00/
     end
 
     it 'has valid link text' do
@@ -483,18 +483,18 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
     end
 
     it 'has valid color' do
-      expect(result.css('a span').first.attr('style')).
-        to match /background-color: #00ff00/
+      expect(result.css('a span').first.attr('style'))
+        .to match /background-color: #00ff00/
     end
 
     it 'has valid link text' do
-      expect(result.css('a').first.text).
-        to eq "#{group_label.name} in #{another_project.name_with_namespace}"
+      expect(result.css('a').first.text)
+        .to eq "#{group_label.name} in #{another_project.name_with_namespace}"
     end
 
     it 'has valid text' do
-      expect(result.text).
-        to eq "See #{group_label.name} in #{another_project.name_with_namespace}"
+      expect(result.text)
+        .to eq "See #{group_label.name} in #{another_project.name_with_namespace}"
     end
 
     it 'ignores invalid IDs on the referenced label' do
@@ -513,25 +513,25 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
     let!(:result)          { reference_filter("See #{reference}", project: project) }
 
     it 'points to referenced project issues page' do
-      expect(result.css('a').first.attr('href')).
-        to eq urls.namespace_project_issues_url(another_project.namespace,
+      expect(result.css('a').first.attr('href'))
+        .to eq urls.namespace_project_issues_url(another_project.namespace,
                                                  another_project,
                                                  label_name: group_label.name)
     end
 
     it 'has valid color' do
-      expect(result.css('a span').first.attr('style')).
-        to match /background-color: #00ff00/
+      expect(result.css('a span').first.attr('style'))
+        .to match /background-color: #00ff00/
     end
 
     it 'has valid link text' do
-      expect(result.css('a').first.text).
-        to eq "#{group_label.name} in #{another_project.name}"
+      expect(result.css('a').first.text)
+        .to eq "#{group_label.name} in #{another_project.name}"
     end
 
     it 'has valid text' do
-      expect(result.text).
-        to eq "See #{group_label.name} in #{another_project.name}"
+      expect(result.text)
+        .to eq "See #{group_label.name} in #{another_project.name}"
     end
 
     it 'ignores invalid IDs on the referenced label' do
@@ -590,8 +590,8 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do
     end
 
     it 'has valid color' do
-      expect(result.css('a span').first.attr('style')).
-        to match /background-color: #00ff00/
+      expect(result.css('a span').first.attr('style'))
+        .to match /background-color: #00ff00/
     end
 
     it 'has valid link text' do
diff --git a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
index 40232f6e4269eafcf298fcf7aca18ff0b5824f95..cd91681551eaaae38eac20c09976c9440c0bd6e2 100644
--- a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
@@ -36,8 +36,8 @@ describe Banzai::Filter::MergeRequestReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).to eq urls.
-        namespace_project_merge_request_url(project.namespace, project, merge)
+      expect(doc.css('a').first.attr('href')).to eq urls
+        .namespace_project_merge_request_url(project.namespace, project, merge)
     end
 
     it 'links with adjacent text' do
@@ -107,8 +107,8 @@ describe Banzai::Filter::MergeRequestReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).
-        to eq urls.namespace_project_merge_request_url(project2.namespace,
+      expect(doc.css('a').first.attr('href'))
+        .to eq urls.namespace_project_merge_request_url(project2.namespace,
                                                        project2, merge)
     end
 
@@ -141,8 +141,8 @@ describe Banzai::Filter::MergeRequestReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).
-        to eq urls.namespace_project_merge_request_url(project2.namespace,
+      expect(doc.css('a').first.attr('href'))
+        .to eq urls.namespace_project_merge_request_url(project2.namespace,
                                                       project2, merge)
     end
 
@@ -175,8 +175,8 @@ describe Banzai::Filter::MergeRequestReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).
-        to eq urls.namespace_project_merge_request_url(project2.namespace,
+      expect(doc.css('a').first.attr('href'))
+        .to eq urls.namespace_project_merge_request_url(project2.namespace,
                                                       project2, merge)
     end
 
@@ -208,8 +208,8 @@ describe Banzai::Filter::MergeRequestReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).
-        to eq reference
+      expect(doc.css('a').first.attr('href'))
+        .to eq reference
     end
 
     it 'links with adjacent text' do
diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
index a317c751d3275ebc5034e61bc38c9c2b2161e3f8..fe88b9cb73ececc1ca67572d82ef27987532e202 100644
--- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
@@ -44,16 +44,16 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
     link = doc.css('a').first.attr('href')
 
     expect(link).not_to match %r(https?://)
-    expect(link).to eq urls.
-      namespace_project_milestone_path(project.namespace, project, milestone)
+    expect(link).to eq urls
+      .namespace_project_milestone_path(project.namespace, project, milestone)
   end
 
   context 'Integer-based references' do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).to eq urls.
-        namespace_project_milestone_url(project.namespace, project, milestone)
+      expect(doc.css('a').first.attr('href')).to eq urls
+        .namespace_project_milestone_url(project.namespace, project, milestone)
     end
 
     it 'links with adjacent text' do
@@ -75,8 +75,8 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).to eq urls.
-        namespace_project_milestone_url(project.namespace, project, milestone)
+      expect(doc.css('a').first.attr('href')).to eq urls
+        .namespace_project_milestone_url(project.namespace, project, milestone)
       expect(doc.text).to eq 'See gfm'
     end
 
@@ -99,8 +99,8 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).to eq urls.
-        namespace_project_milestone_url(project.namespace, project, milestone)
+      expect(doc.css('a').first.attr('href')).to eq urls
+        .namespace_project_milestone_url(project.namespace, project, milestone)
       expect(doc.text).to eq 'See gfm references'
     end
 
@@ -122,8 +122,8 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).to eq urls.
-        namespace_project_milestone_url(project.namespace, project, milestone)
+      expect(doc.css('a').first.attr('href')).to eq urls
+        .namespace_project_milestone_url(project.namespace, project, milestone)
     end
 
     it 'links with adjacent text' do
@@ -156,8 +156,8 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
     let!(:result)         { reference_filter("See #{reference}") }
 
     it 'points to referenced project milestone page' do
-      expect(result.css('a').first.attr('href')).to eq urls.
-        namespace_project_milestone_url(another_project.namespace,
+      expect(result.css('a').first.attr('href')).to eq urls
+        .namespace_project_milestone_url(another_project.namespace,
                                         another_project,
                                         milestone)
     end
@@ -165,15 +165,15 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
     it 'link has valid text' do
       doc = reference_filter("See (#{reference}.)")
 
-      expect(doc.css('a').first.text).
-        to eq("#{milestone.name} in #{another_project.path_with_namespace}")
+      expect(doc.css('a').first.text)
+        .to eq("#{milestone.name} in #{another_project.path_with_namespace}")
     end
 
     it 'has valid text' do
       doc = reference_filter("See (#{reference}.)")
 
-      expect(doc.text).
-        to eq("See (#{milestone.name} in #{another_project.path_with_namespace}.)")
+      expect(doc.text)
+        .to eq("See (#{milestone.name} in #{another_project.path_with_namespace}.)")
     end
 
     it 'escapes the name attribute' do
@@ -181,8 +181,8 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
 
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.text).
-        to eq "#{milestone.name} in #{another_project.path_with_namespace}"
+      expect(doc.css('a').first.text)
+        .to eq "#{milestone.name} in #{another_project.path_with_namespace}"
     end
   end
 
@@ -195,8 +195,8 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
     let!(:result)         { reference_filter("See #{reference}") }
 
     it 'points to referenced project milestone page' do
-      expect(result.css('a').first.attr('href')).to eq urls.
-        namespace_project_milestone_url(another_project.namespace,
+      expect(result.css('a').first.attr('href')).to eq urls
+        .namespace_project_milestone_url(another_project.namespace,
                                         another_project,
                                         milestone)
     end
@@ -204,15 +204,15 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
     it 'link has valid text' do
       doc = reference_filter("See (#{reference}.)")
 
-      expect(doc.css('a').first.text).
-        to eq("#{milestone.name} in #{another_project.path}")
+      expect(doc.css('a').first.text)
+        .to eq("#{milestone.name} in #{another_project.path}")
     end
 
     it 'has valid text' do
       doc = reference_filter("See (#{reference}.)")
 
-      expect(doc.text).
-        to eq("See (#{milestone.name} in #{another_project.path}.)")
+      expect(doc.text)
+        .to eq("See (#{milestone.name} in #{another_project.path}.)")
     end
 
     it 'escapes the name attribute' do
@@ -220,8 +220,8 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
 
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.text).
-        to eq "#{milestone.name} in #{another_project.path}"
+      expect(doc.css('a').first.text)
+        .to eq "#{milestone.name} in #{another_project.path}"
     end
   end
 
@@ -234,8 +234,8 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
     let!(:result)         { reference_filter("See #{reference}") }
 
     it 'points to referenced project milestone page' do
-      expect(result.css('a').first.attr('href')).to eq urls.
-        namespace_project_milestone_url(another_project.namespace,
+      expect(result.css('a').first.attr('href')).to eq urls
+        .namespace_project_milestone_url(another_project.namespace,
                                         another_project,
                                         milestone)
     end
@@ -243,15 +243,15 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
     it 'link has valid text' do
       doc = reference_filter("See (#{reference}.)")
 
-      expect(doc.css('a').first.text).
-        to eq("#{milestone.name} in #{another_project.path}")
+      expect(doc.css('a').first.text)
+        .to eq("#{milestone.name} in #{another_project.path}")
     end
 
     it 'has valid text' do
       doc = reference_filter("See (#{reference}.)")
 
-      expect(doc.text).
-        to eq("See (#{milestone.name} in #{another_project.path}.)")
+      expect(doc.text)
+        .to eq("See (#{milestone.name} in #{another_project.path}.)")
     end
 
     it 'escapes the name attribute' do
@@ -259,8 +259,8 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
 
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.text).
-        to eq "#{milestone.name} in #{another_project.path}"
+      expect(doc.css('a').first.text)
+        .to eq "#{milestone.name} in #{another_project.path}"
     end
   end
 end
diff --git a/spec/lib/banzai/filter/redactor_filter_spec.rb b/spec/lib/banzai/filter/redactor_filter_spec.rb
index 97504aebed5d0b019af56464b597b0074375af40..b81cdbb89570bae5a4f4e41104d86942e747ab1a 100644
--- a/spec/lib/banzai/filter/redactor_filter_spec.rb
+++ b/spec/lib/banzai/filter/redactor_filter_spec.rb
@@ -33,9 +33,9 @@ describe Banzai::Filter::RedactorFilter, lib: true do
     end
 
     before do
-      allow(Banzai::ReferenceParser).to receive(:[]).
-        with('test').
-        and_return(parser_class)
+      allow(Banzai::ReferenceParser).to receive(:[])
+        .with('test')
+        .and_return(parser_class)
     end
 
     context 'valid projects' do
diff --git a/spec/lib/banzai/filter/reference_filter_spec.rb b/spec/lib/banzai/filter/reference_filter_spec.rb
index 55e681f6fafd96b482f2868ce31f34062c647258..ba0fa4a609aa4b8fd0d56c07e923def980280346 100644
--- a/spec/lib/banzai/filter/reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/reference_filter_spec.rb
@@ -8,8 +8,8 @@ describe Banzai::Filter::ReferenceFilter, lib: true do
       document = Nokogiri::HTML.fragment('<a href="foo">foo</a>')
       filter = described_class.new(document, project: project)
 
-      expect { |b| filter.each_node(&b) }.
-        to yield_with_args(an_instance_of(Nokogiri::XML::Element))
+      expect { |b| filter.each_node(&b) }
+        .to yield_with_args(an_instance_of(Nokogiri::XML::Element))
     end
 
     it 'returns an Enumerator when no block is given' do
diff --git a/spec/lib/banzai/filter/relative_link_filter_spec.rb b/spec/lib/banzai/filter/relative_link_filter_spec.rb
index 1957ba739e267edc14b454f2f7ced2adf7b8b661..1ce7bd7706eba2dfdb21aafbceaa6c6451f0f91f 100644
--- a/spec/lib/banzai/filter/relative_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/relative_link_filter_spec.rb
@@ -72,15 +72,15 @@ describe Banzai::Filter::RelativeLinkFilter, lib: true do
 
   it 'ignores ref if commit is passed' do
     doc = filter(link('non/existent.file'), commit: project.commit('empty-branch') )
-    expect(doc.at_css('a')['href']).
-      to eq "/#{project_path}/#{ref}/non/existent.file" # non-existent files have no leading blob/raw/tree
+    expect(doc.at_css('a')['href'])
+      .to eq "/#{project_path}/#{ref}/non/existent.file" # non-existent files have no leading blob/raw/tree
   end
 
   shared_examples :valid_repository do
     it 'rebuilds absolute URL for a file in the repo' do
       doc = filter(link('/doc/api/README.md'))
-      expect(doc.at_css('a')['href']).
-        to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
+      expect(doc.at_css('a')['href'])
+        .to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
     end
 
     it 'ignores absolute URLs with two leading slashes' do
@@ -90,71 +90,71 @@ describe Banzai::Filter::RelativeLinkFilter, lib: true do
 
     it 'rebuilds relative URL for a file in the repo' do
       doc = filter(link('doc/api/README.md'))
-      expect(doc.at_css('a')['href']).
-        to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
+      expect(doc.at_css('a')['href'])
+        .to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
     end
 
     it 'rebuilds relative URL for a file in the repo with leading ./' do
       doc = filter(link('./doc/api/README.md'))
-      expect(doc.at_css('a')['href']).
-        to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
+      expect(doc.at_css('a')['href'])
+        .to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
     end
 
     it 'rebuilds relative URL for a file in the repo up one directory' do
       relative_link = link('../api/README.md')
       doc = filter(relative_link, requested_path: 'doc/update/7.14-to-8.0.md')
 
-      expect(doc.at_css('a')['href']).
-        to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
+      expect(doc.at_css('a')['href'])
+        .to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
     end
 
     it 'rebuilds relative URL for a file in the repo up multiple directories' do
       relative_link = link('../../../api/README.md')
       doc = filter(relative_link, requested_path: 'doc/foo/bar/baz/README.md')
 
-      expect(doc.at_css('a')['href']).
-        to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
+      expect(doc.at_css('a')['href'])
+        .to eq "/#{project_path}/blob/#{ref}/doc/api/README.md"
     end
 
     it 'rebuilds relative URL for a file in the repository root' do
       relative_link = link('../README.md')
       doc = filter(relative_link, requested_path: 'doc/some-file.md')
 
-      expect(doc.at_css('a')['href']).
-        to eq "/#{project_path}/blob/#{ref}/README.md"
+      expect(doc.at_css('a')['href'])
+        .to eq "/#{project_path}/blob/#{ref}/README.md"
     end
 
     it 'rebuilds relative URL for a file in the repo with an anchor' do
       doc = filter(link('README.md#section'))
-      expect(doc.at_css('a')['href']).
-        to eq "/#{project_path}/blob/#{ref}/README.md#section"
+      expect(doc.at_css('a')['href'])
+        .to eq "/#{project_path}/blob/#{ref}/README.md#section"
     end
 
     it 'rebuilds relative URL for a directory in the repo' do
       doc = filter(link('doc/api/'))
-      expect(doc.at_css('a')['href']).
-        to eq "/#{project_path}/tree/#{ref}/doc/api"
+      expect(doc.at_css('a')['href'])
+        .to eq "/#{project_path}/tree/#{ref}/doc/api"
     end
 
     it 'rebuilds relative URL for an image in the repo' do
       doc = filter(image('files/images/logo-black.png'))
 
-      expect(doc.at_css('img')['src']).
-        to eq "/#{project_path}/raw/#{ref}/files/images/logo-black.png"
+      expect(doc.at_css('img')['src'])
+        .to eq "/#{project_path}/raw/#{ref}/files/images/logo-black.png"
     end
 
     it 'rebuilds relative URL for link to an image in the repo' do
       doc = filter(link('files/images/logo-black.png'))
 
-      expect(doc.at_css('a')['href']).
-        to eq "/#{project_path}/raw/#{ref}/files/images/logo-black.png"
+      expect(doc.at_css('a')['href'])
+        .to eq "/#{project_path}/raw/#{ref}/files/images/logo-black.png"
     end
 
     it 'rebuilds relative URL for a video in the repo' do
       doc = filter(video('files/videos/intro.mp4'), commit: project.commit('video'), ref: 'video')
 
-      expect(doc.at_css('video')['src']).
-        to eq "/#{project_path}/raw/video/files/videos/intro.mp4"
+      expect(doc.at_css('video')['src'])
+        .to eq "/#{project_path}/raw/video/files/videos/intro.mp4"
     end
 
     it 'does not modify relative URL with an anchor only' do
diff --git a/spec/lib/banzai/filter/sanitization_filter_spec.rb b/spec/lib/banzai/filter/sanitization_filter_spec.rb
index fb7862f49a26cc1dc00419c2e75a0733f5419d79..a8a0aa6d395c5972b392c9b1068dfe407a13302c 100644
--- a/spec/lib/banzai/filter/sanitization_filter_spec.rb
+++ b/spec/lib/banzai/filter/sanitization_filter_spec.rb
@@ -221,8 +221,8 @@ describe Banzai::Filter::SanitizationFilter, lib: true do
     end
 
     it 'disallows invalid URIs' do
-      expect(Addressable::URI).to receive(:parse).with('foo://example.com').
-        and_raise(Addressable::URI::InvalidURIError)
+      expect(Addressable::URI).to receive(:parse).with('foo://example.com')
+        .and_raise(Addressable::URI::InvalidURIError)
 
       input = '<a href="foo://example.com">Foo</a>'
       output = filter(input)
diff --git a/spec/lib/banzai/filter/snippet_reference_filter_spec.rb b/spec/lib/banzai/filter/snippet_reference_filter_spec.rb
index e036514d283e3647fd22c65745f63abfaf9b444d..e851120bc3ae2c4df1479d2c45a68615e5d4e3e4 100644
--- a/spec/lib/banzai/filter/snippet_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/snippet_reference_filter_spec.rb
@@ -22,8 +22,8 @@ describe Banzai::Filter::SnippetReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).to eq urls.
-        namespace_project_snippet_url(project.namespace, project, snippet)
+      expect(doc.css('a').first.attr('href')).to eq urls
+        .namespace_project_snippet_url(project.namespace, project, snippet)
     end
 
     it 'links with adjacent text' do
@@ -88,8 +88,8 @@ describe Banzai::Filter::SnippetReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).
-        to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
+      expect(doc.css('a').first.attr('href'))
+        .to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
     end
 
     it 'link has valid text' do
@@ -121,8 +121,8 @@ describe Banzai::Filter::SnippetReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).
-        to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
+      expect(doc.css('a').first.attr('href'))
+        .to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
     end
 
     it 'link has valid text' do
@@ -154,8 +154,8 @@ describe Banzai::Filter::SnippetReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).
-        to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
+      expect(doc.css('a').first.attr('href'))
+        .to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
     end
 
     it 'link has valid text' do
@@ -186,8 +186,8 @@ describe Banzai::Filter::SnippetReferenceFilter, lib: true do
     it 'links to a valid reference' do
       doc = reference_filter("See #{reference}")
 
-      expect(doc.css('a').first.attr('href')).
-        to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
+      expect(doc.css('a').first.attr('href'))
+        .to eq urls.namespace_project_snippet_url(project2.namespace, project2, snippet)
     end
 
     it 'links with adjacent text' do
diff --git a/spec/lib/banzai/filter/upload_link_filter_spec.rb b/spec/lib/banzai/filter/upload_link_filter_spec.rb
index 639cac6406aa9ede6440ddbc9077433d5910b8a7..6327ca8bbfdd17da729ad5b52993e7b708d1e937 100644
--- a/spec/lib/banzai/filter/upload_link_filter_spec.rb
+++ b/spec/lib/banzai/filter/upload_link_filter_spec.rb
@@ -51,22 +51,22 @@ describe Banzai::Filter::UploadLinkFilter, lib: true do
   context 'with a valid repository' do
     it 'rebuilds relative URL for a link' do
       doc = filter(link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
-      expect(doc.at_css('a')['href']).
-        to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
+      expect(doc.at_css('a')['href'])
+        .to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
 
       doc = filter(nested_link('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
-      expect(doc.at_css('a')['href']).
-        to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
+      expect(doc.at_css('a')['href'])
+        .to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
     end
 
     it 'rebuilds relative URL for an image' do
       doc = filter(image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
-      expect(doc.at_css('img')['src']).
-        to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
+      expect(doc.at_css('img')['src'])
+        .to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
 
       doc = filter(nested_image('/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg'))
-      expect(doc.at_css('img')['src']).
-        to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
+      expect(doc.at_css('img')['src'])
+        .to eq "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/e90decf88d8f96fe9e1389afc2e4a91f/test.jpg"
     end
 
     it 'does not modify absolute URL' do
@@ -79,10 +79,10 @@ describe Banzai::Filter::UploadLinkFilter, lib: true do
       escaped = Addressable::URI.escape(path)
 
       # Stub these methods so the file doesn't actually need to be in the repo
-      allow_any_instance_of(described_class).
-        to receive(:file_exists?).and_return(true)
-      allow_any_instance_of(described_class).
-        to receive(:image?).with(path).and_return(true)
+      allow_any_instance_of(described_class)
+        .to receive(:file_exists?).and_return(true)
+      allow_any_instance_of(described_class)
+        .to receive(:image?).with(path).and_return(true)
 
       doc = filter(image(escaped))
       expect(doc.at_css('img')['src']).to match "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/uploads/%ED%95%9C%EA%B8%80.png"
diff --git a/spec/lib/banzai/note_renderer_spec.rb b/spec/lib/banzai/note_renderer_spec.rb
index 49556074278ae67931989f84dc48d1a8248ae1e2..32764bee5eb178cd94cfbd113afc2cc4bc09a7e6 100644
--- a/spec/lib/banzai/note_renderer_spec.rb
+++ b/spec/lib/banzai/note_renderer_spec.rb
@@ -8,15 +8,15 @@ describe Banzai::NoteRenderer do
       wiki = double(:wiki)
       user = double(:user)
 
-      expect(Banzai::ObjectRenderer).to receive(:new).
-        with(project, user,
+      expect(Banzai::ObjectRenderer).to receive(:new)
+        .with(project, user,
              requested_path: 'foo',
              project_wiki: wiki,
-             ref: 'bar').
-        and_call_original
+             ref: 'bar')
+        .and_call_original
 
-      expect_any_instance_of(Banzai::ObjectRenderer).
-        to receive(:render).with([note], :note)
+      expect_any_instance_of(Banzai::ObjectRenderer)
+        .to receive(:render).with([note], :note)
 
       described_class.render([note], project, user, 'foo', wiki, 'bar')
     end
diff --git a/spec/lib/banzai/redactor_spec.rb b/spec/lib/banzai/redactor_spec.rb
index e6f2963193cea70ed56c054519a8989b16ffcbb0..81ae5685b1011737a9876c687425e271e94c2db6 100644
--- a/spec/lib/banzai/redactor_spec.rb
+++ b/spec/lib/banzai/redactor_spec.rb
@@ -12,11 +12,11 @@ describe Banzai::Redactor do
       end
 
       it 'redacts an array of documents' do
-        doc1 = Nokogiri::HTML.
-               fragment('<a class="gfm" data-reference-type="issue">foo</a>')
+        doc1 = Nokogiri::HTML
+               .fragment('<a class="gfm" data-reference-type="issue">foo</a>')
 
-        doc2 = Nokogiri::HTML.
-               fragment('<a class="gfm" data-reference-type="issue">bar</a>')
+        doc2 = Nokogiri::HTML
+               .fragment('<a class="gfm" data-reference-type="issue">bar</a>')
 
         redacted_data = redactor.redact([doc1, doc2])
 
@@ -93,9 +93,9 @@ describe Banzai::Redactor do
       doc = Nokogiri::HTML.fragment('<a href="foo">foo</a>')
       node = doc.children[0]
 
-      expect(redactor).to receive(:nodes_visible_to_user).
-        with([node]).
-        and_return(Set.new)
+      expect(redactor).to receive(:nodes_visible_to_user)
+        .with([node])
+        .and_return(Set.new)
 
       redactor.redact_document_nodes([{ document: doc, nodes: [node] }])
 
@@ -108,10 +108,10 @@ describe Banzai::Redactor do
       doc = Nokogiri::HTML.fragment('<a data-reference-type="issue"></a>')
       node = doc.children[0]
 
-      expect_any_instance_of(Banzai::ReferenceParser::IssueParser).
-        to receive(:nodes_visible_to_user).
-        with(user, [node]).
-        and_return([node])
+      expect_any_instance_of(Banzai::ReferenceParser::IssueParser)
+        .to receive(:nodes_visible_to_user)
+        .with(user, [node])
+        .and_return([node])
 
       expect(redactor.nodes_visible_to_user([node])).to eq(Set.new([node]))
     end
diff --git a/spec/lib/banzai/reference_parser/base_parser_spec.rb b/spec/lib/banzai/reference_parser/base_parser_spec.rb
index 76fab93821aa5cfa194a982ffc02fe1e7c18b302..b444ca05b8ecae41c8af4789040d23eaabb2252e 100644
--- a/spec/lib/banzai/reference_parser/base_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/base_parser_spec.rb
@@ -54,8 +54,8 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
   describe '#referenced_by' do
     context 'when references_relation is implemented' do
       it 'returns a collection of objects' do
-        links = Nokogiri::HTML.fragment("<a data-foo='#{user.id}'></a>").
-          children
+        links = Nokogiri::HTML.fragment("<a data-foo='#{user.id}'></a>")
+          .children
 
         expect(subject).to receive(:references_relation).and_return(User)
         expect(subject.referenced_by(links)).to eq([user])
@@ -66,8 +66,8 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
       it 'raises NotImplementedError' do
         links = Nokogiri::HTML.fragment('<a data-foo="1"></a>').children
 
-        expect { subject.referenced_by(links) }.
-          to raise_error(NotImplementedError)
+        expect { subject.referenced_by(links) }
+          .to raise_error(NotImplementedError)
       end
     end
   end
@@ -80,8 +80,8 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
 
   describe '#gather_attributes_per_project' do
     it 'returns a Hash containing attribute values per project' do
-      link = Nokogiri::HTML.fragment('<a data-project="1" data-foo="2"></a>').
-        children[0]
+      link = Nokogiri::HTML.fragment('<a data-project="1" data-foo="2"></a>')
+        .children[0]
 
       hash = subject.gather_attributes_per_project([link], 'data-foo')
 
@@ -95,19 +95,19 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
     it 'returns a Hash grouping objects per node' do
       link = double(:link)
 
-      expect(link).to receive(:has_attribute?).
-        with('data-user').
-        and_return(true)
+      expect(link).to receive(:has_attribute?)
+        .with('data-user')
+        .and_return(true)
 
-      expect(link).to receive(:attr).
-        with('data-user').
-        and_return(user.id.to_s)
+      expect(link).to receive(:attr)
+        .with('data-user')
+        .and_return(user.id.to_s)
 
       nodes = [link]
 
-      expect(subject).to receive(:unique_attribute_values).
-        with(nodes, 'data-user').
-        and_return([user.id.to_s])
+      expect(subject).to receive(:unique_attribute_values)
+        .with(nodes, 'data-user')
+        .and_return([user.id.to_s])
 
       hash = subject.grouped_objects_for_nodes(nodes, User, 'data-user')
 
@@ -117,20 +117,20 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
     it 'returns an empty Hash when entry does not exist in the database', :request_store do
       link = double(:link)
 
-      expect(link).to receive(:has_attribute?).
-          with('data-user').
-          and_return(true)
+      expect(link).to receive(:has_attribute?)
+          .with('data-user')
+          .and_return(true)
 
-      expect(link).to receive(:attr).
-          with('data-user').
-          and_return('1')
+      expect(link).to receive(:attr)
+          .with('data-user')
+          .and_return('1')
 
       nodes = [link]
       bad_id = user.id + 100
 
-      expect(subject).to receive(:unique_attribute_values).
-          with(nodes, 'data-user').
-          and_return([bad_id.to_s])
+      expect(subject).to receive(:unique_attribute_values)
+          .with(nodes, 'data-user')
+          .and_return([bad_id.to_s])
 
       hash = subject.grouped_objects_for_nodes(nodes, User, 'data-user')
 
@@ -142,15 +142,15 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
     it 'returns an Array of unique values' do
       link = double(:link)
 
-      expect(link).to receive(:has_attribute?).
-        with('data-foo').
-        twice.
-        and_return(true)
+      expect(link).to receive(:has_attribute?)
+        .with('data-foo')
+        .twice
+        .and_return(true)
 
-      expect(link).to receive(:attr).
-        with('data-foo').
-        twice.
-        and_return('1')
+      expect(link).to receive(:attr)
+        .with('data-foo')
+        .twice
+        .and_return('1')
 
       nodes = [link, link]
 
@@ -167,9 +167,9 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
       instance = dummy.new(project, user)
       document = Nokogiri::HTML.fragment('<a class="gfm"></a><a class="gfm" data-reference-type="test"></a>')
 
-      expect(instance).to receive(:gather_references).
-        with([document.children[1]]).
-        and_return([user])
+      expect(instance).to receive(:gather_references)
+        .with([document.children[1]])
+        .and_return([user])
 
       expect(instance.process([document])).to eq([user])
     end
@@ -179,9 +179,9 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
     let(:link) { double(:link) }
 
     it 'does not process links a user can not reference' do
-      expect(subject).to receive(:nodes_user_can_reference).
-        with(user, [link]).
-        and_return([])
+      expect(subject).to receive(:nodes_user_can_reference)
+        .with(user, [link])
+        .and_return([])
 
       expect(subject).to receive(:referenced_by).with([])
 
@@ -189,13 +189,13 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
     end
 
     it 'does not process links a user can not see' do
-      expect(subject).to receive(:nodes_user_can_reference).
-        with(user, [link]).
-        and_return([link])
+      expect(subject).to receive(:nodes_user_can_reference)
+        .with(user, [link])
+        .and_return([link])
 
-      expect(subject).to receive(:nodes_visible_to_user).
-        with(user, [link]).
-        and_return([])
+      expect(subject).to receive(:nodes_visible_to_user)
+        .with(user, [link])
+        .and_return([])
 
       expect(subject).to receive(:referenced_by).with([])
 
@@ -203,13 +203,13 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
     end
 
     it 'returns the references if a user can reference and see a link' do
-      expect(subject).to receive(:nodes_user_can_reference).
-        with(user, [link]).
-        and_return([link])
+      expect(subject).to receive(:nodes_user_can_reference)
+        .with(user, [link])
+        .and_return([link])
 
-      expect(subject).to receive(:nodes_visible_to_user).
-        with(user, [link]).
-        and_return([link])
+      expect(subject).to receive(:nodes_visible_to_user)
+        .with(user, [link])
+        .and_return([link])
 
       expect(subject).to receive(:referenced_by).with([link])
 
@@ -221,8 +221,8 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
     it 'delegates the permissions check to the Ability class' do
       user = double(:user)
 
-      expect(Ability).to receive(:allowed?).
-        with(user, :read_project, project)
+      expect(Ability).to receive(:allowed?)
+        .with(user, :read_project, project)
 
       subject.can?(user, :read_project, project)
     end
@@ -230,8 +230,8 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
 
   describe '#find_projects_for_hash_keys' do
     it 'returns a list of Projects' do
-      expect(subject.find_projects_for_hash_keys(project.id => project)).
-        to eq([project])
+      expect(subject.find_projects_for_hash_keys(project.id => project))
+        .to eq([project])
     end
   end
 
@@ -243,8 +243,8 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
         expect(collection).to receive(:where).twice.and_call_original
 
         2.times do
-          expect(subject.collection_objects_for_ids(collection, [user.id])).
-            to eq([user])
+          expect(subject.collection_objects_for_ids(collection, [user.id]))
+            .to eq([user])
         end
       end
     end
@@ -258,8 +258,8 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
       end
 
       it 'queries the collection on the first call' do
-        expect(subject.collection_objects_for_ids(User, [user.id])).
-          to eq([user])
+        expect(subject.collection_objects_for_ids(User, [user.id]))
+          .to eq([user])
       end
 
       it 'does not query previously queried objects' do
@@ -268,34 +268,34 @@ describe Banzai::ReferenceParser::BaseParser, lib: true do
         expect(collection).to receive(:where).once.and_call_original
 
         2.times do
-          expect(subject.collection_objects_for_ids(collection, [user.id])).
-            to eq([user])
+          expect(subject.collection_objects_for_ids(collection, [user.id]))
+            .to eq([user])
         end
       end
 
       it 'casts String based IDs to Fixnums before querying objects' do
         2.times do
-          expect(subject.collection_objects_for_ids(User, [user.id.to_s])).
-            to eq([user])
+          expect(subject.collection_objects_for_ids(User, [user.id.to_s]))
+            .to eq([user])
         end
       end
 
       it 'queries any additional objects after the first call' do
         other_user = create(:user)
 
-        expect(subject.collection_objects_for_ids(User, [user.id])).
-          to eq([user])
+        expect(subject.collection_objects_for_ids(User, [user.id]))
+          .to eq([user])
 
-        expect(subject.collection_objects_for_ids(User, [user.id, other_user.id])).
-          to eq([user, other_user])
+        expect(subject.collection_objects_for_ids(User, [user.id, other_user.id]))
+          .to eq([user, other_user])
       end
 
       it 'caches objects on a per collection class basis' do
-        expect(subject.collection_objects_for_ids(User, [user.id])).
-          to eq([user])
+        expect(subject.collection_objects_for_ids(User, [user.id]))
+          .to eq([user])
 
-        expect(subject.collection_objects_for_ids(Project, [project.id])).
-          to eq([project])
+        expect(subject.collection_objects_for_ids(Project, [project.id]))
+          .to eq([project])
       end
     end
   end
diff --git a/spec/lib/banzai/reference_parser/commit_parser_spec.rb b/spec/lib/banzai/reference_parser/commit_parser_spec.rb
index 583ce63a8ab728efce996e12aae570f355e32a21..a314a6119cbdb775d1d74eb72db5185d07076631 100644
--- a/spec/lib/banzai/reference_parser/commit_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/commit_parser_spec.rb
@@ -32,30 +32,30 @@ describe Banzai::ReferenceParser::CommitParser, lib: true do
         it 'returns an Array of commits' do
           commit = double(:commit)
 
-          allow_any_instance_of(Project).to receive(:valid_repo?).
-            and_return(true)
+          allow_any_instance_of(Project).to receive(:valid_repo?)
+            .and_return(true)
 
-          expect(subject).to receive(:find_commits).
-            with(project, ['123']).
-            and_return([commit])
+          expect(subject).to receive(:find_commits)
+            .with(project, ['123'])
+            .and_return([commit])
 
           expect(subject.referenced_by([link])).to eq([commit])
         end
 
         it 'returns an empty Array when the commit could not be found' do
-          allow_any_instance_of(Project).to receive(:valid_repo?).
-            and_return(true)
+          allow_any_instance_of(Project).to receive(:valid_repo?)
+            .and_return(true)
 
-          expect(subject).to receive(:find_commits).
-            with(project, ['123']).
-            and_return([])
+          expect(subject).to receive(:find_commits)
+            .with(project, ['123'])
+            .and_return([])
 
           expect(subject.referenced_by([link])).to eq([])
         end
 
         it 'skips projects without valid repositories' do
-          allow_any_instance_of(Project).to receive(:valid_repo?).
-            and_return(false)
+          allow_any_instance_of(Project).to receive(:valid_repo?)
+            .and_return(false)
 
           expect(subject.referenced_by([link])).to eq([])
         end
@@ -63,8 +63,8 @@ describe Banzai::ReferenceParser::CommitParser, lib: true do
 
       context 'when the link does not have a data-commit attribute' do
         it 'returns an empty Array' do
-          allow_any_instance_of(Project).to receive(:valid_repo?).
-            and_return(true)
+          allow_any_instance_of(Project).to receive(:valid_repo?)
+            .and_return(true)
 
           expect(subject.referenced_by([link])).to eq([])
         end
@@ -73,8 +73,8 @@ describe Banzai::ReferenceParser::CommitParser, lib: true do
 
     context 'when the link does not have a data-project attribute' do
       it 'returns an empty Array' do
-        allow_any_instance_of(Project).to receive(:valid_repo?).
-          and_return(true)
+        allow_any_instance_of(Project).to receive(:valid_repo?)
+          .and_return(true)
 
         expect(subject.referenced_by([link])).to eq([])
       end
diff --git a/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb b/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb
index 8c0f5d7df97d6216ea1a386230dc413779744dc8..5dca5e784da760c80800d9dd38cb4f2856615e85 100644
--- a/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/commit_range_parser_spec.rb
@@ -32,17 +32,17 @@ describe Banzai::ReferenceParser::CommitRangeParser, lib: true do
         it 'returns an Array of commit ranges' do
           range = double(:range)
 
-          expect(subject).to receive(:find_object).
-            with(project, '123..456').
-            and_return(range)
+          expect(subject).to receive(:find_object)
+            .with(project, '123..456')
+            .and_return(range)
 
           expect(subject.referenced_by([link])).to eq([range])
         end
 
         it 'returns an empty Array when the commit range could not be found' do
-          expect(subject).to receive(:find_object).
-            with(project, '123..456').
-            and_return(nil)
+          expect(subject).to receive(:find_object)
+            .with(project, '123..456')
+            .and_return(nil)
 
           expect(subject.referenced_by([link])).to eq([])
         end
@@ -88,17 +88,17 @@ describe Banzai::ReferenceParser::CommitRangeParser, lib: true do
     it 'returns an Array of range objects' do
       range = double(:commit)
 
-      expect(subject).to receive(:find_object).
-        with(project, '123..456').
-        and_return(range)
+      expect(subject).to receive(:find_object)
+        .with(project, '123..456')
+        .and_return(range)
 
       expect(subject.find_ranges(project, ['123..456'])).to eq([range])
     end
 
     it 'skips ranges that could not be found' do
-      expect(subject).to receive(:find_object).
-        with(project, '123..456').
-        and_return(nil)
+      expect(subject).to receive(:find_object)
+        .with(project, '123..456')
+        .and_return(nil)
 
       expect(subject.find_ranges(project, ['123..456'])).to eq([])
     end
diff --git a/spec/lib/banzai/reference_parser/issue_parser_spec.rb b/spec/lib/banzai/reference_parser/issue_parser_spec.rb
index 7031c47231cc1792e74e493552112d0386c29706..58e1a0c1bc1c399e973f47473fbd1e030fbccc27 100644
--- a/spec/lib/banzai/reference_parser/issue_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/issue_parser_spec.rb
@@ -18,17 +18,17 @@ describe Banzai::ReferenceParser::IssueParser, lib: true do
       it_behaves_like "referenced feature visibility", "issues"
 
       it 'returns the nodes when the user can read the issue' do
-        expect(Ability).to receive(:issues_readable_by_user).
-          with([issue], user).
-          and_return([issue])
+        expect(Ability).to receive(:issues_readable_by_user)
+          .with([issue], user)
+          .and_return([issue])
 
         expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
       end
 
       it 'returns an empty Array when the user can not read the issue' do
-        expect(Ability).to receive(:issues_readable_by_user).
-          with([issue], user).
-          and_return([])
+        expect(Ability).to receive(:issues_readable_by_user)
+          .with([issue], user)
+          .and_return([])
 
         expect(subject.nodes_visible_to_user(user, [link])).to eq([])
       end
diff --git a/spec/lib/banzai/reference_parser/user_parser_spec.rb b/spec/lib/banzai/reference_parser/user_parser_spec.rb
index 4d560667342b96f933e185fb3a7ef761f780d34f..dfebb971f3a4fe7094b56da2c1ed7dff7808d665 100644
--- a/spec/lib/banzai/reference_parser/user_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/user_parser_spec.rb
@@ -96,17 +96,17 @@ describe Banzai::ReferenceParser::UserParser, lib: true do
         end
 
         it 'returns the nodes if the user can read the group' do
-          expect(Ability).to receive(:allowed?).
-            with(user, :read_group, group).
-            and_return(true)
+          expect(Ability).to receive(:allowed?)
+            .with(user, :read_group, group)
+            .and_return(true)
 
           expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
         end
 
         it 'returns an empty Array if the user can not read the group' do
-          expect(Ability).to receive(:allowed?).
-            with(user, :read_group, group).
-            and_return(false)
+          expect(Ability).to receive(:allowed?)
+            .with(user, :read_group, group)
+            .and_return(false)
 
           expect(subject.nodes_visible_to_user(user, [link])).to eq([])
         end
@@ -129,9 +129,9 @@ describe Banzai::ReferenceParser::UserParser, lib: true do
 
             link['data-project'] = other_project.id.to_s
 
-            expect(Ability).to receive(:allowed?).
-              with(user, :read_project, other_project).
-              and_return(true)
+            expect(Ability).to receive(:allowed?)
+              .with(user, :read_project, other_project)
+              .and_return(true)
 
             expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
           end
@@ -141,9 +141,9 @@ describe Banzai::ReferenceParser::UserParser, lib: true do
 
             link['data-project'] = other_project.id.to_s
 
-            expect(Ability).to receive(:allowed?).
-              with(user, :read_project, other_project).
-              and_return(false)
+            expect(Ability).to receive(:allowed?)
+              .with(user, :read_project, other_project)
+              .and_return(false)
 
             expect(subject.nodes_visible_to_user(user, [link])).to eq([])
           end
diff --git a/spec/lib/container_registry/blob_spec.rb b/spec/lib/container_registry/blob_spec.rb
index ab010c6dfebd1ea9ba4b712a542f6c0817cbd849..175fd2e7e130b06a0db8174a8f0cb610f227d7b6 100644
--- a/spec/lib/container_registry/blob_spec.rb
+++ b/spec/lib/container_registry/blob_spec.rb
@@ -72,8 +72,8 @@ describe ContainerRegistry::Blob do
   describe '#data' do
     context 'when locally stored' do
       before do
-        stub_request(:get, 'http://registry.gitlab/v2/group/test/image/blobs/sha256:0123456789012345').
-          to_return(
+        stub_request(:get, 'http://registry.gitlab/v2/group/test/image/blobs/sha256:0123456789012345')
+          .to_return(
             status: 200,
             headers: { 'Content-Type' => 'application/json' },
             body: '{"key":"value"}')
@@ -97,9 +97,9 @@ describe ContainerRegistry::Blob do
 
       context 'for a valid address' do
         before do
-          stub_request(:get, location).
-            with { |request| !request.headers.include?('Authorization') }.
-            to_return(
+          stub_request(:get, location)
+            .with { |request| !request.headers.include?('Authorization') }
+            .to_return(
               status: 200,
               headers: { 'Content-Type' => 'application/json' },
               body: '{"key":"value"}')
diff --git a/spec/lib/container_registry/client_spec.rb b/spec/lib/container_registry/client_spec.rb
index ec03b53338374517c615532b984e100ee9143cdd..3df33f48adbb1e49c126f6986d87e0281e284c19 100644
--- a/spec/lib/container_registry/client_spec.rb
+++ b/spec/lib/container_registry/client_spec.rb
@@ -8,28 +8,28 @@ describe ContainerRegistry::Client do
 
   describe '#blob' do
     it 'GET /v2/:name/blobs/:digest' do
-      stub_request(:get, "http://container-registry/v2/group/test/blobs/sha256:0123456789012345").
-        with(headers: {
+      stub_request(:get, "http://container-registry/v2/group/test/blobs/sha256:0123456789012345")
+        .with(headers: {
                'Accept' => 'application/octet-stream',
                'Authorization' => "bearer #{token}"
-             }).
-        to_return(status: 200, body: "Blob")
+             })
+        .to_return(status: 200, body: "Blob")
 
       expect(client.blob('group/test', 'sha256:0123456789012345')).to eq('Blob')
     end
 
     it 'follows 307 redirect for GET /v2/:name/blobs/:digest' do
-      stub_request(:get, "http://container-registry/v2/group/test/blobs/sha256:0123456789012345").
-        with(headers: {
+      stub_request(:get, "http://container-registry/v2/group/test/blobs/sha256:0123456789012345")
+        .with(headers: {
                'Accept' => 'application/octet-stream',
                'Authorization' => "bearer #{token}"
-             }).
-        to_return(status: 307, body: "", headers: { Location: 'http://redirected' })
+             })
+        .to_return(status: 307, body: "", headers: { Location: 'http://redirected' })
       # We should probably use hash_excluding here, but that requires an update to WebMock:
       # https://github.com/bblimke/webmock/blob/master/lib/webmock/matchers/hash_excluding_matcher.rb
-      stub_request(:get, "http://redirected/").
-        with { |request| !request.headers.include?('Authorization') }.
-        to_return(status: 200, body: "Successfully redirected")
+      stub_request(:get, "http://redirected/")
+        .with { |request| !request.headers.include?('Authorization') }
+        .to_return(status: 200, body: "Successfully redirected")
 
       response = client.blob('group/test', 'sha256:0123456789012345')
 
diff --git a/spec/lib/container_registry/tag_spec.rb b/spec/lib/container_registry/tag_spec.rb
index f8fffbdca4148b9a713d9ed5eded43de18bb08ee..cb4ae3be525b8c95803b99fe7369a6a877441301 100644
--- a/spec/lib/container_registry/tag_spec.rb
+++ b/spec/lib/container_registry/tag_spec.rb
@@ -60,9 +60,9 @@ describe ContainerRegistry::Tag do
   context 'manifest processing' do
     context 'schema v1' do
       before do
-        stub_request(:get, 'http://registry.gitlab/v2/group/test/manifests/tag').
-          with(headers: headers).
-          to_return(
+        stub_request(:get, 'http://registry.gitlab/v2/group/test/manifests/tag')
+          .with(headers: headers)
+          .to_return(
             status: 200,
             body: File.read(Rails.root + 'spec/fixtures/container_registry/tag_manifest_1.json'),
             headers: { 'Content-Type' => 'application/vnd.docker.distribution.manifest.v1+prettyjws' })
@@ -97,9 +97,9 @@ describe ContainerRegistry::Tag do
 
     context 'schema v2' do
       before do
-        stub_request(:get, 'http://registry.gitlab/v2/group/test/manifests/tag').
-          with(headers: headers).
-          to_return(
+        stub_request(:get, 'http://registry.gitlab/v2/group/test/manifests/tag')
+          .with(headers: headers)
+          .to_return(
             status: 200,
             body: File.read(Rails.root + 'spec/fixtures/container_registry/tag_manifest.json'),
             headers: { 'Content-Type' => 'application/vnd.docker.distribution.manifest.v2+json' })
@@ -134,9 +134,9 @@ describe ContainerRegistry::Tag do
 
         context 'when locally stored' do
           before do
-            stub_request(:get, 'http://registry.gitlab/v2/group/test/blobs/sha256:d7a513a663c1a6dcdba9ed832ca53c02ac2af0c333322cd6ca92936d1d9917ac').
-              with(headers: { 'Accept' => 'application/octet-stream' }).
-              to_return(
+            stub_request(:get, 'http://registry.gitlab/v2/group/test/blobs/sha256:d7a513a663c1a6dcdba9ed832ca53c02ac2af0c333322cd6ca92936d1d9917ac')
+              .with(headers: { 'Accept' => 'application/octet-stream' })
+              .to_return(
                 status: 200,
                 body: File.read(Rails.root + 'spec/fixtures/container_registry/config_blob.json'))
           end
@@ -146,14 +146,14 @@ describe ContainerRegistry::Tag do
 
         context 'when externally stored' do
           before do
-            stub_request(:get, 'http://registry.gitlab/v2/group/test/blobs/sha256:d7a513a663c1a6dcdba9ed832ca53c02ac2af0c333322cd6ca92936d1d9917ac').
-              with(headers: { 'Accept' => 'application/octet-stream' }).
-              to_return(
+            stub_request(:get, 'http://registry.gitlab/v2/group/test/blobs/sha256:d7a513a663c1a6dcdba9ed832ca53c02ac2af0c333322cd6ca92936d1d9917ac')
+              .with(headers: { 'Accept' => 'application/octet-stream' })
+              .to_return(
                 status: 307,
                 headers: { 'Location' => 'http://external.com/blob/file' })
 
-            stub_request(:get, 'http://external.com/blob/file').
-              to_return(
+            stub_request(:get, 'http://external.com/blob/file')
+              .to_return(
                 status: 200,
                 body: File.read(Rails.root + 'spec/fixtures/container_registry/config_blob.json'))
           end
diff --git a/spec/lib/extracts_path_spec.rb b/spec/lib/extracts_path_spec.rb
index 2b26a3185831eb3dffef7347c921383019059ba1..f2132d485abdf7cc72504902c642d06ace8ba2bc 100644
--- a/spec/lib/extracts_path_spec.rb
+++ b/spec/lib/extracts_path_spec.rb
@@ -14,8 +14,8 @@ describe ExtractsPath, lib: true do
     repo = double(ref_names: ['master', 'foo/bar/baz', 'v1.0.0', 'v2.0.0',
                               'release/app', 'release/app/v1.0.0'])
     allow(project).to receive(:repository).and_return(repo)
-    allow(project).to receive(:path_with_namespace).
-      and_return('gitlab/gitlab-ci')
+    allow(project).to receive(:path_with_namespace)
+      .and_return('gitlab/gitlab-ci')
     allow(request).to receive(:format=)
   end
 
diff --git a/spec/lib/feature_spec.rb b/spec/lib/feature_spec.rb
index 1d92a5cb33fc445f93f64e396f12b956d60eef70..5cc3a3745e441c0e35030d11c3b5992bfdc8a66e 100644
--- a/spec/lib/feature_spec.rb
+++ b/spec/lib/feature_spec.rb
@@ -6,8 +6,8 @@ describe Feature, lib: true do
     let(:key) { 'my_feature' }
 
     it 'returns the Flipper feature' do
-      expect_any_instance_of(Flipper::DSL).to receive(:feature).with(key).
-        and_return(feature)
+      expect_any_instance_of(Flipper::DSL).to receive(:feature).with(key)
+        .and_return(feature)
 
       expect(described_class.get(key)).to be(feature)
     end
@@ -17,8 +17,8 @@ describe Feature, lib: true do
     let(:features) { Set.new }
 
     it 'returns the Flipper features as an array' do
-      expect_any_instance_of(Flipper::DSL).to receive(:features).
-        and_return(features)
+      expect_any_instance_of(Flipper::DSL).to receive(:features)
+        .and_return(features)
 
       expect(described_class.all).to eq(features.to_a)
     end
diff --git a/spec/lib/gitlab/background_migration_spec.rb b/spec/lib/gitlab/background_migration_spec.rb
index f2073b9bcb354d06f2f1cd7f9027a814e70a6b4f..64f82fe27b2adb11fbedd56936ad6a5552dbec6a 100644
--- a/spec/lib/gitlab/background_migration_spec.rb
+++ b/spec/lib/gitlab/background_migration_spec.rb
@@ -5,9 +5,9 @@ describe Gitlab::BackgroundMigration do
     it 'steals jobs from a queue' do
       queue = [double(:job, args: ['Foo', [10, 20]])]
 
-      allow(Sidekiq::Queue).to receive(:new).
-        with(BackgroundMigrationWorker.sidekiq_options['queue']).
-        and_return(queue)
+      allow(Sidekiq::Queue).to receive(:new)
+        .with(BackgroundMigrationWorker.sidekiq_options['queue'])
+        .and_return(queue)
 
       expect(queue[0]).to receive(:delete)
 
@@ -19,9 +19,9 @@ describe Gitlab::BackgroundMigration do
     it 'does not steal jobs for a different migration' do
       queue = [double(:job, args: ['Foo', [10, 20]])]
 
-      allow(Sidekiq::Queue).to receive(:new).
-        with(BackgroundMigrationWorker.sidekiq_options['queue']).
-        and_return(queue)
+      allow(Sidekiq::Queue).to receive(:new)
+        .with(BackgroundMigrationWorker.sidekiq_options['queue'])
+        .and_return(queue)
 
       expect(described_class).not_to receive(:perform)
 
@@ -36,9 +36,9 @@ describe Gitlab::BackgroundMigration do
       instance = double(:instance)
       klass = double(:klass, new: instance)
 
-      expect(described_class).to receive(:const_get).
-        with('Foo').
-        and_return(klass)
+      expect(described_class).to receive(:const_get)
+        .with('Foo')
+        .and_return(klass)
 
       expect(instance).to receive(:perform).with(10, 20)
 
diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
index a7ee7f53a6b23d2d47267855bc0b4a6b75e105dc..d8beb05601c11b6ec1f46ac1b90beb90676fae20 100644
--- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
@@ -86,11 +86,9 @@ describe Gitlab::BitbucketImport::Importer, lib: true do
                   headers: { "Content-Type" => "application/json" },
                   body: issues_statuses_sample_data.to_json)
 
-      stub_request(:get, "https://api.bitbucket.org/2.0/repositories/namespace/repo?pagelen=50&sort=created_on").
-        with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer', 'User-Agent' => 'Faraday v0.9.2' }).
-        to_return(status: 200,
-                  body: "",
-                  headers: {})
+      stub_request(:get, "https://api.bitbucket.org/2.0/repositories/namespace/repo?pagelen=50&sort=created_on")
+        .with(headers: { 'Accept' => '*/*', 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization' => 'Bearer', 'User-Agent' => 'Faraday v0.9.2' })
+        .to_return(status: 200, body: "", headers: {})
 
       sample_issues_statuses.each_with_index do |issue, index|
         stub_request(
diff --git a/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb b/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
index cfb5cba054ec0079b77d47f45ad82a96841f14fe..07db6c3a640bbf8273fd6a8051f4b958781a3f9f 100644
--- a/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
+++ b/spec/lib/gitlab/cache/ci/project_pipeline_status_spec.rb
@@ -37,11 +37,11 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :redis do
             loaded_from_cache: false
           )
 
-          expect(described_class).to receive(:new).
-                                       with(project_without_status,
+          expect(described_class).to receive(:new)
+                                       .with(project_without_status,
                                             pipeline_info: empty_status,
-                                            loaded_from_cache: false).
-                                       and_return(fake_pipeline)
+                                            loaded_from_cache: false)
+                                       .and_return(fake_pipeline)
           expect(fake_pipeline).to receive(:load_from_project)
           expect(fake_pipeline).to receive(:store_in_cache)
 
@@ -112,12 +112,12 @@ describe Gitlab::Cache::Ci::ProjectPipelineStatus, :redis do
       pipeline = build_stubbed(:ci_pipeline,
                                sha: '123456', status: 'success', ref: 'master')
       fake_status = double
-      expect(described_class).to receive(:new).
-                                   with(pipeline.project,
+      expect(described_class).to receive(:new)
+                                   .with(pipeline.project,
                                         pipeline_info: {
                                           sha: '123456', status: 'success', ref: 'master'
-                                        }).
-                                   and_return(fake_status)
+                                        })
+                                   .and_return(fake_status)
 
       expect(fake_status).to receive(:store_in_cache_if_needed)
 
diff --git a/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
index eea01f91879ec97d2ded8e56b17119ff4b74e6c5..6a52ae01b2fc258cc96ea1687fdeb3d943f64bea 100644
--- a/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
+++ b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
@@ -33,8 +33,8 @@ describe Gitlab::Ci::Build::Artifacts::Metadata do
       subject { metadata('other_artifacts_0.1.2/').find_entries! }
 
       it 'matches correct paths' do
-        expect(subject.keys).
-          to contain_exactly 'other_artifacts_0.1.2/',
+        expect(subject.keys)
+          .to contain_exactly 'other_artifacts_0.1.2/',
                              'other_artifacts_0.1.2/doc_sample.txt',
                              'other_artifacts_0.1.2/another-subdirectory/'
       end
@@ -44,8 +44,8 @@ describe Gitlab::Ci::Build::Artifacts::Metadata do
       subject { metadata('other_artifacts_0.1.2/another-subdirectory/').find_entries! }
 
       it 'matches correct paths' do
-        expect(subject.keys).
-          to contain_exactly 'other_artifacts_0.1.2/another-subdirectory/',
+        expect(subject.keys)
+          .to contain_exactly 'other_artifacts_0.1.2/another-subdirectory/',
                              'other_artifacts_0.1.2/another-subdirectory/empty_directory/',
                              'other_artifacts_0.1.2/another-subdirectory/banana_sample.gif'
       end
@@ -55,8 +55,8 @@ describe Gitlab::Ci::Build::Artifacts::Metadata do
       subject { metadata('other_artifacts_0.1.2/', recursive: true).find_entries! }
 
       it 'matches correct paths' do
-        expect(subject.keys).
-          to contain_exactly 'other_artifacts_0.1.2/',
+        expect(subject.keys)
+          .to contain_exactly 'other_artifacts_0.1.2/',
                              'other_artifacts_0.1.2/doc_sample.txt',
                              'other_artifacts_0.1.2/another-subdirectory/',
                              'other_artifacts_0.1.2/another-subdirectory/empty_directory/',
diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb
index 97af1c2523dc3caf0075eb90d413875ef0b60152..ca68010cb895d0447dce4834c8a530460a043bdd 100644
--- a/spec/lib/gitlab/closing_issue_extractor_spec.rb
+++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb
@@ -306,58 +306,58 @@ describe Gitlab::ClosingIssueExtractor, lib: true do
       it 'fetches issues in single line message' do
         message = "Closes #{reference} and fix #{reference2}"
 
-        expect(subject.closed_by_message(message)).
-            to match_array([issue, other_issue])
+        expect(subject.closed_by_message(message))
+            .to match_array([issue, other_issue])
       end
 
       it 'fetches comma-separated issues references in single line message' do
         message = "Closes #{reference}, closes #{reference2}"
 
-        expect(subject.closed_by_message(message)).
-            to match_array([issue, other_issue])
+        expect(subject.closed_by_message(message))
+            .to match_array([issue, other_issue])
       end
 
       it 'fetches comma-separated issues numbers in single line message' do
         message = "Closes #{reference}, #{reference2} and #{reference3}"
 
-        expect(subject.closed_by_message(message)).
-            to match_array([issue, other_issue, third_issue])
+        expect(subject.closed_by_message(message))
+            .to match_array([issue, other_issue, third_issue])
       end
 
       it 'fetches issues in multi-line message' do
         message = "Awesome commit (closes #{reference})\nAlso fixes #{reference2}"
 
-        expect(subject.closed_by_message(message)).
-            to match_array([issue, other_issue])
+        expect(subject.closed_by_message(message))
+            .to match_array([issue, other_issue])
       end
 
       it 'fetches issues in hybrid message' do
         message = "Awesome commit (closes #{reference})\n"\
                   "Also fixing issues #{reference2}, #{reference3} and #4"
 
-        expect(subject.closed_by_message(message)).
-            to match_array([issue, other_issue, third_issue])
+        expect(subject.closed_by_message(message))
+            .to match_array([issue, other_issue, third_issue])
       end
 
       it "fetches cross-project references" do
         message = "Closes #{reference} and #{cross_reference}"
 
-        expect(subject.closed_by_message(message)).
-            to match_array([issue, issue2])
+        expect(subject.closed_by_message(message))
+            .to match_array([issue, issue2])
       end
 
       it "fetches cross-project URL references" do
         message = "Closes #{urls.namespace_project_issue_url(issue2.project.namespace, issue2.project, issue2)} and #{reference}"
 
-        expect(subject.closed_by_message(message)).
-            to match_array([issue, issue2])
+        expect(subject.closed_by_message(message))
+            .to match_array([issue, issue2])
       end
 
       it "ignores invalid cross-project URL references" do
         message = "Closes https://google.com#{urls.namespace_project_issue_path(issue2.project.namespace, issue2.project, issue2)} and #{reference}"
 
-        expect(subject.closed_by_message(message)).
-            to match_array([issue])
+        expect(subject.closed_by_message(message))
+            .to match_array([issue])
       end
     end
   end
diff --git a/spec/lib/gitlab/conflict/file_spec.rb b/spec/lib/gitlab/conflict/file_spec.rb
index 780ac0ad97ed433e8505c4f3ffd9c8138303b90e..585eeb77bd5c46e2428cf5e0e30593891cd8c201 100644
--- a/spec/lib/gitlab/conflict/file_spec.rb
+++ b/spec/lib/gitlab/conflict/file_spec.rb
@@ -43,8 +43,8 @@ describe Gitlab::Conflict::File, lib: true do
       end
 
       it 'returns a file containing only the chosen parts of the resolved sections' do
-        expect(resolved_lines.chunk { |line| line.type || 'both' }.map(&:first)).
-          to eq(%w(both new both old both new both))
+        expect(resolved_lines.chunk { |line| line.type || 'both' }.map(&:first))
+          .to eq(%w(both new both old both new both))
       end
     end
 
@@ -52,14 +52,14 @@ describe Gitlab::Conflict::File, lib: true do
       empty_hash = section_keys.map { |key| [key, nil] }.to_h
       invalid_hash = section_keys.map { |key| [key, 'invalid'] }.to_h
 
-      expect { conflict_file.resolve_lines({}) }.
-        to raise_error(Gitlab::Conflict::File::MissingResolution)
+      expect { conflict_file.resolve_lines({}) }
+        .to raise_error(Gitlab::Conflict::File::MissingResolution)
 
-      expect { conflict_file.resolve_lines(empty_hash) }.
-        to raise_error(Gitlab::Conflict::File::MissingResolution)
+      expect { conflict_file.resolve_lines(empty_hash) }
+        .to raise_error(Gitlab::Conflict::File::MissingResolution)
 
-      expect { conflict_file.resolve_lines(invalid_hash) }.
-        to raise_error(Gitlab::Conflict::File::MissingResolution)
+      expect { conflict_file.resolve_lines(invalid_hash) }
+        .to raise_error(Gitlab::Conflict::File::MissingResolution)
     end
   end
 
@@ -250,8 +250,8 @@ FILE
 
   describe '#as_json' do
     it 'includes the blob path for the file' do
-      expect(conflict_file.as_json[:blob_path]).
-        to eq("/#{project.full_path}/blob/#{our_commit.oid}/files/ruby/regex.rb")
+      expect(conflict_file.as_json[:blob_path])
+        .to eq("/#{project.full_path}/blob/#{our_commit.oid}/files/ruby/regex.rb")
     end
 
     it 'includes the blob icon for the file' do
@@ -264,8 +264,8 @@ FILE
       end
 
       it 'includes the detected language of the conflict file' do
-        expect(conflict_file.as_json(full_content: true)[:blob_ace_mode]).
-          to eq('ruby')
+        expect(conflict_file.as_json(full_content: true)[:blob_ace_mode])
+          .to eq('ruby')
       end
     end
   end
diff --git a/spec/lib/gitlab/conflict/parser_spec.rb b/spec/lib/gitlab/conflict/parser_spec.rb
index 2570f95dd21be4bc396c4df1feb44e7233f697ae..aed57b75789ce242d796fec8248bc4bf65603075 100644
--- a/spec/lib/gitlab/conflict/parser_spec.rb
+++ b/spec/lib/gitlab/conflict/parser_spec.rb
@@ -122,18 +122,18 @@ CONFLICT
     context 'when the file contents include conflict delimiters' do
       context 'when there is a non-start delimiter first' do
         it 'raises UnexpectedDelimiter when there is a middle delimiter first' do
-          expect { parse_text('=======') }.
-            to raise_error(Gitlab::Conflict::Parser::UnexpectedDelimiter)
+          expect { parse_text('=======') }
+            .to raise_error(Gitlab::Conflict::Parser::UnexpectedDelimiter)
         end
 
         it 'raises UnexpectedDelimiter when there is an end delimiter first' do
-          expect { parse_text('>>>>>>> README.md') }.
-            to raise_error(Gitlab::Conflict::Parser::UnexpectedDelimiter)
+          expect { parse_text('>>>>>>> README.md') }
+            .to raise_error(Gitlab::Conflict::Parser::UnexpectedDelimiter)
         end
 
         it 'does not raise when there is an end delimiter for a different path first' do
-          expect { parse_text('>>>>>>> some-other-path.md') }.
-            not_to raise_error
+          expect { parse_text('>>>>>>> some-other-path.md') }
+            .not_to raise_error
         end
       end
 
@@ -142,18 +142,18 @@ CONFLICT
         let(:end_text) { "\n=======\n>>>>>>> README.md" }
 
         it 'raises UnexpectedDelimiter when it is followed by an end delimiter' do
-          expect { parse_text(start_text + '>>>>>>> README.md' + end_text) }.
-            to raise_error(Gitlab::Conflict::Parser::UnexpectedDelimiter)
+          expect { parse_text(start_text + '>>>>>>> README.md' + end_text) }
+            .to raise_error(Gitlab::Conflict::Parser::UnexpectedDelimiter)
         end
 
         it 'raises UnexpectedDelimiter when it is followed by another start delimiter' do
-          expect { parse_text(start_text + start_text + end_text) }.
-            to raise_error(Gitlab::Conflict::Parser::UnexpectedDelimiter)
+          expect { parse_text(start_text + start_text + end_text) }
+            .to raise_error(Gitlab::Conflict::Parser::UnexpectedDelimiter)
         end
 
         it 'does not raise when it is followed by a start delimiter for a different path' do
-          expect { parse_text(start_text + '>>>>>>> some-other-path.md' + end_text) }.
-            not_to raise_error
+          expect { parse_text(start_text + '>>>>>>> some-other-path.md' + end_text) }
+            .not_to raise_error
         end
       end
 
@@ -162,59 +162,59 @@ CONFLICT
         let(:end_text) { "\n>>>>>>> README.md" }
 
         it 'raises UnexpectedDelimiter when it is followed by another middle delimiter' do
-          expect { parse_text(start_text + '=======' + end_text) }.
-            to raise_error(Gitlab::Conflict::Parser::UnexpectedDelimiter)
+          expect { parse_text(start_text + '=======' + end_text) }
+            .to raise_error(Gitlab::Conflict::Parser::UnexpectedDelimiter)
         end
 
         it 'raises UnexpectedDelimiter when it is followed by a start delimiter' do
-          expect { parse_text(start_text + start_text + end_text) }.
-            to raise_error(Gitlab::Conflict::Parser::UnexpectedDelimiter)
+          expect { parse_text(start_text + start_text + end_text) }
+            .to raise_error(Gitlab::Conflict::Parser::UnexpectedDelimiter)
         end
 
         it 'does not raise when it is followed by a start delimiter for another path' do
-          expect { parse_text(start_text + '<<<<<<< some-other-path.md' + end_text) }.
-            not_to raise_error
+          expect { parse_text(start_text + '<<<<<<< some-other-path.md' + end_text) }
+            .not_to raise_error
         end
       end
 
       it 'raises MissingEndDelimiter when there is no end delimiter at the end' do
         start_text = "<<<<<<< README.md\n=======\n"
 
-        expect { parse_text(start_text) }.
-          to raise_error(Gitlab::Conflict::Parser::MissingEndDelimiter)
+        expect { parse_text(start_text) }
+          .to raise_error(Gitlab::Conflict::Parser::MissingEndDelimiter)
 
-        expect { parse_text(start_text + '>>>>>>> some-other-path.md') }.
-          to raise_error(Gitlab::Conflict::Parser::MissingEndDelimiter)
+        expect { parse_text(start_text + '>>>>>>> some-other-path.md') }
+          .to raise_error(Gitlab::Conflict::Parser::MissingEndDelimiter)
       end
     end
 
     context 'other file types' do
       it 'raises UnmergeableFile when lines is blank, indicating a binary file' do
-        expect { parse_text('') }.
-          to raise_error(Gitlab::Conflict::Parser::UnmergeableFile)
+        expect { parse_text('') }
+          .to raise_error(Gitlab::Conflict::Parser::UnmergeableFile)
 
-        expect { parse_text(nil) }.
-          to raise_error(Gitlab::Conflict::Parser::UnmergeableFile)
+        expect { parse_text(nil) }
+          .to raise_error(Gitlab::Conflict::Parser::UnmergeableFile)
       end
 
       it 'raises UnmergeableFile when the file is over 200 KB' do
-        expect { parse_text('a' * 204801) }.
-          to raise_error(Gitlab::Conflict::Parser::UnmergeableFile)
+        expect { parse_text('a' * 204801) }
+          .to raise_error(Gitlab::Conflict::Parser::UnmergeableFile)
       end
 
       # All text from Rugged has an encoding of ASCII_8BIT, so force that in
       # these strings.
       context 'when the file contains UTF-8 characters' do
         it 'does not raise' do
-          expect { parse_text("Espa\xC3\xB1a".force_encoding(Encoding::ASCII_8BIT)) }.
-            not_to raise_error
+          expect { parse_text("Espa\xC3\xB1a".force_encoding(Encoding::ASCII_8BIT)) }
+            .not_to raise_error
         end
       end
 
       context 'when the file contains non-UTF-8 characters' do
         it 'raises UnsupportedEncoding' do
-          expect { parse_text("a\xC4\xFC".force_encoding(Encoding::ASCII_8BIT)) }.
-            to raise_error(Gitlab::Conflict::Parser::UnsupportedEncoding)
+          expect { parse_text("a\xC4\xFC".force_encoding(Encoding::ASCII_8BIT)) }
+            .to raise_error(Gitlab::Conflict::Parser::UnsupportedEncoding)
         end
       end
     end
diff --git a/spec/lib/gitlab/current_settings_spec.rb b/spec/lib/gitlab/current_settings_spec.rb
index fda39d7861009e9225d6b91eca20c053d9f40d49..a566f24f6a6a31d69769ae016bb2344d7804bf7d 100644
--- a/spec/lib/gitlab/current_settings_spec.rb
+++ b/spec/lib/gitlab/current_settings_spec.rb
@@ -32,6 +32,37 @@ describe Gitlab::CurrentSettings do
 
         expect(current_application_settings).to be_a(ApplicationSetting)
       end
+
+      context 'with migrations pending' do
+        before do
+          expect(ActiveRecord::Migrator).to receive(:needs_migration?).and_return(true)
+        end
+
+        it 'returns an in-memory ApplicationSetting object' do
+          settings = current_application_settings
+
+          expect(settings).to be_a(OpenStruct)
+          expect(settings.sign_in_enabled?).to eq(settings.sign_in_enabled)
+          expect(settings.sign_up_enabled?).to eq(settings.sign_up_enabled)
+        end
+
+        it 'uses the existing database settings and falls back to defaults' do
+          db_settings = create(:application_setting,
+                               home_page_url: 'http://mydomain.com',
+                               signup_enabled: false)
+          settings = current_application_settings
+          app_defaults = ApplicationSetting.last
+
+          expect(settings).to be_a(OpenStruct)
+          expect(settings.home_page_url).to eq(db_settings.home_page_url)
+          expect(settings.signup_enabled?).to be_falsey
+          expect(settings.signup_enabled).to be_falsey
+
+          # Check that unspecified values use the defaults
+          settings.reject! { |key, _| [:home_page_url, :signup_enabled].include? key }
+          settings.each { |key, _| expect(settings[key]).to eq(app_defaults[key]) }
+        end
+      end
     end
 
     context 'with DB unavailable' do
diff --git a/spec/lib/gitlab/data_builder/push_spec.rb b/spec/lib/gitlab/data_builder/push_spec.rb
index e59cba35b2feb737bfe06cc20f4d2c5113eccec1..73936969832fb8ff5d200f49fa765f1bcd7f78c7 100644
--- a/spec/lib/gitlab/data_builder/push_spec.rb
+++ b/spec/lib/gitlab/data_builder/push_spec.rb
@@ -47,8 +47,8 @@ describe Gitlab::DataBuilder::Push, lib: true do
     include_examples 'deprecated repository hook data'
 
     it 'does not raise an error when given nil commits' do
-      expect { described_class.build(spy, spy, spy, spy, spy, nil) }.
-        not_to raise_error
+      expect { described_class.build(spy, spy, spy, spy, spy, nil) }
+        .not_to raise_error
     end
   end
 end
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
index 30aa463faf88a9617092e12e1302cba302849140..6a0485112c121db674be7744810f8175a8c1f1ea 100644
--- a/spec/lib/gitlab/database/migration_helpers_spec.rb
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -57,15 +57,15 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
         end
 
         it 'creates the index concurrently' do
-          expect(model).to receive(:add_index).
-            with(:users, :foo, algorithm: :concurrently)
+          expect(model).to receive(:add_index)
+            .with(:users, :foo, algorithm: :concurrently)
 
           model.add_concurrent_index(:users, :foo)
         end
 
         it 'creates unique index concurrently' do
-          expect(model).to receive(:add_index).
-            with(:users, :foo, { algorithm: :concurrently, unique: true })
+          expect(model).to receive(:add_index)
+            .with(:users, :foo, { algorithm: :concurrently, unique: true })
 
           model.add_concurrent_index(:users, :foo, unique: true)
         end
@@ -75,8 +75,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
         it 'creates a regular index' do
           expect(Gitlab::Database).to receive(:postgresql?).and_return(false)
 
-          expect(model).to receive(:add_index).
-            with(:users, :foo, {})
+          expect(model).to receive(:add_index)
+            .with(:users, :foo, {})
 
           model.add_concurrent_index(:users, :foo)
         end
@@ -87,8 +87,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
       it 'raises RuntimeError' do
         expect(model).to receive(:transaction_open?).and_return(true)
 
-        expect { model.add_concurrent_index(:users, :foo) }.
-          to raise_error(RuntimeError)
+        expect { model.add_concurrent_index(:users, :foo) }
+          .to raise_error(RuntimeError)
       end
     end
   end
@@ -106,15 +106,15 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
         end
 
         it 'removes the index concurrently by column name' do
-          expect(model).to receive(:remove_index).
-            with(:users, { algorithm: :concurrently, column: :foo })
+          expect(model).to receive(:remove_index)
+            .with(:users, { algorithm: :concurrently, column: :foo })
 
           model.remove_concurrent_index(:users, :foo)
         end
 
         it 'removes the index concurrently by index name' do
-          expect(model).to receive(:remove_index).
-            with(:users, { algorithm: :concurrently, name: "index_x_by_y" })
+          expect(model).to receive(:remove_index)
+            .with(:users, { algorithm: :concurrently, name: "index_x_by_y" })
 
           model.remove_concurrent_index_by_name(:users, "index_x_by_y")
         end
@@ -124,8 +124,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
         it 'removes an index' do
           expect(Gitlab::Database).to receive(:postgresql?).and_return(false)
 
-          expect(model).to receive(:remove_index).
-            with(:users, { column: :foo })
+          expect(model).to receive(:remove_index)
+            .with(:users, { column: :foo })
 
           model.remove_concurrent_index(:users, :foo)
         end
@@ -136,8 +136,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
       it 'raises RuntimeError' do
         expect(model).to receive(:transaction_open?).and_return(true)
 
-        expect { model.remove_concurrent_index(:users, :foo) }.
-          to raise_error(RuntimeError)
+        expect { model.remove_concurrent_index(:users, :foo) }
+          .to raise_error(RuntimeError)
       end
     end
   end
@@ -162,8 +162,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
         it 'creates a regular foreign key' do
           allow(Gitlab::Database).to receive(:mysql?).and_return(true)
 
-          expect(model).to receive(:add_foreign_key).
-            with(:projects, :users, column: :user_id, on_delete: :cascade)
+          expect(model).to receive(:add_foreign_key)
+            .with(:projects, :users, column: :user_id, on_delete: :cascade)
 
           model.add_concurrent_foreign_key(:projects, :users, column: :user_id)
         end
@@ -307,16 +307,16 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
 
           expect(model).to receive(:transaction).and_yield
 
-          expect(model).to receive(:add_column).
-            with(:projects, :foo, :integer, default: nil)
+          expect(model).to receive(:add_column)
+            .with(:projects, :foo, :integer, default: nil)
 
-          expect(model).to receive(:change_column_default).
-            with(:projects, :foo, 10)
+          expect(model).to receive(:change_column_default)
+            .with(:projects, :foo, 10)
         end
 
         it 'adds the column while allowing NULL values' do
-          expect(model).to receive(:update_column_in_batches).
-            with(:projects, :foo, 10)
+          expect(model).to receive(:update_column_in_batches)
+            .with(:projects, :foo, 10)
 
           expect(model).not_to receive(:change_column_null)
 
@@ -326,22 +326,22 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
         end
 
         it 'adds the column while not allowing NULL values' do
-          expect(model).to receive(:update_column_in_batches).
-            with(:projects, :foo, 10)
+          expect(model).to receive(:update_column_in_batches)
+            .with(:projects, :foo, 10)
 
-          expect(model).to receive(:change_column_null).
-            with(:projects, :foo, false)
+          expect(model).to receive(:change_column_null)
+            .with(:projects, :foo, false)
 
           model.add_column_with_default(:projects, :foo, :integer, default: 10)
         end
 
         it 'removes the added column whenever updating the rows fails' do
-          expect(model).to receive(:update_column_in_batches).
-            with(:projects, :foo, 10).
-            and_raise(RuntimeError)
+          expect(model).to receive(:update_column_in_batches)
+            .with(:projects, :foo, 10)
+            .and_raise(RuntimeError)
 
-          expect(model).to receive(:remove_column).
-            with(:projects, :foo)
+          expect(model).to receive(:remove_column)
+            .with(:projects, :foo)
 
           expect do
             model.add_column_with_default(:projects, :foo, :integer, default: 10)
@@ -349,12 +349,12 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
         end
 
         it 'removes the added column whenever changing a column NULL constraint fails' do
-          expect(model).to receive(:change_column_null).
-            with(:projects, :foo, false).
-            and_raise(RuntimeError)
+          expect(model).to receive(:change_column_null)
+            .with(:projects, :foo, false)
+            .and_raise(RuntimeError)
 
-          expect(model).to receive(:remove_column).
-            with(:projects, :foo)
+          expect(model).to receive(:remove_column)
+            .with(:projects, :foo)
 
           expect do
             model.add_column_with_default(:projects, :foo, :integer, default: 10)
@@ -370,8 +370,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
           allow(model).to receive(:change_column_null).with(:projects, :foo, false)
           allow(model).to receive(:change_column_default).with(:projects, :foo, 10)
 
-          expect(model).to receive(:add_column).
-            with(:projects, :foo, :integer, default: nil, limit: 8)
+          expect(model).to receive(:add_column)
+            .with(:projects, :foo, :integer, default: nil, limit: 8)
 
           model.add_column_with_default(:projects, :foo, :integer, default: 10, limit: 8)
         end
@@ -394,8 +394,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
       it 'raises RuntimeError' do
         allow(model).to receive(:transaction_open?).and_return(true)
 
-        expect { model.rename_column_concurrently(:users, :old, :new) }.
-          to raise_error(RuntimeError)
+        expect { model.rename_column_concurrently(:users, :old, :new) }
+          .to raise_error(RuntimeError)
       end
     end
 
@@ -426,17 +426,17 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
         it 'renames a column concurrently' do
           allow(Gitlab::Database).to receive(:postgresql?).and_return(false)
 
-          expect(model).to receive(:install_rename_triggers_for_mysql).
-            with(trigger_name, 'users', 'old', 'new')
+          expect(model).to receive(:install_rename_triggers_for_mysql)
+            .with(trigger_name, 'users', 'old', 'new')
 
-          expect(model).to receive(:add_column).
-            with(:users, :new, :integer,
+          expect(model).to receive(:add_column)
+            .with(:users, :new, :integer,
                  limit: old_column.limit,
                  precision: old_column.precision,
                  scale: old_column.scale)
 
-          expect(model).to receive(:change_column_default).
-            with(:users, :new, old_column.default)
+          expect(model).to receive(:change_column_default)
+            .with(:users, :new, old_column.default)
 
           expect(model).to receive(:update_column_in_batches)
 
@@ -453,17 +453,17 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
         it 'renames a column concurrently' do
           allow(Gitlab::Database).to receive(:postgresql?).and_return(true)
 
-          expect(model).to receive(:install_rename_triggers_for_postgresql).
-            with(trigger_name, 'users', 'old', 'new')
+          expect(model).to receive(:install_rename_triggers_for_postgresql)
+            .with(trigger_name, 'users', 'old', 'new')
 
-          expect(model).to receive(:add_column).
-            with(:users, :new, :integer,
+          expect(model).to receive(:add_column)
+            .with(:users, :new, :integer,
                  limit: old_column.limit,
                  precision: old_column.precision,
                  scale: old_column.scale)
 
-          expect(model).to receive(:change_column_default).
-            with(:users, :new, old_column.default)
+          expect(model).to receive(:change_column_default)
+            .with(:users, :new, old_column.default)
 
           expect(model).to receive(:update_column_in_batches)
 
@@ -482,8 +482,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
     it 'cleans up the renaming procedure for PostgreSQL' do
       allow(Gitlab::Database).to receive(:postgresql?).and_return(true)
 
-      expect(model).to receive(:remove_rename_triggers_for_postgresql).
-        with(:users, /trigger_.{12}/)
+      expect(model).to receive(:remove_rename_triggers_for_postgresql)
+        .with(:users, /trigger_.{12}/)
 
       expect(model).to receive(:remove_column).with(:users, :old)
 
@@ -493,8 +493,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
     it 'cleans up the renaming procedure for MySQL' do
       allow(Gitlab::Database).to receive(:postgresql?).and_return(false)
 
-      expect(model).to receive(:remove_rename_triggers_for_mysql).
-        with(/trigger_.{12}/)
+      expect(model).to receive(:remove_rename_triggers_for_mysql)
+        .with(/trigger_.{12}/)
 
       expect(model).to receive(:remove_column).with(:users, :old)
 
@@ -504,8 +504,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
 
   describe '#change_column_type_concurrently' do
     it 'changes the column type' do
-      expect(model).to receive(:rename_column_concurrently).
-        with('users', 'username', 'username_for_type_change', type: :text)
+      expect(model).to receive(:rename_column_concurrently)
+        .with('users', 'username', 'username_for_type_change', type: :text)
 
       model.change_column_type_concurrently('users', 'username', :text)
     end
@@ -513,11 +513,11 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
 
   describe '#cleanup_concurrent_column_type_change' do
     it 'cleans up the type changing procedure' do
-      expect(model).to receive(:cleanup_concurrent_column_rename).
-        with('users', 'username', 'username_for_type_change')
+      expect(model).to receive(:cleanup_concurrent_column_rename)
+        .with('users', 'username', 'username_for_type_change')
 
-      expect(model).to receive(:rename_column).
-        with('users', 'username_for_type_change', 'username')
+      expect(model).to receive(:rename_column)
+        .with('users', 'username_for_type_change', 'username')
 
       model.cleanup_concurrent_column_type_change('users', 'username')
     end
@@ -525,11 +525,11 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
 
   describe '#install_rename_triggers_for_postgresql' do
     it 'installs the triggers for PostgreSQL' do
-      expect(model).to receive(:execute).
-        with(/CREATE OR REPLACE FUNCTION foo()/m)
+      expect(model).to receive(:execute)
+        .with(/CREATE OR REPLACE FUNCTION foo()/m)
 
-      expect(model).to receive(:execute).
-        with(/CREATE TRIGGER foo/m)
+      expect(model).to receive(:execute)
+        .with(/CREATE TRIGGER foo/m)
 
       model.install_rename_triggers_for_postgresql('foo', :users, :old, :new)
     end
@@ -537,11 +537,11 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
 
   describe '#install_rename_triggers_for_mysql' do
     it 'installs the triggers for MySQL' do
-      expect(model).to receive(:execute).
-        with(/CREATE TRIGGER foo_insert.+ON users/m)
+      expect(model).to receive(:execute)
+        .with(/CREATE TRIGGER foo_insert.+ON users/m)
 
-      expect(model).to receive(:execute).
-        with(/CREATE TRIGGER foo_update.+ON users/m)
+      expect(model).to receive(:execute)
+        .with(/CREATE TRIGGER foo_update.+ON users/m)
 
       model.install_rename_triggers_for_mysql('foo', :users, :old, :new)
     end
@@ -567,8 +567,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
 
   describe '#rename_trigger_name' do
     it 'returns a String' do
-      expect(model.rename_trigger_name(:users, :foo, :bar)).
-        to match(/trigger_.{12}/)
+      expect(model.rename_trigger_name(:users, :foo, :bar))
+        .to match(/trigger_.{12}/)
     end
   end
 
@@ -607,11 +607,11 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
                        lengths: [],
                        orders: [])
 
-        allow(model).to receive(:indexes_for).with(:issues, 'project_id').
-          and_return([index])
+        allow(model).to receive(:indexes_for).with(:issues, 'project_id')
+          .and_return([index])
 
-        expect(model).to receive(:add_concurrent_index).
-          with(:issues,
+        expect(model).to receive(:add_concurrent_index)
+          .with(:issues,
                %w(gl_project_id),
                unique: false,
                name: 'index_on_issues_gl_project_id',
@@ -634,11 +634,11 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
                        lengths: [],
                        orders: [])
 
-        allow(model).to receive(:indexes_for).with(:issues, 'project_id').
-          and_return([index])
+        allow(model).to receive(:indexes_for).with(:issues, 'project_id')
+          .and_return([index])
 
-        expect(model).to receive(:add_concurrent_index).
-          with(:issues,
+        expect(model).to receive(:add_concurrent_index)
+          .with(:issues,
                %w(gl_project_id foobar),
                unique: false,
                name: 'index_on_issues_gl_project_id_foobar',
@@ -661,11 +661,11 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
                        lengths: [],
                        orders: [])
 
-        allow(model).to receive(:indexes_for).with(:issues, 'project_id').
-          and_return([index])
+        allow(model).to receive(:indexes_for).with(:issues, 'project_id')
+          .and_return([index])
 
-        expect(model).to receive(:add_concurrent_index).
-          with(:issues,
+        expect(model).to receive(:add_concurrent_index)
+          .with(:issues,
                %w(gl_project_id),
                unique: false,
                name: 'index_on_issues_gl_project_id',
@@ -689,11 +689,11 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
                        lengths: [],
                        orders: [])
 
-        allow(model).to receive(:indexes_for).with(:issues, 'project_id').
-          and_return([index])
+        allow(model).to receive(:indexes_for).with(:issues, 'project_id')
+          .and_return([index])
 
-        expect(model).to receive(:add_concurrent_index).
-          with(:issues,
+        expect(model).to receive(:add_concurrent_index)
+          .with(:issues,
                %w(gl_project_id),
                unique: false,
                name: 'index_on_issues_gl_project_id',
@@ -717,11 +717,11 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
                        lengths: [],
                        orders: [])
 
-        allow(model).to receive(:indexes_for).with(:issues, 'project_id').
-          and_return([index])
+        allow(model).to receive(:indexes_for).with(:issues, 'project_id')
+          .and_return([index])
 
-        expect(model).to receive(:add_concurrent_index).
-          with(:issues,
+        expect(model).to receive(:add_concurrent_index)
+          .with(:issues,
                %w(gl_project_id),
                unique: false,
                name: 'index_on_issues_gl_project_id',
@@ -745,11 +745,11 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
                        lengths: [],
                        orders: [])
 
-        allow(model).to receive(:indexes_for).with(:issues, 'project_id').
-          and_return([index])
+        allow(model).to receive(:indexes_for).with(:issues, 'project_id')
+          .and_return([index])
 
-        expect { model.copy_indexes(:issues, :project_id, :gl_project_id) }.
-          to raise_error(RuntimeError)
+        expect { model.copy_indexes(:issues, :project_id, :gl_project_id) }
+          .to raise_error(RuntimeError)
       end
     end
   end
@@ -761,11 +761,11 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
                   to_table: 'projects',
                   on_delete: :cascade)
 
-      allow(model).to receive(:foreign_keys_for).with(:issues, :project_id).
-        and_return([fk])
+      allow(model).to receive(:foreign_keys_for).with(:issues, :project_id)
+        .and_return([fk])
 
-      expect(model).to receive(:add_concurrent_foreign_key).
-        with('issues', 'projects', column: :gl_project_id, on_delete: :cascade)
+      expect(model).to receive(:add_concurrent_foreign_key)
+        .with('issues', 'projects', column: :gl_project_id, on_delete: :cascade)
 
       model.copy_foreign_keys(:issues, :project_id, :gl_project_id)
     end
@@ -790,8 +790,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
       end
 
       it 'builds the sql with correct functions' do
-        expect(model.replace_sql(Arel::Table.new(:users)[:first_name], "Alice", "Eve").to_s).
-          to include('regexp_replace')
+        expect(model.replace_sql(Arel::Table.new(:users)[:first_name], "Alice", "Eve").to_s)
+          .to include('regexp_replace')
       end
     end
 
@@ -801,8 +801,8 @@ describe Gitlab::Database::MigrationHelpers, lib: true do
       end
 
       it 'builds the sql with the correct functions' do
-        expect(model.replace_sql(Arel::Table.new(:users)[:first_name], "Alice", "Eve").to_s).
-          to include('locate', 'insert')
+        expect(model.replace_sql(Arel::Table.new(:users)[:first_name], "Alice", "Eve").to_s)
+          .to include('locate', 'insert')
       end
     end
 
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
index ce2b5d620fdcf464089df5235cb2c03d97e5c25c..aa63f6f980595008b091af346ee6d53c60700352 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
@@ -21,8 +21,8 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces do
         parent = create(:group, path: 'parent')
         child = create(:group, path: 'the-path', parent: parent)
 
-        found_ids = subject.namespaces_for_paths(type: :child).
-                      map(&:id)
+        found_ids = subject.namespaces_for_paths(type: :child)
+                      .map(&:id)
 
         expect(found_ids).to contain_exactly(child.id)
       end
@@ -38,8 +38,8 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces do
                            path: 'the-path',
                            parent: create(:group))
 
-        found_ids = subject.namespaces_for_paths(type: :child).
-                      map(&:id)
+        found_ids = subject.namespaces_for_paths(type: :child)
+                      .map(&:id)
 
         expect(found_ids).to contain_exactly(namespace.id)
       end
@@ -53,8 +53,8 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces do
                            path: 'the-path',
                            parent: create(:group))
 
-        found_ids = subject.namespaces_for_paths(type: :child).
-                      map(&:id)
+        found_ids = subject.namespaces_for_paths(type: :child)
+                      .map(&:id)
 
         expect(found_ids).to contain_exactly(namespace.id)
       end
@@ -68,8 +68,8 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces do
                                   path: 'the-path',
                                   parent: create(:group))
 
-        found_ids = subject.namespaces_for_paths(type: :top_level).
-                      map(&:id)
+        found_ids = subject.namespaces_for_paths(type: :top_level)
+                      .map(&:id)
 
         expect(found_ids).to contain_exactly(root_namespace.id)
       end
@@ -81,8 +81,8 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces do
                                   path: 'the-path',
                                   parent: create(:group))
 
-        found_ids = subject.namespaces_for_paths(type: :top_level).
-                      map(&:id)
+        found_ids = subject.namespaces_for_paths(type: :top_level)
+                      .map(&:id)
 
         expect(found_ids).to contain_exactly(root_namespace.id)
       end
@@ -140,9 +140,9 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces do
     let(:namespace) { create(:group, name: 'the-path') }
 
     it 'renames paths & routes for the namespace' do
-      expect(subject).to receive(:rename_path_for_routable).
-                           with(namespace).
-                           and_call_original
+      expect(subject).to receive(:rename_path_for_routable)
+                           .with(namespace)
+                           .and_call_original
 
       subject.rename_namespace(namespace)
 
@@ -211,15 +211,15 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces do
     end
 
     it 'renames top level namespaces the namespace' do
-      expect(subject).to receive(:rename_namespace).
-                           with(migration_namespace(top_level_namespace))
+      expect(subject).to receive(:rename_namespace)
+                           .with(migration_namespace(top_level_namespace))
 
       subject.rename_namespaces(type: :top_level)
     end
 
     it 'renames child namespaces' do
-      expect(subject).to receive(:rename_namespace).
-                           with(migration_namespace(child_namespace))
+      expect(subject).to receive(:rename_namespace)
+                           .with(migration_namespace(child_namespace))
 
       subject.rename_namespaces(type: :child)
     end
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
index 59e8de2712d05b0a7a3418501985a3460081b5b5..9a6ed98898dca8f4126c6b8b62cb3be76eedaccc 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
@@ -13,8 +13,8 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects do
       namespace = create(:namespace, path: 'hello')
       project = create(:empty_project, path: 'THE-path', namespace: namespace)
 
-      result_ids = described_class.new(['Hello/the-path'], migration).
-                     projects_for_paths.map(&:id)
+      result_ids = described_class.new(['Hello/the-path'], migration)
+                     .projects_for_paths.map(&:id)
 
       expect(result_ids).to contain_exactly(project.id)
     end
@@ -39,8 +39,8 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects do
     end
 
     it 'invalidates the markdown cache of related projects' do
-      expect(subject).to receive(:remove_cached_html_for_projects).
-                           with(projects.map(&:id))
+      expect(subject).to receive(:remove_cached_html_for_projects)
+                           .with(projects.map(&:id))
 
       subject.rename_projects
     end
@@ -54,9 +54,9 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects do
     end
 
     it 'renames path & route for the project' do
-      expect(subject).to receive(:rename_path_for_routable).
-                           with(project).
-                           and_call_original
+      expect(subject).to receive(:rename_path_for_routable)
+                           .with(project)
+                           .and_call_original
 
       subject.rename_project(project)
 
@@ -64,24 +64,24 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects do
     end
 
     it 'moves the wiki & the repo' do
-      expect(subject).to receive(:move_repository).
-                           with(project, 'known-parent/the-path.wiki', 'known-parent/the-path0.wiki')
-      expect(subject).to receive(:move_repository).
-                           with(project, 'known-parent/the-path', 'known-parent/the-path0')
+      expect(subject).to receive(:move_repository)
+                           .with(project, 'known-parent/the-path.wiki', 'known-parent/the-path0.wiki')
+      expect(subject).to receive(:move_repository)
+                           .with(project, 'known-parent/the-path', 'known-parent/the-path0')
 
       subject.rename_project(project)
     end
 
     it 'moves uploads' do
-      expect(subject).to receive(:move_uploads).
-                           with('known-parent/the-path', 'known-parent/the-path0')
+      expect(subject).to receive(:move_uploads)
+                           .with('known-parent/the-path', 'known-parent/the-path0')
 
       subject.rename_project(project)
     end
 
     it 'moves pages' do
-      expect(subject).to receive(:move_pages).
-                           with('known-parent/the-path', 'known-parent/the-path0')
+      expect(subject).to receive(:move_pages)
+                           .with('known-parent/the-path', 'known-parent/the-path0')
 
       subject.rename_project(project)
     end
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb
index f8cc1eb91ec9945097e9f1e77ba713ea99a8c495..bdd3af4ad44bac6054b43199063225b4c757805c 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb
@@ -3,11 +3,11 @@ require 'spec_helper'
 shared_examples 'renames child namespaces' do |type|
   it 'renames namespaces' do
     rename_namespaces = double
-    expect(described_class::RenameNamespaces).
-      to receive(:new).with(['first-path', 'second-path'], subject).
-           and_return(rename_namespaces)
-    expect(rename_namespaces).to receive(:rename_namespaces).
-                                   with(type: :child)
+    expect(described_class::RenameNamespaces)
+      .to receive(:new).with(['first-path', 'second-path'], subject)
+           .and_return(rename_namespaces)
+    expect(rename_namespaces).to receive(:rename_namespaces)
+                                   .with(type: :child)
 
     subject.rename_wildcard_paths(['first-path', 'second-path'])
   end
@@ -29,9 +29,9 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1 do
 
     it 'should rename projects' do
       rename_projects = double
-      expect(described_class::RenameProjects).
-        to receive(:new).with(['the-path'], subject).
-             and_return(rename_projects)
+      expect(described_class::RenameProjects)
+        .to receive(:new).with(['the-path'], subject)
+             .and_return(rename_projects)
 
       expect(rename_projects).to receive(:rename_projects)
 
@@ -42,11 +42,11 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1 do
   describe '#rename_root_paths' do
     it 'should rename namespaces' do
       rename_namespaces = double
-      expect(described_class::RenameNamespaces).
-        to receive(:new).with(['the-path'], subject).
-             and_return(rename_namespaces)
-      expect(rename_namespaces).to receive(:rename_namespaces).
-                           with(type: :top_level)
+      expect(described_class::RenameNamespaces)
+        .to receive(:new).with(['the-path'], subject)
+             .and_return(rename_namespaces)
+      expect(rename_namespaces).to receive(:rename_namespaces)
+                           .with(type: :top_level)
 
       subject.rename_root_paths('the-path')
     end
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index 26e5d73d3334f16533290e4a6c6646379eb07ee8..5e6206b96c72e44afc44731a76090c033012c803 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -34,8 +34,8 @@ describe Gitlab::Database, lib: true do
   describe '.version' do
     context "on mysql" do
       it "extracts the version number" do
-        allow(described_class).to receive(:database_version).
-          and_return("5.7.12-standard")
+        allow(described_class).to receive(:database_version)
+          .and_return("5.7.12-standard")
 
         expect(described_class.version).to eq '5.7.12-standard'
       end
@@ -43,8 +43,8 @@ describe Gitlab::Database, lib: true do
 
     context "on postgresql" do
       it "extracts the version number" do
-        allow(described_class).to receive(:database_version).
-          and_return("PostgreSQL 9.4.4 on x86_64-apple-darwin14.3.0")
+        allow(described_class).to receive(:database_version)
+          .and_return("PostgreSQL 9.4.4 on x86_64-apple-darwin14.3.0")
 
         expect(described_class.version).to eq '9.4.4'
       end
@@ -129,6 +129,55 @@ describe Gitlab::Database, lib: true do
     end
   end
 
+  describe '.bulk_insert' do
+    before do
+      allow(described_class).to receive(:connection).and_return(connection)
+      allow(connection).to receive(:quote_column_name, &:itself)
+      allow(connection).to receive(:quote, &:itself)
+      allow(connection).to receive(:execute)
+    end
+
+    let(:connection) { double(:connection) }
+
+    let(:rows) do
+      [
+        { a: 1, b: 2, c: 3 },
+        { c: 6, a: 4, b: 5 }
+      ]
+    end
+
+    it 'does nothing with empty rows' do
+      expect(connection).not_to receive(:execute)
+
+      described_class.bulk_insert('test', [])
+    end
+
+    it 'uses the ordering from the first row' do
+      expect(connection).to receive(:execute) do |sql|
+        expect(sql).to include('(1, 2, 3)')
+        expect(sql).to include('(4, 5, 6)')
+      end
+
+      described_class.bulk_insert('test', rows)
+    end
+
+    it 'quotes column names' do
+      expect(connection).to receive(:quote_column_name).with(:a)
+      expect(connection).to receive(:quote_column_name).with(:b)
+      expect(connection).to receive(:quote_column_name).with(:c)
+
+      described_class.bulk_insert('test', rows)
+    end
+
+    it 'quotes values' do
+      1.upto(6) do |i|
+        expect(connection).to receive(:quote).with(i)
+      end
+
+      described_class.bulk_insert('test', rows)
+    end
+  end
+
   describe '.create_connection_pool' do
     it 'creates a new connection pool with specific pool size' do
       pool = described_class.create_connection_pool(5)
diff --git a/spec/lib/gitlab/downtime_check_spec.rb b/spec/lib/gitlab/downtime_check_spec.rb
index 42d895e548e3aa97b948034d83a8148f46c77f4e..1f1e4e0216c4267dc9ae34aca4929102e62ff3aa 100644
--- a/spec/lib/gitlab/downtime_check_spec.rb
+++ b/spec/lib/gitlab/downtime_check_spec.rb
@@ -11,12 +11,12 @@ describe Gitlab::DowntimeCheck do
 
     context 'when a migration does not specify if downtime is required' do
       it 'raises RuntimeError' do
-        expect(subject).to receive(:class_for_migration_file).
-          with(path).
-          and_return(Class.new)
+        expect(subject).to receive(:class_for_migration_file)
+          .with(path)
+          .and_return(Class.new)
 
-        expect { subject.check([path]) }.
-          to raise_error(RuntimeError, /it requires downtime/)
+        expect { subject.check([path]) }
+          .to raise_error(RuntimeError, /it requires downtime/)
       end
     end
 
@@ -25,12 +25,12 @@ describe Gitlab::DowntimeCheck do
         it 'raises RuntimeError' do
           stub_const('TestMigration::DOWNTIME', true)
 
-          expect(subject).to receive(:class_for_migration_file).
-            with(path).
-            and_return(TestMigration)
+          expect(subject).to receive(:class_for_migration_file)
+            .with(path)
+            .and_return(TestMigration)
 
-          expect { subject.check([path]) }.
-            to raise_error(RuntimeError, /no reason was given/)
+          expect { subject.check([path]) }
+            .to raise_error(RuntimeError, /no reason was given/)
         end
       end
 
@@ -39,9 +39,9 @@ describe Gitlab::DowntimeCheck do
           stub_const('TestMigration::DOWNTIME', true)
           stub_const('TestMigration::DOWNTIME_REASON', 'foo')
 
-          expect(subject).to receive(:class_for_migration_file).
-            with(path).
-            and_return(TestMigration)
+          expect(subject).to receive(:class_for_migration_file)
+            .with(path)
+            .and_return(TestMigration)
 
           messages = subject.check([path])
 
@@ -65,9 +65,9 @@ describe Gitlab::DowntimeCheck do
 
       expect(subject).to receive(:require).with(path)
 
-      expect(subject).to receive(:class_for_migration_file).
-        with(path).
-        and_return(TestMigration)
+      expect(subject).to receive(:class_for_migration_file)
+        .with(path)
+        .and_return(TestMigration)
 
       expect(subject).to receive(:puts).with(an_instance_of(String))
 
diff --git a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
index 3f79eaf7afbb308a84941fdd2b83980a2dc646e0..cd0309e248de1fcdf0d9c8ae1374cdf5e2a7c1bc 100644
--- a/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
+++ b/spec/lib/gitlab/email/handler/create_note_handler_spec.rb
@@ -91,7 +91,7 @@ describe Gitlab::Email::Handler::CreateNoteHandler, lib: true do
     end
   end
 
-  context 'when the note contains slash commands' do
+  context 'when the note contains quick actions' do
     let!(:email_raw) { fixture_file("emails/commands_in_reply.eml") }
 
     context 'and current user cannot update noteable' do
diff --git a/spec/lib/gitlab/email/reply_parser_spec.rb b/spec/lib/gitlab/email/reply_parser_spec.rb
index 28698e89c338cee596d73e3cb9ddebc370c9b3cd..2ea5e6460a35d1377777c27b803bc492d60d678a 100644
--- a/spec/lib/gitlab/email/reply_parser_spec.rb
+++ b/spec/lib/gitlab/email/reply_parser_spec.rb
@@ -20,8 +20,8 @@ describe Gitlab::Email::ReplyParser, lib: true do
     end
 
     it "properly renders plaintext-only email" do
-      expect(test_parse_body(fixture_file("emails/plaintext_only.eml"))).
-        to eq(
+      expect(test_parse_body(fixture_file("emails/plaintext_only.eml")))
+        .to eq(
           <<-BODY.strip_heredoc.chomp
             ### reply from default mail client in Windows 8.1 Metro
 
@@ -46,8 +46,8 @@ describe Gitlab::Email::ReplyParser, lib: true do
     end
 
     it "handles multiple paragraphs" do
-      expect(test_parse_body(fixture_file("emails/paragraphs.eml"))).
-        to eq(
+      expect(test_parse_body(fixture_file("emails/paragraphs.eml")))
+        .to eq(
           <<-BODY.strip_heredoc.chomp
             Is there any reason the *old* candy can't be be kept in silos while the new candy
             is imported into *new* silos?
@@ -61,8 +61,8 @@ describe Gitlab::Email::ReplyParser, lib: true do
     end
 
     it "handles multiple paragraphs when parsing html" do
-      expect(test_parse_body(fixture_file("emails/html_paragraphs.eml"))).
-        to eq(
+      expect(test_parse_body(fixture_file("emails/html_paragraphs.eml")))
+        .to eq(
           <<-BODY.strip_heredoc.chomp
             Awesome!
 
@@ -74,8 +74,8 @@ describe Gitlab::Email::ReplyParser, lib: true do
     end
 
     it "handles newlines" do
-      expect(test_parse_body(fixture_file("emails/newlines.eml"))).
-        to eq(
+      expect(test_parse_body(fixture_file("emails/newlines.eml")))
+        .to eq(
           <<-BODY.strip_heredoc.chomp
             This is my reply.
             It is my best reply.
@@ -85,8 +85,8 @@ describe Gitlab::Email::ReplyParser, lib: true do
     end
 
     it "handles inline reply" do
-      expect(test_parse_body(fixture_file("emails/inline_reply.eml"))).
-        to eq(
+      expect(test_parse_body(fixture_file("emails/inline_reply.eml")))
+        .to eq(
           <<-BODY.strip_heredoc.chomp
             >     techAPJ <https://meta.discourse.org/users/techapj>
             > November 28
@@ -132,8 +132,8 @@ describe Gitlab::Email::ReplyParser, lib: true do
     end
 
     it "properly renders email reply from gmail web client" do
-      expect(test_parse_body(fixture_file("emails/gmail_web.eml"))).
-        to eq(
+      expect(test_parse_body(fixture_file("emails/gmail_web.eml")))
+        .to eq(
           <<-BODY.strip_heredoc.chomp
             ### This is a reply from standard GMail in Google Chrome.
 
@@ -151,8 +151,8 @@ describe Gitlab::Email::ReplyParser, lib: true do
     end
 
     it "properly renders email reply from iOS default mail client" do
-      expect(test_parse_body(fixture_file("emails/ios_default.eml"))).
-        to eq(
+      expect(test_parse_body(fixture_file("emails/ios_default.eml")))
+        .to eq(
           <<-BODY.strip_heredoc.chomp
             ### this is a reply from iOS default mail
 
@@ -166,8 +166,8 @@ describe Gitlab::Email::ReplyParser, lib: true do
     end
 
     it "properly renders email reply from Android 5 gmail client" do
-      expect(test_parse_body(fixture_file("emails/android_gmail.eml"))).
-        to eq(
+      expect(test_parse_body(fixture_file("emails/android_gmail.eml")))
+        .to eq(
           <<-BODY.strip_heredoc.chomp
             ### this is a reply from Android 5 gmail
 
@@ -184,8 +184,8 @@ describe Gitlab::Email::ReplyParser, lib: true do
     end
 
     it "properly renders email reply from Windows 8.1 Metro default mail client" do
-      expect(test_parse_body(fixture_file("emails/windows_8_metro.eml"))).
-        to eq(
+      expect(test_parse_body(fixture_file("emails/windows_8_metro.eml")))
+        .to eq(
           <<-BODY.strip_heredoc.chomp
             ### reply from default mail client in Windows 8.1 Metro
 
@@ -208,5 +208,9 @@ describe Gitlab::Email::ReplyParser, lib: true do
     it "properly renders html-only email from MS Outlook" do
       expect(test_parse_body(fixture_file("emails/outlook_html.eml"))).to eq("Microsoft Outlook 2010")
     end
+
+    it "does not wrap links with no href in unnecessary brackets" do
+      expect(test_parse_body(fixture_file("emails/html_empty_link.eml"))).to eq("no brackets!")
+    end
   end
 end
diff --git a/spec/lib/gitlab/etag_caching/middleware_spec.rb b/spec/lib/gitlab/etag_caching/middleware_spec.rb
index 4acf4f047f15a477fcef4a336191257037a48872..4a54d641b4ea8c55c2f7136fbd204e24ba5cc8ef 100644
--- a/spec/lib/gitlab/etag_caching/middleware_spec.rb
+++ b/spec/lib/gitlab/etag_caching/middleware_spec.rb
@@ -108,8 +108,8 @@ describe Gitlab::EtagCaching::Middleware do
 
     context 'when polling is disabled' do
       before do
-        allow(Gitlab::PollingInterval).to receive(:polling_enabled?).
-          and_return(false)
+        allow(Gitlab::PollingInterval).to receive(:polling_enabled?)
+          .and_return(false)
       end
 
       it 'returns status code 429' do
diff --git a/spec/lib/gitlab/fake_application_settings_spec.rb b/spec/lib/gitlab/fake_application_settings_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b793176d84a45fe5a70453c930d26b4b62c5f3f5
--- /dev/null
+++ b/spec/lib/gitlab/fake_application_settings_spec.rb
@@ -0,0 +1,32 @@
+require 'spec_helper'
+
+describe Gitlab::FakeApplicationSettings do
+  let(:defaults) { { signin_enabled: false, foobar: 'asdf', signup_enabled: true, 'test?' => 123 } }
+
+  subject { described_class.new(defaults) }
+
+  it 'wraps OpenStruct variables properly' do
+    expect(subject.signin_enabled).to be_falsey
+    expect(subject.signup_enabled).to be_truthy
+    expect(subject.foobar).to eq('asdf')
+  end
+
+  it 'defines predicate methods' do
+    expect(subject.signin_enabled?).to be_falsey
+    expect(subject.signup_enabled?).to be_truthy
+  end
+
+  it 'predicate method changes when value is updated' do
+    subject.signin_enabled = true
+
+    expect(subject.signin_enabled?).to be_truthy
+  end
+
+  it 'does not define a predicate method' do
+    expect(subject.foobar?).to be_nil
+  end
+
+  it 'does not override an existing predicate method' do
+    expect(subject.test?).to eq(123)
+  end
+end
diff --git a/spec/lib/gitlab/file_detector_spec.rb b/spec/lib/gitlab/file_detector_spec.rb
index e5ba13bbaf84bec9fda75257e7b6525d7037cc3d..695fd6f8573ae1e5cc8ecc9366f2a80d959e89d9 100644
--- a/spec/lib/gitlab/file_detector_spec.rb
+++ b/spec/lib/gitlab/file_detector_spec.rb
@@ -3,13 +3,13 @@ require 'spec_helper'
 describe Gitlab::FileDetector do
   describe '.types_in_paths' do
     it 'returns the file types for the given paths' do
-      expect(described_class.types_in_paths(%w(README.md CHANGELOG VERSION VERSION))).
-        to eq(%i{readme changelog version})
+      expect(described_class.types_in_paths(%w(README.md CHANGELOG VERSION VERSION)))
+        .to eq(%i{readme changelog version})
     end
 
     it 'does not include unrecognized file paths' do
-      expect(described_class.types_in_paths(%w(README.md foo.txt))).
-        to eq(%i{readme})
+      expect(described_class.types_in_paths(%w(README.md foo.txt)))
+        .to eq(%i{readme})
     end
   end
 
diff --git a/spec/lib/gitlab/git/attributes_spec.rb b/spec/lib/gitlab/git/attributes_spec.rb
index 1cfd8db09a5dab03eceb85b56b6c31ace08ee796..b715fc3410a196a14c0204c2bf431ff1b2fb9fa6 100644
--- a/spec/lib/gitlab/git/attributes_spec.rb
+++ b/spec/lib/gitlab/git/attributes_spec.rb
@@ -14,13 +14,13 @@ describe Gitlab::Git::Attributes, seed_helper: true do
       end
 
       it 'returns a Hash containing multiple attributes' do
-        expect(subject.attributes('test.sh')).
-          to eq({ 'eol' => 'lf', 'gitlab-language' => 'shell' })
+        expect(subject.attributes('test.sh'))
+          .to eq({ 'eol' => 'lf', 'gitlab-language' => 'shell' })
       end
 
       it 'returns a Hash containing attributes for a file with multiple extensions' do
-        expect(subject.attributes('test.haml.html')).
-          to eq({ 'gitlab-language' => 'haml' })
+        expect(subject.attributes('test.haml.html'))
+          .to eq({ 'gitlab-language' => 'haml' })
       end
 
       it 'returns a Hash containing attributes for a file in a directory' do
@@ -28,8 +28,8 @@ describe Gitlab::Git::Attributes, seed_helper: true do
       end
 
       it 'returns a Hash containing attributes with query string parameters' do
-        expect(subject.attributes('foo.cgi')).
-          to eq({ 'key' => 'value?p1=v1&p2=v2' })
+        expect(subject.attributes('foo.cgi'))
+          .to eq({ 'key' => 'value?p1=v1&p2=v2' })
       end
 
       it 'returns a Hash containing the attributes for an absolute path' do
@@ -39,11 +39,11 @@ describe Gitlab::Git::Attributes, seed_helper: true do
       it 'returns a Hash containing the attributes when a pattern is defined using an absolute path' do
         # When a path is given without a leading slash it should still match
         # patterns defined with a leading slash.
-        expect(subject.attributes('foo.png')).
-          to eq({ 'gitlab-language' => 'png' })
+        expect(subject.attributes('foo.png'))
+          .to eq({ 'gitlab-language' => 'png' })
 
-        expect(subject.attributes('/foo.png')).
-          to eq({ 'gitlab-language' => 'png' })
+        expect(subject.attributes('/foo.png'))
+          .to eq({ 'gitlab-language' => 'png' })
       end
 
       it 'returns an empty Hash for a defined path without attributes' do
@@ -74,8 +74,8 @@ describe Gitlab::Git::Attributes, seed_helper: true do
     end
 
     it 'parses an entry that uses a tab to separate the pattern and attributes' do
-      expect(subject.patterns[File.join(path, '*.md')]).
-        to eq({ 'gitlab-language' => 'markdown' })
+      expect(subject.patterns[File.join(path, '*.md')])
+        .to eq({ 'gitlab-language' => 'markdown' })
     end
 
     it 'stores patterns in reverse order' do
@@ -91,9 +91,9 @@ describe Gitlab::Git::Attributes, seed_helper: true do
     end
 
     it 'does not parse anything when the attributes file does not exist' do
-      expect(File).to receive(:exist?).
-        with(File.join(path, 'info/attributes')).
-        and_return(false)
+      expect(File).to receive(:exist?)
+        .with(File.join(path, 'info/attributes'))
+        .and_return(false)
 
       expect(subject.patterns).to eq({})
     end
@@ -115,13 +115,13 @@ describe Gitlab::Git::Attributes, seed_helper: true do
     it 'parses multiple attributes' do
       input = 'boolean key=value -negated'
 
-      expect(subject.parse_attributes(input)).
-        to eq({ 'boolean' => true, 'key' => 'value', 'negated' => false })
+      expect(subject.parse_attributes(input))
+        .to eq({ 'boolean' => true, 'key' => 'value', 'negated' => false })
     end
 
     it 'parses attributes with query string parameters' do
-      expect(subject.parse_attributes('foo=bar?baz=1')).
-        to eq({ 'foo' => 'bar?baz=1' })
+      expect(subject.parse_attributes('foo=bar?baz=1'))
+        .to eq({ 'foo' => 'bar?baz=1' })
     end
   end
 
@@ -133,9 +133,9 @@ describe Gitlab::Git::Attributes, seed_helper: true do
     end
 
     it 'does not yield when the attributes file does not exist' do
-      expect(File).to receive(:exist?).
-        with(File.join(path, 'info/attributes')).
-        and_return(false)
+      expect(File).to receive(:exist?)
+        .with(File.join(path, 'info/attributes'))
+        .and_return(false)
 
       expect { |b| subject.each_line(&b) }.not_to yield_control
     end
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index e6a07a58d735e6ab0a826597febbcad121d888d9..58d3ee6b488a6074224ba1d7d281d204b64ece39 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -15,7 +15,7 @@ describe Gitlab::Git::Blob, seed_helper: true do
     end
   end
 
-  describe '.find' do
+  shared_examples 'finding blobs' do
     context 'file in subdir' do
       let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "files/ruby/popen.rb") }
 
@@ -92,15 +92,25 @@ describe Gitlab::Git::Blob, seed_helper: true do
       end
 
       it 'marks the blob as binary' do
-        expect(Gitlab::Git::Blob).to receive(:new).
-          with(hash_including(binary: true)).
-          and_call_original
+        expect(Gitlab::Git::Blob).to receive(:new)
+          .with(hash_including(binary: true))
+          .and_call_original
 
         expect(blob).to be_binary
       end
     end
   end
 
+  describe '.find' do
+    context 'when project_raw_show Gitaly feature is enabled' do
+      it_behaves_like 'finding blobs'
+    end
+
+    context 'when project_raw_show Gitaly feature is disabled', skip_gitaly_mock: true do
+      it_behaves_like 'finding blobs'
+    end
+  end
+
   describe '.raw' do
     let(:raw_blob) { Gitlab::Git::Blob.raw(repository, SeedRepo::RubyBlob::ID) }
     it { expect(raw_blob.id).to eq(SeedRepo::RubyBlob::ID) }
diff --git a/spec/lib/gitlab/git/branch_spec.rb b/spec/lib/gitlab/git/branch_spec.rb
index 9eac7660cd1430dcfc5640500ac89105ea9995c2..9dba4397e7953a17585e0a6348c9855889fd31dc 100644
--- a/spec/lib/gitlab/git/branch_spec.rb
+++ b/spec/lib/gitlab/git/branch_spec.rb
@@ -45,8 +45,8 @@ describe Gitlab::Git::Branch, seed_helper: true do
     let(:branch) { described_class.new(repository, 'foo', gitaly_branch) }
 
     it 'parses Gitaly::FindLocalBranchResponse correctly' do
-      expect(Gitlab::Git::Commit).to receive(:decorate).
-        with(hash_including(attributes)).and_call_original
+      expect(Gitlab::Git::Commit).to receive(:decorate)
+        .with(hash_including(attributes)).and_call_original
 
       expect(branch.dereferenced_target.message.encoding).to be(Encoding::UTF_8)
     end
diff --git a/spec/lib/gitlab/git/diff_collection_spec.rb b/spec/lib/gitlab/git/diff_collection_spec.rb
index a9a7bba2c054701e4f63910ac282616ff3ebde73..d20298fa1398cc1a740a9237caee6b419e7be341 100644
--- a/spec/lib/gitlab/git/diff_collection_spec.rb
+++ b/spec/lib/gitlab/git/diff_collection_spec.rb
@@ -325,8 +325,8 @@ describe Gitlab::Git::DiffCollection, seed_helper: true do
       end
 
       it 'yields Diff instances even when they are too large' do
-        expect { |b| collection.each(&b) }.
-          to yield_with_args(an_instance_of(Gitlab::Git::Diff))
+        expect { |b| collection.each(&b) }
+          .to yield_with_args(an_instance_of(Gitlab::Git::Diff))
       end
 
       it 'prunes diffs that are too large' do
@@ -348,8 +348,8 @@ describe Gitlab::Git::DiffCollection, seed_helper: true do
         let(:expanded) { true }
 
         it 'yields Diff instances even when they are quite big' do
-          expect { |b| subject.each(&b) }.
-            to yield_with_args(an_instance_of(Gitlab::Git::Diff))
+          expect { |b| subject.each(&b) }
+            .to yield_with_args(an_instance_of(Gitlab::Git::Diff))
         end
 
         it 'does not prune diffs' do
@@ -367,8 +367,8 @@ describe Gitlab::Git::DiffCollection, seed_helper: true do
         let(:expanded) { false }
 
         it 'yields Diff instances even when they are quite big' do
-          expect { |b| subject.each(&b) }.
-            to yield_with_args(an_instance_of(Gitlab::Git::Diff))
+          expect { |b| subject.each(&b) }
+            .to yield_with_args(an_instance_of(Gitlab::Git::Diff))
         end
 
         it 'prunes diffs that are quite big' do
@@ -454,8 +454,8 @@ describe Gitlab::Git::DiffCollection, seed_helper: true do
         let(:limits) { false }
 
         it 'yields Diff instances even when they are quite big' do
-          expect { |b| subject.each(&b) }.
-            to yield_with_args(an_instance_of(Gitlab::Git::Diff))
+          expect { |b| subject.each(&b) }
+            .to yield_with_args(an_instance_of(Gitlab::Git::Diff))
         end
 
         it 'does not prune diffs' do
diff --git a/spec/lib/gitlab/git/diff_spec.rb b/spec/lib/gitlab/git/diff_spec.rb
index da213f617cc92ab2dc502a3501e3fa8e203d0728..5627562abfb73c9a905a055e7f44062f9299788b 100644
--- a/spec/lib/gitlab/git/diff_spec.rb
+++ b/spec/lib/gitlab/git/diff_spec.rb
@@ -90,7 +90,7 @@ EOT
         let(:diff) { described_class.new(@rugged_diff) }
 
         it 'initializes the diff' do
-          expect(diff.to_hash).to eq(@raw_diff_hash.merge(too_large: nil))
+          expect(diff.to_hash).to eq(@raw_diff_hash)
         end
 
         it 'does not prune the diff' do
@@ -100,8 +100,8 @@ EOT
 
       context 'using a diff that is too large' do
         it 'prunes the diff' do
-          expect_any_instance_of(String).to receive(:bytesize).
-            and_return(1024 * 1024 * 1024)
+          expect_any_instance_of(String).to receive(:bytesize)
+            .and_return(1024 * 1024 * 1024)
 
           diff = described_class.new(@rugged_diff)
 
@@ -130,8 +130,8 @@ EOT
 
       context 'using a large binary diff' do
         it 'does not prune the diff' do
-          expect_any_instance_of(Rugged::Diff::Delta).to receive(:binary?).
-            and_return(true)
+          expect_any_instance_of(Rugged::Diff::Delta).to receive(:binary?)
+            .and_return(true)
 
           diff = described_class.new(@rugged_diff)
 
diff --git a/spec/lib/gitlab/git/gitmodules_parser_spec.rb b/spec/lib/gitlab/git/gitmodules_parser_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..143aa2218c9a4771d01d353d2273e02b6d1833ea
--- /dev/null
+++ b/spec/lib/gitlab/git/gitmodules_parser_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+describe Gitlab::Git::GitmodulesParser do
+  it 'should parse a .gitmodules file correctly' do
+    parser = described_class.new(<<-'GITMODULES'.strip_heredoc)
+      [submodule "vendor/libgit2"]
+         path = vendor/libgit2
+      [submodule "vendor/libgit2"]
+         url = https://github.com/nodegit/libgit2.git
+
+      # a comment
+      [submodule "moved"]
+          path = new/path
+          url = https://example.com/some/project
+      [submodule "bogus"]
+          url = https://example.com/another/project
+    GITMODULES
+
+    modules = parser.parse
+
+    expect(modules).to eq({
+                            'vendor/libgit2' => { 'name' => 'vendor/libgit2',
+                                                  'url' => 'https://github.com/nodegit/libgit2.git' },
+                            'new/path' => { 'name' => 'moved',
+                                            'url' => 'https://example.com/some/project' }
+                          })
+  end
+end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index eee4c9eab6da5abc9940dec0fd9c9cc9c186852d..703b0c2c202309cc33c1c4f5fff6497af8eb8478 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -41,14 +41,14 @@ describe Gitlab::Git::Repository, seed_helper: true do
       end
 
       it 'wraps GRPC not found' do
-        expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:default_branch_name).
-          and_raise(GRPC::NotFound)
+        expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:default_branch_name)
+          .and_raise(GRPC::NotFound)
         expect { repository.root_ref }.to raise_error(Gitlab::Git::Repository::NoRepository)
       end
 
       it 'wraps GRPC exceptions' do
-        expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:default_branch_name).
-          and_raise(GRPC::Unknown)
+        expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:default_branch_name)
+          .and_raise(GRPC::Unknown)
         expect { repository.root_ref }.to raise_error(Gitlab::Git::CommandError)
       end
     end
@@ -141,14 +141,14 @@ describe Gitlab::Git::Repository, seed_helper: true do
       end
 
       it 'wraps GRPC not found' do
-        expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:branch_names).
-          and_raise(GRPC::NotFound)
+        expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:branch_names)
+          .and_raise(GRPC::NotFound)
         expect { subject }.to raise_error(Gitlab::Git::Repository::NoRepository)
       end
 
       it 'wraps GRPC other exceptions' do
-        expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:branch_names).
-          and_raise(GRPC::Unknown)
+        expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:branch_names)
+          .and_raise(GRPC::Unknown)
         expect { subject }.to raise_error(Gitlab::Git::CommandError)
       end
     end
@@ -184,14 +184,14 @@ describe Gitlab::Git::Repository, seed_helper: true do
       end
 
       it 'wraps GRPC not found' do
-        expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:tag_names).
-          and_raise(GRPC::NotFound)
+        expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:tag_names)
+          .and_raise(GRPC::NotFound)
         expect { subject }.to raise_error(Gitlab::Git::Repository::NoRepository)
       end
 
       it 'wraps GRPC exceptions' do
-        expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:tag_names).
-          and_raise(GRPC::Unknown)
+        expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:tag_names)
+          .and_raise(GRPC::Unknown)
         expect { subject }.to raise_error(Gitlab::Git::CommandError)
       end
     end
@@ -358,7 +358,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
         expect(submodule).to eq([
           "six", {
             "id" => "409f37c4f05865e4fb208c771485f211a22c4c2d",
-            "path" => "six",
+            "name" => "six",
             "url" => "git://github.com/randx/six.git"
           }
         ])
@@ -366,14 +366,14 @@ describe Gitlab::Git::Repository, seed_helper: true do
 
       it 'should handle nested submodules correctly' do
         nested = submodules['nested/six']
-        expect(nested['path']).to eq('nested/six')
+        expect(nested['name']).to eq('nested/six')
         expect(nested['url']).to eq('git://github.com/randx/six.git')
         expect(nested['id']).to eq('24fb71c79fcabc63dfd8832b12ee3bf2bf06b196')
       end
 
       it 'should handle deeply nested submodules correctly' do
         nested = submodules['deeper/nested/six']
-        expect(nested['path']).to eq('deeper/nested/six')
+        expect(nested['name']).to eq('deeper/nested/six')
         expect(nested['url']).to eq('git://github.com/randx/six.git')
         expect(nested['id']).to eq('24fb71c79fcabc63dfd8832b12ee3bf2bf06b196')
       end
@@ -393,7 +393,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
         expect(submodules.first).to eq([
           "six", {
             "id" => "409f37c4f05865e4fb208c771485f211a22c4c2d",
-            "path" => "six",
+            "name" => "six",
             "url" => "git://github.com/randx/six.git"
           }
         ])
@@ -472,8 +472,8 @@ describe Gitlab::Git::Repository, seed_helper: true do
       end
 
       it "should move the tip of the master branch to the correct commit" do
-        new_tip = @normal_repo.rugged.references["refs/heads/master"].
-          target.oid
+        new_tip = @normal_repo.rugged.references["refs/heads/master"]
+          .target.oid
 
         expect(new_tip).to eq(reset_commit)
       end
@@ -1306,20 +1306,20 @@ describe Gitlab::Git::Repository, seed_helper: true do
       end
 
       it 'gets the branches from GitalyClient' do
-        expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:local_branches).
-          and_return([])
+        expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:local_branches)
+          .and_return([])
         @repo.local_branches
       end
 
       it 'wraps GRPC not found' do
-        expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:local_branches).
-          and_raise(GRPC::NotFound)
+        expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:local_branches)
+          .and_raise(GRPC::NotFound)
         expect { @repo.local_branches }.to raise_error(Gitlab::Git::Repository::NoRepository)
       end
 
       it 'wraps GRPC exceptions' do
-        expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:local_branches).
-          and_raise(GRPC::Unknown)
+        expect_any_instance_of(Gitlab::GitalyClient::Ref).to receive(:local_branches)
+          .and_raise(GRPC::Unknown)
         expect { @repo.local_branches }.to raise_error(Gitlab::Git::CommandError)
       end
     end
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index 3dcc20c48e8a1ad0297a95a45378111fc13d0326..9a86cfa66e4831c2116e4112c81230225fa12e3d 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -3,11 +3,12 @@ require 'spec_helper'
 describe Gitlab::GitAccess, lib: true do
   let(:pull_access_check) { access.check('git-upload-pack', '_any') }
   let(:push_access_check) { access.check('git-receive-pack', '_any') }
-  let(:access) { Gitlab::GitAccess.new(actor, project, protocol, authentication_abilities: authentication_abilities) }
+  let(:access) { Gitlab::GitAccess.new(actor, project, protocol, authentication_abilities: authentication_abilities, redirected_path: redirected_path) }
   let(:project) { create(:project, :repository) }
   let(:user) { create(:user) }
   let(:actor) { user }
   let(:protocol) { 'ssh' }
+  let(:redirected_path) { nil }
   let(:authentication_abilities) do
     [
       :read_project,
@@ -162,6 +163,46 @@ describe Gitlab::GitAccess, lib: true do
     end
   end
 
+  describe '#check_project_moved!' do
+    before do
+      project.team << [user, :master]
+    end
+
+    context 'when a redirect was not followed to find the project' do
+      context 'pull code' do
+        it { expect { pull_access_check }.not_to raise_error }
+      end
+
+      context 'push code' do
+        it { expect { push_access_check }.not_to raise_error }
+      end
+    end
+
+    context 'when a redirect was followed to find the project' do
+      let(:redirected_path) { 'some/other-path' }
+
+      context 'pull code' do
+        it { expect { pull_access_check }.to raise_not_found(/Project '#{redirected_path}' was moved to '#{project.full_path}'/) }
+        it { expect { pull_access_check }.to raise_not_found(/git remote set-url origin #{project.ssh_url_to_repo}/) }
+
+        context 'http protocol' do
+          let(:protocol) { 'http' }
+          it { expect { pull_access_check }.to raise_not_found(/git remote set-url origin #{project.http_url_to_repo}/) }
+        end
+      end
+
+      context 'push code' do
+        it { expect { push_access_check }.to raise_not_found(/Project '#{redirected_path}' was moved to '#{project.full_path}'/) }
+        it { expect { push_access_check }.to raise_not_found(/git remote set-url origin #{project.ssh_url_to_repo}/) }
+
+        context 'http protocol' do
+          let(:protocol) { 'http' }
+          it { expect { push_access_check }.to raise_not_found(/git remote set-url origin #{project.http_url_to_repo}/) }
+        end
+      end
+    end
+  end
+
   describe '#check_command_disabled!' do
     before do
       project.team << [user, :master]
diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb
index a1eb95750ba314b9c777a08803906dd449a5634a..797ec8cb23ebfa8771ffd0d38235c6af3edfd149 100644
--- a/spec/lib/gitlab/git_access_wiki_spec.rb
+++ b/spec/lib/gitlab/git_access_wiki_spec.rb
@@ -1,9 +1,10 @@
 require 'spec_helper'
 
 describe Gitlab::GitAccessWiki, lib: true do
-  let(:access) { Gitlab::GitAccessWiki.new(user, project, 'web', authentication_abilities: authentication_abilities) }
+  let(:access) { Gitlab::GitAccessWiki.new(user, project, 'web', authentication_abilities: authentication_abilities, redirected_path: redirected_path) }
   let(:project) { create(:project, :repository) }
   let(:user) { create(:user) }
+  let(:redirected_path) { nil }
   let(:authentication_abilities) do
     [
       :read_project,
diff --git a/spec/lib/gitlab/gitaly_client/commit_spec.rb b/spec/lib/gitlab/gitaly_client/commit_spec.rb
index cf1bc74779e170c8ff1bf7252efcb250042d2399..dff5b25c7129bb5d8b66698b76005e166091d3a2 100644
--- a/spec/lib/gitlab/gitaly_client/commit_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/commit_spec.rb
@@ -16,7 +16,7 @@ describe Gitlab::GitalyClient::Commit do
           right_commit_id: commit.id
         )
 
-        expect_any_instance_of(Gitaly::Diff::Stub).to receive(:commit_diff).with(request)
+        expect_any_instance_of(Gitaly::Diff::Stub).to receive(:commit_diff).with(request, kind_of(Hash))
 
         described_class.new(repository).diff_from_parent(commit)
       end
@@ -31,7 +31,7 @@ describe Gitlab::GitalyClient::Commit do
           right_commit_id: initial_commit.id
         )
 
-        expect_any_instance_of(Gitaly::Diff::Stub).to receive(:commit_diff).with(request)
+        expect_any_instance_of(Gitaly::Diff::Stub).to receive(:commit_diff).with(request, kind_of(Hash))
 
         described_class.new(repository).diff_from_parent(initial_commit)
       end
@@ -61,7 +61,7 @@ describe Gitlab::GitalyClient::Commit do
           right_commit_id: commit.id
         )
 
-        expect_any_instance_of(Gitaly::Diff::Stub).to receive(:commit_delta).with(request).and_return([])
+        expect_any_instance_of(Gitaly::Diff::Stub).to receive(:commit_delta).with(request, kind_of(Hash)).and_return([])
 
         described_class.new(repository).commit_deltas(commit)
       end
@@ -76,7 +76,7 @@ describe Gitlab::GitalyClient::Commit do
           right_commit_id: initial_commit.id
         )
 
-        expect_any_instance_of(Gitaly::Diff::Stub).to receive(:commit_delta).with(request).and_return([])
+        expect_any_instance_of(Gitaly::Diff::Stub).to receive(:commit_delta).with(request, kind_of(Hash)).and_return([])
 
         described_class.new(repository).commit_deltas(initial_commit)
       end
diff --git a/spec/lib/gitlab/gitaly_client/notifications_spec.rb b/spec/lib/gitlab/gitaly_client/notifications_spec.rb
index e5c9e06a15e95c003d8caeec90568d1c09763ca4..7404ffe0f06ccf799ba1eae155df01dab458eb1a 100644
--- a/spec/lib/gitlab/gitaly_client/notifications_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/notifications_spec.rb
@@ -8,8 +8,8 @@ describe Gitlab::GitalyClient::Notifications do
     subject { described_class.new(project.repository) }
 
     it 'sends a post_receive message' do
-      expect_any_instance_of(Gitaly::Notifications::Stub).
-        to receive(:post_receive).with(gitaly_request_with_path(storage_name, relative_path))
+      expect_any_instance_of(Gitaly::Notifications::Stub)
+        .to receive(:post_receive).with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
 
       subject.post_receive
     end
diff --git a/spec/lib/gitlab/gitaly_client/ref_spec.rb b/spec/lib/gitlab/gitaly_client/ref_spec.rb
index 2ea44ef74b060507f7089ad993a102972fd75f8c..42dba2ff874394320337035336a01266edcda2a7 100644
--- a/spec/lib/gitlab/gitaly_client/ref_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/ref_spec.rb
@@ -19,10 +19,10 @@ describe Gitlab::GitalyClient::Ref do
 
   describe '#branch_names' do
     it 'sends a find_all_branch_names message' do
-      expect_any_instance_of(Gitaly::Ref::Stub).
-        to receive(:find_all_branch_names).
-          with(gitaly_request_with_path(storage_name, relative_path)).
-          and_return([])
+      expect_any_instance_of(Gitaly::Ref::Stub)
+        .to receive(:find_all_branch_names)
+        .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
+        .and_return([])
 
       client.branch_names
     end
@@ -30,10 +30,10 @@ describe Gitlab::GitalyClient::Ref do
 
   describe '#tag_names' do
     it 'sends a find_all_tag_names message' do
-      expect_any_instance_of(Gitaly::Ref::Stub).
-        to receive(:find_all_tag_names).
-          with(gitaly_request_with_path(storage_name, relative_path)).
-          and_return([])
+      expect_any_instance_of(Gitaly::Ref::Stub)
+        .to receive(:find_all_tag_names)
+        .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
+        .and_return([])
 
       client.tag_names
     end
@@ -41,10 +41,10 @@ describe Gitlab::GitalyClient::Ref do
 
   describe '#default_branch_name' do
     it 'sends a find_default_branch_name message' do
-      expect_any_instance_of(Gitaly::Ref::Stub).
-        to receive(:find_default_branch_name).
-          with(gitaly_request_with_path(storage_name, relative_path)).
-        and_return(double(name: 'foo'))
+      expect_any_instance_of(Gitaly::Ref::Stub)
+        .to receive(:find_default_branch_name)
+        .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
+        .and_return(double(name: 'foo'))
 
       client.default_branch_name
     end
@@ -52,19 +52,19 @@ describe Gitlab::GitalyClient::Ref do
 
   describe '#local_branches' do
     it 'sends a find_local_branches message' do
-      expect_any_instance_of(Gitaly::Ref::Stub).
-        to receive(:find_local_branches).
-          with(gitaly_request_with_path(storage_name, relative_path)).
-          and_return([])
+      expect_any_instance_of(Gitaly::Ref::Stub)
+        .to receive(:find_local_branches)
+        .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
+        .and_return([])
 
       client.local_branches
     end
 
     it 'parses and sends the sort parameter' do
-      expect_any_instance_of(Gitaly::Ref::Stub).
-        to receive(:find_local_branches).
-          with(gitaly_request_with_params(sort_by: :UPDATED_DESC)).
-          and_return([])
+      expect_any_instance_of(Gitaly::Ref::Stub)
+        .to receive(:find_local_branches)
+        .with(gitaly_request_with_params(sort_by: :UPDATED_DESC), kind_of(Hash))
+        .and_return([])
 
       client.local_branches(sort_by: 'updated_desc')
     end
diff --git a/spec/lib/gitlab/gitlab_import/importer_spec.rb b/spec/lib/gitlab/gitlab_import/importer_spec.rb
index 9b499b593d32ced37b99ab25a72b56699ac2c1df..4f588da0a8350859e593608932eb2a576c802e78 100644
--- a/spec/lib/gitlab/gitlab_import/importer_spec.rb
+++ b/spec/lib/gitlab/gitlab_import/importer_spec.rb
@@ -45,8 +45,8 @@ describe Gitlab::GitlabImport::Importer, lib: true do
     def stub_request(path, body)
       url = "https://gitlab.com/api/v3/projects/asd%2Fvim/#{path}?page=1&per_page=100"
 
-      WebMock.stub_request(:get, url).
-        to_return(
+      WebMock.stub_request(:get, url)
+        .to_return(
           headers: { 'Content-Type' => 'application/json' },
           body: body
         )
diff --git a/spec/lib/gitlab/group_hierarchy_spec.rb b/spec/lib/gitlab/group_hierarchy_spec.rb
index 5d0ed1522b39e391dfbfa544b6be4655dd7c1798..08010c2d0e222f1e943aea149c62b2bafe2356d7 100644
--- a/spec/lib/gitlab/group_hierarchy_spec.rb
+++ b/spec/lib/gitlab/group_hierarchy_spec.rb
@@ -17,6 +17,12 @@ describe Gitlab::GroupHierarchy, :postgresql do
     it 'includes all of the ancestors' do
       expect(relation).to include(parent, child1)
     end
+
+    it 'uses ancestors_base #initialize argument' do
+      relation = described_class.new(Group.where(id: child2.id), Group.none).base_and_ancestors
+
+      expect(relation).to include(parent, child1, child2)
+    end
   end
 
   describe '#base_and_descendants' do
@@ -31,6 +37,12 @@ describe Gitlab::GroupHierarchy, :postgresql do
     it 'includes all the descendants' do
       expect(relation).to include(child1, child2)
     end
+
+    it 'uses descendants_base #initialize argument' do
+      relation = described_class.new(Group.none, Group.where(id: parent.id)).base_and_descendants
+
+      expect(relation).to include(parent, child1, child2)
+    end
   end
 
   describe '#all_groups' do
@@ -49,5 +61,17 @@ describe Gitlab::GroupHierarchy, :postgresql do
     it 'includes the descendants' do
       expect(relation).to include(child2)
     end
+
+    it 'uses ancestors_base #initialize argument for ancestors' do
+      relation = described_class.new(Group.where(id: child1.id), Group.where(id: Group.maximum(:id).succ)).all_groups
+
+      expect(relation).to include(parent)
+    end
+
+    it 'uses descendants_base #initialize argument for descendants' do
+      relation = described_class.new(Group.where(id: Group.maximum(:id).succ), Group.where(id: child1.id)).all_groups
+
+      expect(relation).to include(child2)
+    end
   end
 end
diff --git a/spec/lib/gitlab/highlight_spec.rb b/spec/lib/gitlab/highlight_spec.rb
index fdc5b484ef187eeffa3649382ed6b964a0d1131e..07687b470c5ba07ed4cf8159679736c5e3f45130 100644
--- a/spec/lib/gitlab/highlight_spec.rb
+++ b/spec/lib/gitlab/highlight_spec.rb
@@ -51,8 +51,8 @@ describe Gitlab::Highlight, lib: true do
     end
 
     it 'links dependencies via DependencyLinker' do
-      expect(Gitlab::DependencyLinker).to receive(:link).
-        with('file.name', 'Contents', anything).and_call_original
+      expect(Gitlab::DependencyLinker).to receive(:link)
+        .with('file.name', 'Contents', anything).and_call_original
 
       described_class.highlight('file.name', 'Contents')
     end
diff --git a/spec/lib/gitlab/identifier_spec.rb b/spec/lib/gitlab/identifier_spec.rb
index bb758a8a2029edd712c811e2ae5830742517eb95..29912da2e252038fcbd2bd33e2f19c4b8e38f631 100644
--- a/spec/lib/gitlab/identifier_spec.rb
+++ b/spec/lib/gitlab/identifier_spec.rb
@@ -12,8 +12,8 @@ describe Gitlab::Identifier do
   describe '#identify' do
     context 'without an identifier' do
       it 'identifies the user using a commit' do
-        expect(identifier).to receive(:identify_using_commit).
-          with(project, '123')
+        expect(identifier).to receive(:identify_using_commit)
+          .with(project, '123')
 
         identifier.identify('', project, '123')
       end
@@ -21,8 +21,8 @@ describe Gitlab::Identifier do
 
     context 'with a user identifier' do
       it 'identifies the user using a user ID' do
-        expect(identifier).to receive(:identify_using_user).
-          with("user-#{user.id}")
+        expect(identifier).to receive(:identify_using_user)
+          .with("user-#{user.id}")
 
         identifier.identify("user-#{user.id}", project, '123')
       end
@@ -30,8 +30,8 @@ describe Gitlab::Identifier do
 
     context 'with an SSH key identifier' do
       it 'identifies the user using an SSH key ID' do
-        expect(identifier).to receive(:identify_using_ssh_key).
-          with("key-#{key.id}")
+        expect(identifier).to receive(:identify_using_ssh_key)
+          .with("key-#{key.id}")
 
         identifier.identify("key-#{key.id}", project, '123')
       end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index 412eb33b35b0b1a3733dec067f118c75d190e26d..a5f09f1856e170e8df341cbb1146ef607c0f2e0d 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -88,6 +88,9 @@ merge_requests:
 - head_pipeline
 merge_request_diff:
 - merge_request
+- merge_request_diff_files
+merge_request_diff_files:
+- merge_request_diff
 pipelines:
 - project
 - user
diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json
index e3599d6fe594d5e7841c37e03f576ecf8f2c9fdf..98c117b4cd8323a8980ded7cf2df9fee8e8e0e7d 100644
--- a/spec/lib/gitlab/import_export/project.json
+++ b/spec/lib/gitlab/import_export/project.json
@@ -2821,9 +2821,11 @@
             "committer_email": "dmitriy.zaporozhets@gmail.com"
           }
         ],
-        "utf8_st_diffs": [
+        "merge_request_diff_files": [
           {
-            "diff": "Binary files a/.DS_Store and /dev/null differ\n",
+            "merge_request_diff_id": 27,
+            "relative_order": 0,
+            "utf8_diff": "Binary files a/.DS_Store and /dev/null differ\n",
             "new_path": ".DS_Store",
             "old_path": ".DS_Store",
             "a_mode": "100644",
@@ -2834,7 +2836,9 @@
             "too_large": false
           },
           {
-            "diff": "--- a/.gitignore\n+++ b/.gitignore\n@@ -17,3 +17,4 @@ rerun.txt\n pickle-email-*.html\n .project\n config/initializers/secret_token.rb\n+.DS_Store\n",
+            "merge_request_diff_id": 27,
+            "relative_order": 1,
+            "utf8_diff": "--- a/.gitignore\n+++ b/.gitignore\n@@ -17,3 +17,4 @@ rerun.txt\n pickle-email-*.html\n .project\n config/initializers/secret_token.rb\n+.DS_Store\n",
             "new_path": ".gitignore",
             "old_path": ".gitignore",
             "a_mode": "100644",
@@ -2845,7 +2849,9 @@
             "too_large": false
           },
           {
-            "diff": "--- a/.gitmodules\n+++ b/.gitmodules\n@@ -1,3 +1,9 @@\n [submodule \"six\"]\n \tpath = six\n \turl = git://github.com/randx/six.git\n+[submodule \"gitlab-shell\"]\n+\tpath = gitlab-shell\n+\turl = https://github.com/gitlabhq/gitlab-shell.git\n+[submodule \"gitlab-grack\"]\n+\tpath = gitlab-grack\n+\turl = https://gitlab.com/gitlab-org/gitlab-grack.git\n",
+            "merge_request_diff_id": 27,
+            "relative_order": 2,
+            "utf8_diff": "--- a/.gitmodules\n+++ b/.gitmodules\n@@ -1,3 +1,9 @@\n [submodule \"six\"]\n \tpath = six\n \turl = git://github.com/randx/six.git\n+[submodule \"gitlab-shell\"]\n+\tpath = gitlab-shell\n+\turl = https://github.com/gitlabhq/gitlab-shell.git\n+[submodule \"gitlab-grack\"]\n+\tpath = gitlab-grack\n+\turl = https://gitlab.com/gitlab-org/gitlab-grack.git\n",
             "new_path": ".gitmodules",
             "old_path": ".gitmodules",
             "a_mode": "100644",
@@ -2856,7 +2862,9 @@
             "too_large": false
           },
           {
-            "diff": "Binary files a/files/.DS_Store and /dev/null differ\n",
+            "merge_request_diff_id": 27,
+            "relative_order": 3,
+            "utf8_diff": "Binary files a/files/.DS_Store and /dev/null differ\n",
             "new_path": "files/.DS_Store",
             "old_path": "files/.DS_Store",
             "a_mode": "100644",
@@ -2867,7 +2875,9 @@
             "too_large": false
           },
           {
-            "diff": "--- /dev/null\n+++ b/files/ruby/feature.rb\n@@ -0,0 +1,4 @@\n+# This file was changed in feature branch\n+# We put different code here to make merge conflict\n+class Conflict\n+end\n",
+            "merge_request_diff_id": 27,
+            "relative_order": 4,
+            "utf8_diff": "--- /dev/null\n+++ b/files/ruby/feature.rb\n@@ -0,0 +1,4 @@\n+# This file was changed in feature branch\n+# We put different code here to make merge conflict\n+class Conflict\n+end\n",
             "new_path": "files/ruby/feature.rb",
             "old_path": "files/ruby/feature.rb",
             "a_mode": "0",
@@ -2878,7 +2888,9 @@
             "too_large": false
           },
           {
-            "diff": "--- a/files/ruby/popen.rb\n+++ b/files/ruby/popen.rb\n@@ -6,12 +6,18 @@ module Popen\n \n   def popen(cmd, path=nil)\n     unless cmd.is_a?(Array)\n-      raise \"System commands must be given as an array of strings\"\n+      raise RuntimeError, \"System commands must be given as an array of strings\"\n     end\n \n     path ||= Dir.pwd\n-    vars = { \"PWD\" =\u003e path }\n-    options = { chdir: path }\n+\n+    vars = {\n+      \"PWD\" =\u003e path\n+    }\n+\n+    options = {\n+      chdir: path\n+    }\n \n     unless File.directory?(path)\n       FileUtils.mkdir_p(path)\n@@ -19,6 +25,7 @@ module Popen\n \n     @cmd_output = \"\"\n     @cmd_status = 0\n+\n     Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|\n       @cmd_output \u003c\u003c stdout.read\n       @cmd_output \u003c\u003c stderr.read\n",
+            "merge_request_diff_id": 27,
+            "relative_order": 5,
+            "utf8_diff": "--- a/files/ruby/popen.rb\n+++ b/files/ruby/popen.rb\n@@ -6,12 +6,18 @@ module Popen\n \n   def popen(cmd, path=nil)\n     unless cmd.is_a?(Array)\n-      raise \"System commands must be given as an array of strings\"\n+      raise RuntimeError, \"System commands must be given as an array of strings\"\n     end\n \n     path ||= Dir.pwd\n-    vars = { \"PWD\" =\u003e path }\n-    options = { chdir: path }\n+\n+    vars = {\n+      \"PWD\" =\u003e path\n+    }\n+\n+    options = {\n+      chdir: path\n+    }\n \n     unless File.directory?(path)\n       FileUtils.mkdir_p(path)\n@@ -19,6 +25,7 @@ module Popen\n \n     @cmd_output = \"\"\n     @cmd_status = 0\n+\n     Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|\n       @cmd_output \u003c\u003c stdout.read\n       @cmd_output \u003c\u003c stderr.read\n",
             "new_path": "files/ruby/popen.rb",
             "old_path": "files/ruby/popen.rb",
             "a_mode": "100644",
@@ -2889,7 +2901,9 @@
             "too_large": false
           },
           {
-            "diff": "--- a/files/ruby/regex.rb\n+++ b/files/ruby/regex.rb\n@@ -19,14 +19,12 @@ module Gitlab\n     end\n \n     def archive_formats_regex\n-      #|zip|tar|    tar.gz    |         tar.bz2         |\n-      /(zip|tar|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n+      /(zip|tar|7z|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n     end\n \n     def git_reference_regex\n       # Valid git ref regex, see:\n       # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html\n-\n       %r{\n         (?!\n            (?# doesn't begins with)\n",
+            "merge_request_diff_id": 27,
+            "relative_order": 6,
+            "utf8_diff": "--- a/files/ruby/regex.rb\n+++ b/files/ruby/regex.rb\n@@ -19,14 +19,12 @@ module Gitlab\n     end\n \n     def archive_formats_regex\n-      #|zip|tar|    tar.gz    |         tar.bz2         |\n-      /(zip|tar|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n+      /(zip|tar|7z|tar\\.gz|tgz|gz|tar\\.bz2|tbz|tbz2|tb2|bz2)/\n     end\n \n     def git_reference_regex\n       # Valid git ref regex, see:\n       # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html\n-\n       %r{\n         (?!\n            (?# doesn't begins with)\n",
             "new_path": "files/ruby/regex.rb",
             "old_path": "files/ruby/regex.rb",
             "a_mode": "100644",
@@ -2900,7 +2914,9 @@
             "too_large": false
           },
           {
-            "diff": "--- /dev/null\n+++ b/gitlab-grack\n@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n",
+            "merge_request_diff_id": 27,
+            "relative_order": 7,
+            "utf8_diff": "--- /dev/null\n+++ b/gitlab-grack\n@@ -0,0 +1 @@\n+Subproject commit 645f6c4c82fd3f5e06f67134450a570b795e55a6\n",
             "new_path": "gitlab-grack",
             "old_path": "gitlab-grack",
             "a_mode": "0",
@@ -2911,7 +2927,9 @@
             "too_large": false
           },
           {
-            "diff": "--- /dev/null\n+++ b/gitlab-shell\n@@ -0,0 +1 @@\n+Subproject commit 79bceae69cb5750d6567b223597999bfa91cb3b9\n",
+            "merge_request_diff_id": 27,
+            "relative_order": 8,
+            "utf8_diff": "--- /dev/null\n+++ b/gitlab-shell\n@@ -0,0 +1 @@\n+Subproject commit 79bceae69cb5750d6567b223597999bfa91cb3b9\n",
             "new_path": "gitlab-shell",
             "old_path": "gitlab-shell",
             "a_mode": "0",
diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
index 14338515892f3af832d690a20f8f79faca4614bd..c11b15a811b962ce0ccd553ab4048a2140bc9b41 100644
--- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb
@@ -86,8 +86,13 @@ describe Gitlab::ImportExport::ProjectTreeRestorer, services: true do
 
       it 'has the correct data for merge request st_diffs' do
         # makes sure we are renaming the custom method +utf8_st_diffs+ into +st_diffs+
+        # one MergeRequestDiff uses the new format, where st_diffs is expected to be nil
 
-        expect(MergeRequestDiff.where.not(st_diffs: nil).count).to eq(9)
+        expect(MergeRequestDiff.where.not(st_diffs: nil).count).to eq(8)
+      end
+
+      it 'has the correct data for merge request diff files' do
+        expect(MergeRequestDiffFile.where.not(diff: nil).count).to eq(9)
       end
 
       it 'has the correct time for merge request st_commits' do
diff --git a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
index 5aeb29b7fecb72676fa1d4128e38b2208f4bba29..e52f79513f16672fa6e7444e146fd54c2b8c8d0c 100644
--- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
@@ -83,6 +83,10 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
         expect(saved_project_json['merge_requests'].first['merge_request_diff']['utf8_st_diffs']).not_to be_nil
       end
 
+      it 'has merge request diff files' do
+        expect(saved_project_json['merge_requests'].first['merge_request_diff']['merge_request_diff_files']).not_to be_empty
+      end
+
       it 'has merge requests comments' do
         expect(saved_project_json['merge_requests'].first['notes']).not_to be_empty
       end
@@ -145,6 +149,12 @@ describe Gitlab::ImportExport::ProjectTreeSaver, services: true do
         expect(project_tree_saver.save).to be true
       end
 
+      it 'does not complain about non UTF-8 characters in MR diff files' do
+        ActiveRecord::Base.connection.execute("UPDATE merge_request_diff_files SET diff = '---\n- :diff: !binary |-\n    LS0tIC9kZXYvbnVsbAorKysgYi9pbWFnZXMvbnVjb3IucGRmCkBAIC0wLDAg\n    KzEsMTY3OSBAQAorJVBERi0xLjUNJeLjz9MNCisxIDAgb2JqDTw8L01ldGFk\n    YXR'")
+
+        expect(project_tree_saver.save).to be true
+      end
+
       context 'group members' do
         let(:user2) { create(:user, email: 'group@member.com') }
         let(:member_emails) do
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 50ff6ecc1e08b19d3cd2010a9579e0a5fb81cc09..fadd3ad133011fbf470ef2f9010360ef2b59dd6c 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -172,6 +172,17 @@ MergeRequestDiff:
 - real_size
 - head_commit_sha
 - start_commit_sha
+MergeRequestDiffFile:
+- merge_request_diff_id
+- relative_order
+- new_file
+- renamed_file
+- deleted_file
+- new_path
+- old_path
+- a_mode
+- b_mode
+- too_large
 Ci::Pipeline:
 - id
 - project_id
diff --git a/spec/lib/gitlab/job_waiter_spec.rb b/spec/lib/gitlab/job_waiter_spec.rb
index 780f5b1f8d7a4abaa49c42ac33f20ee5125e138e..6186cec268993200b7cd0b4334fbb503fd530a69 100644
--- a/spec/lib/gitlab/job_waiter_spec.rb
+++ b/spec/lib/gitlab/job_waiter_spec.rb
@@ -4,8 +4,8 @@ describe Gitlab::JobWaiter do
   describe '#wait' do
     let(:waiter) { described_class.new(%w(a)) }
     it 'returns when all jobs have been completed' do
-      expect(Gitlab::SidekiqStatus).to receive(:all_completed?).with(%w(a)).
-        and_return(true)
+      expect(Gitlab::SidekiqStatus).to receive(:all_completed?).with(%w(a))
+        .and_return(true)
 
       expect(waiter).not_to receive(:sleep)
 
@@ -13,9 +13,9 @@ describe Gitlab::JobWaiter do
     end
 
     it 'sleeps between checking the job statuses' do
-      expect(Gitlab::SidekiqStatus).to receive(:all_completed?).
-        with(%w(a)).
-        and_return(false, true)
+      expect(Gitlab::SidekiqStatus).to receive(:all_completed?)
+        .with(%w(a))
+        .and_return(false, true)
 
       expect(waiter).to receive(:sleep).with(described_class::INTERVAL)
 
diff --git a/spec/lib/gitlab/ldap/authentication_spec.rb b/spec/lib/gitlab/ldap/authentication_spec.rb
index b8f3290e84c9cd4013f25900e91130f2de31b52b..f689b47fec4c2c3f9bb91bc5085d4a2f589d1aaf 100644
--- a/spec/lib/gitlab/ldap/authentication_spec.rb
+++ b/spec/lib/gitlab/ldap/authentication_spec.rb
@@ -16,8 +16,8 @@ describe Gitlab::LDAP::Authentication, lib: true do
 
       # try only to fake the LDAP call
       adapter = double('adapter', dn: dn).as_null_object
-      allow_any_instance_of(described_class).
-        to receive(:adapter).and_return(adapter)
+      allow_any_instance_of(described_class)
+        .to receive(:adapter).and_return(adapter)
 
       expect(described_class.login(login, password)).to be_truthy
     end
@@ -25,8 +25,8 @@ describe Gitlab::LDAP::Authentication, lib: true do
     it "is false if the user does not exist" do
       # try only to fake the LDAP call
       adapter = double('adapter', dn: dn).as_null_object
-      allow_any_instance_of(described_class).
-        to receive(:adapter).and_return(adapter)
+      allow_any_instance_of(described_class)
+        .to receive(:adapter).and_return(adapter)
 
       expect(described_class.login(login, password)).to be_falsey
     end
@@ -36,8 +36,8 @@ describe Gitlab::LDAP::Authentication, lib: true do
 
       # try only to fake the LDAP call
       adapter = double('adapter', bind_as: nil).as_null_object
-      allow_any_instance_of(described_class).
-        to receive(:adapter).and_return(adapter)
+      allow_any_instance_of(described_class)
+        .to receive(:adapter).and_return(adapter)
 
       expect(described_class.login(login, password)).to be_falsey
     end
diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb
index f0a1dd22fee775c2ed18be5fba5e8b78b34df97c..b796d8bf076036bde5b8fd72c445054cde3bdd62 100644
--- a/spec/lib/gitlab/ldap/user_spec.rb
+++ b/spec/lib/gitlab/ldap/user_spec.rb
@@ -167,8 +167,8 @@ describe Gitlab::LDAP::User, lib: true do
 
   describe 'blocking' do
     def configure_block(value)
-      allow_any_instance_of(Gitlab::LDAP::Config).
-        to receive(:block_auto_created_users).and_return(value)
+      allow_any_instance_of(Gitlab::LDAP::Config)
+        .to receive(:block_auto_created_users).and_return(value)
     end
 
     context 'signup' do
diff --git a/spec/lib/gitlab/metrics/instrumentation_spec.rb b/spec/lib/gitlab/metrics/instrumentation_spec.rb
index a986cb520fb0c30d032caaa8c5cb13412d8aaca0..4b19ee19103661583725a445f0913952807e2002 100644
--- a/spec/lib/gitlab/metrics/instrumentation_spec.rb
+++ b/spec/lib/gitlab/metrics/instrumentation_spec.rb
@@ -78,11 +78,11 @@ describe Gitlab::Metrics::Instrumentation do
       end
 
       it 'tracks the call duration upon calling the method' do
-        allow(Gitlab::Metrics).to receive(:method_call_threshold).
-          and_return(0)
+        allow(Gitlab::Metrics).to receive(:method_call_threshold)
+          .and_return(0)
 
-        allow(described_class).to receive(:transaction).
-          and_return(transaction)
+        allow(described_class).to receive(:transaction)
+          .and_return(transaction)
 
         expect_any_instance_of(Gitlab::Metrics::MethodCall).to receive(:measure)
 
@@ -90,8 +90,8 @@ describe Gitlab::Metrics::Instrumentation do
       end
 
       it 'does not track method calls below a given duration threshold' do
-        allow(Gitlab::Metrics).to receive(:method_call_threshold).
-          and_return(100)
+        allow(Gitlab::Metrics).to receive(:method_call_threshold)
+          .and_return(100)
 
         expect(transaction).not_to receive(:add_metric)
 
@@ -137,8 +137,8 @@ describe Gitlab::Metrics::Instrumentation do
       before do
         allow(Gitlab::Metrics).to receive(:enabled?).and_return(true)
 
-        described_class.
-          instrument_instance_method(@dummy, :bar)
+        described_class
+          .instrument_instance_method(@dummy, :bar)
       end
 
       it 'instruments instances of the Class' do
@@ -156,11 +156,11 @@ describe Gitlab::Metrics::Instrumentation do
       end
 
       it 'tracks the call duration upon calling the method' do
-        allow(Gitlab::Metrics).to receive(:method_call_threshold).
-          and_return(0)
+        allow(Gitlab::Metrics).to receive(:method_call_threshold)
+          .and_return(0)
 
-        allow(described_class).to receive(:transaction).
-          and_return(transaction)
+        allow(described_class).to receive(:transaction)
+          .and_return(transaction)
 
         expect_any_instance_of(Gitlab::Metrics::MethodCall).to receive(:measure)
 
@@ -168,8 +168,8 @@ describe Gitlab::Metrics::Instrumentation do
       end
 
       it 'does not track method calls below a given duration threshold' do
-        allow(Gitlab::Metrics).to receive(:method_call_threshold).
-          and_return(100)
+        allow(Gitlab::Metrics).to receive(:method_call_threshold)
+          .and_return(100)
 
         expect(transaction).not_to receive(:add_metric)
 
@@ -183,8 +183,8 @@ describe Gitlab::Metrics::Instrumentation do
       end
 
       it 'does not instrument the method' do
-        described_class.
-          instrument_instance_method(@dummy, :bar)
+        described_class
+          .instrument_instance_method(@dummy, :bar)
 
         expect(described_class.instrumented?(@dummy)).to eq(false)
       end
diff --git a/spec/lib/gitlab/metrics/rack_middleware_spec.rb b/spec/lib/gitlab/metrics/rack_middleware_spec.rb
index fb470ea75684f567dd05e86106e7742f44272207..ec415f2bd8598c4418d1f8a39108a16109b4719a 100644
--- a/spec/lib/gitlab/metrics/rack_middleware_spec.rb
+++ b/spec/lib/gitlab/metrics/rack_middleware_spec.rb
@@ -26,8 +26,8 @@ describe Gitlab::Metrics::RackMiddleware do
 
       allow(app).to receive(:call).with(env)
 
-      expect(middleware).to receive(:tag_controller).
-        with(an_instance_of(Gitlab::Metrics::Transaction), env)
+      expect(middleware).to receive(:tag_controller)
+        .with(an_instance_of(Gitlab::Metrics::Transaction), env)
 
       middleware.call(env)
     end
@@ -40,8 +40,8 @@ describe Gitlab::Metrics::RackMiddleware do
 
       allow(app).to receive(:call).with(env)
 
-      expect(middleware).to receive(:tag_endpoint).
-        with(an_instance_of(Gitlab::Metrics::Transaction), env)
+      expect(middleware).to receive(:tag_endpoint)
+        .with(an_instance_of(Gitlab::Metrics::Transaction), env)
 
       middleware.call(env)
     end
@@ -49,8 +49,8 @@ describe Gitlab::Metrics::RackMiddleware do
     it 'tracks any raised exceptions' do
       expect(app).to receive(:call).with(env).and_raise(RuntimeError)
 
-      expect_any_instance_of(Gitlab::Metrics::Transaction).
-        to receive(:add_event).with(:rails_exception)
+      expect_any_instance_of(Gitlab::Metrics::Transaction)
+        .to receive(:add_event).with(:rails_exception)
 
       expect { middleware.call(env) }.to raise_error(RuntimeError)
     end
diff --git a/spec/lib/gitlab/metrics/sampler_spec.rb b/spec/lib/gitlab/metrics/sampler_spec.rb
index 1ab923b58cf6881b901a5c0445b1231ab00648b9..d07ce6f81af2450eb6234bd7a65af5a75062991d 100644
--- a/spec/lib/gitlab/metrics/sampler_spec.rb
+++ b/spec/lib/gitlab/metrics/sampler_spec.rb
@@ -38,8 +38,8 @@ describe Gitlab::Metrics::Sampler do
 
   describe '#flush' do
     it 'schedules the metrics using Sidekiq' do
-      expect(Gitlab::Metrics).to receive(:submit_metrics).
-        with([an_instance_of(Hash)])
+      expect(Gitlab::Metrics).to receive(:submit_metrics)
+        .with([an_instance_of(Hash)])
 
       sampler.sample_memory_usage
       sampler.flush
@@ -48,12 +48,12 @@ describe Gitlab::Metrics::Sampler do
 
   describe '#sample_memory_usage' do
     it 'adds a metric containing the memory usage' do
-      expect(Gitlab::Metrics::System).to receive(:memory_usage).
-        and_return(9000)
+      expect(Gitlab::Metrics::System).to receive(:memory_usage)
+        .and_return(9000)
 
-      expect(sampler).to receive(:add_metric).
-        with(/memory_usage/, value: 9000).
-        and_call_original
+      expect(sampler).to receive(:add_metric)
+        .with(/memory_usage/, value: 9000)
+        .and_call_original
 
       sampler.sample_memory_usage
     end
@@ -61,12 +61,12 @@ describe Gitlab::Metrics::Sampler do
 
   describe '#sample_file_descriptors' do
     it 'adds a metric containing the amount of open file descriptors' do
-      expect(Gitlab::Metrics::System).to receive(:file_descriptor_count).
-        and_return(4)
+      expect(Gitlab::Metrics::System).to receive(:file_descriptor_count)
+        .and_return(4)
 
-      expect(sampler).to receive(:add_metric).
-        with(/file_descriptors/, value: 4).
-        and_call_original
+      expect(sampler).to receive(:add_metric)
+        .with(/file_descriptors/, value: 4)
+        .and_call_original
 
       sampler.sample_file_descriptors
     end
@@ -75,10 +75,10 @@ describe Gitlab::Metrics::Sampler do
   if Gitlab::Metrics.mri?
     describe '#sample_objects' do
       it 'adds a metric containing the amount of allocated objects' do
-        expect(sampler).to receive(:add_metric).
-          with(/object_counts/, an_instance_of(Hash), an_instance_of(Hash)).
-          at_least(:once).
-          and_call_original
+        expect(sampler).to receive(:add_metric)
+          .with(/object_counts/, an_instance_of(Hash), an_instance_of(Hash))
+          .at_least(:once)
+          .and_call_original
 
         sampler.sample_objects
       end
@@ -86,8 +86,8 @@ describe Gitlab::Metrics::Sampler do
       it 'ignores classes without a name' do
         expect(Allocations).to receive(:to_hash).and_return({ Class.new => 4 })
 
-        expect(sampler).not_to receive(:add_metric).
-          with('object_counts', an_instance_of(Hash), type: nil)
+        expect(sampler).not_to receive(:add_metric)
+          .with('object_counts', an_instance_of(Hash), type: nil)
 
         sampler.sample_objects
       end
@@ -98,9 +98,9 @@ describe Gitlab::Metrics::Sampler do
     it 'adds a metric containing garbage collection statistics' do
       expect(GC::Profiler).to receive(:total_time).and_return(0.24)
 
-      expect(sampler).to receive(:add_metric).
-        with(/gc_statistics/, an_instance_of(Hash)).
-        and_call_original
+      expect(sampler).to receive(:add_metric)
+        .with(/gc_statistics/, an_instance_of(Hash))
+        .and_call_original
 
       sampler.sample_gc
     end
@@ -110,9 +110,9 @@ describe Gitlab::Metrics::Sampler do
     it 'prefixes the series name for a Rails process' do
       expect(sampler).to receive(:sidekiq?).and_return(false)
 
-      expect(Gitlab::Metrics::Metric).to receive(:new).
-        with('rails_cats', { value: 10 }, {}).
-        and_call_original
+      expect(Gitlab::Metrics::Metric).to receive(:new)
+        .with('rails_cats', { value: 10 }, {})
+        .and_call_original
 
       sampler.add_metric('cats', value: 10)
     end
@@ -120,9 +120,9 @@ describe Gitlab::Metrics::Sampler do
     it 'prefixes the series name for a Sidekiq process' do
       expect(sampler).to receive(:sidekiq?).and_return(true)
 
-      expect(Gitlab::Metrics::Metric).to receive(:new).
-        with('sidekiq_cats', { value: 10 }, {}).
-        and_call_original
+      expect(Gitlab::Metrics::Metric).to receive(:new)
+        .with('sidekiq_cats', { value: 10 }, {})
+        .and_call_original
 
       sampler.add_metric('cats', value: 10)
     end
diff --git a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb
index acaba785606e13e9b43348a3fa4247eb3ac56d91..b576d7173f5a040b5a0fdf6d2364ea96f7bbddb7 100644
--- a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb
+++ b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb
@@ -8,12 +8,12 @@ describe Gitlab::Metrics::SidekiqMiddleware do
     it 'tracks the transaction' do
       worker = double(:worker, class: double(:class, name: 'TestWorker'))
 
-      expect(Gitlab::Metrics::Transaction).to receive(:new).
-        with('TestWorker#perform').
-        and_call_original
+      expect(Gitlab::Metrics::Transaction).to receive(:new)
+        .with('TestWorker#perform')
+        .and_call_original
 
-      expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:set).
-        with(:sidekiq_queue_duration, instance_of(Float))
+      expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:set)
+        .with(:sidekiq_queue_duration, instance_of(Float))
 
       expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:finish)
 
@@ -23,12 +23,12 @@ describe Gitlab::Metrics::SidekiqMiddleware do
     it 'tracks the transaction (for messages without `enqueued_at`)' do
       worker = double(:worker, class: double(:class, name: 'TestWorker'))
 
-      expect(Gitlab::Metrics::Transaction).to receive(:new).
-        with('TestWorker#perform').
-        and_call_original
+      expect(Gitlab::Metrics::Transaction).to receive(:new)
+        .with('TestWorker#perform')
+        .and_call_original
 
-      expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:set).
-        with(:sidekiq_queue_duration, instance_of(Float))
+      expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:set)
+        .with(:sidekiq_queue_duration, instance_of(Float))
 
       expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:finish)
 
@@ -38,17 +38,17 @@ describe Gitlab::Metrics::SidekiqMiddleware do
     it 'tracks any raised exceptions' do
       worker = double(:worker, class: double(:class, name: 'TestWorker'))
 
-      expect_any_instance_of(Gitlab::Metrics::Transaction).
-        to receive(:run).and_raise(RuntimeError)
+      expect_any_instance_of(Gitlab::Metrics::Transaction)
+        .to receive(:run).and_raise(RuntimeError)
 
-      expect_any_instance_of(Gitlab::Metrics::Transaction).
-        to receive(:add_event).with(:sidekiq_exception)
+      expect_any_instance_of(Gitlab::Metrics::Transaction)
+        .to receive(:add_event).with(:sidekiq_exception)
 
-      expect_any_instance_of(Gitlab::Metrics::Transaction).
-        to receive(:finish)
+      expect_any_instance_of(Gitlab::Metrics::Transaction)
+        .to receive(:finish)
 
-      expect { middleware.call(worker, message, :test) }.
-        to raise_error(RuntimeError)
+      expect { middleware.call(worker, message, :test) }
+        .to raise_error(RuntimeError)
     end
   end
 end
diff --git a/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb b/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb
index 0695c5ce096fca07564bfe53d9397955797cdee6..e7b595405a81272cd8b8a96b530ad59a52bb283b 100644
--- a/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb
@@ -21,11 +21,11 @@ describe Gitlab::Metrics::Subscribers::ActionView do
       values = { duration: 2.1 }
       tags   = { view: 'app/views/x.html.haml' }
 
-      expect(transaction).to receive(:increment).
-        with(:view_duration, 2.1)
+      expect(transaction).to receive(:increment)
+        .with(:view_duration, 2.1)
 
-      expect(transaction).to receive(:add_metric).
-        with(described_class::SERIES, values, tags)
+      expect(transaction).to receive(:add_metric)
+        .with(described_class::SERIES, values, tags)
 
       subscriber.render_template(event)
     end
diff --git a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
index 49699ffe28f0a1875e8b1a2134f7ad23104ac7eb..ce6587e993f429b9e86e2c7c7c233d829cfde42c 100644
--- a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb
@@ -12,8 +12,8 @@ describe Gitlab::Metrics::Subscribers::ActiveRecord do
   describe '#sql' do
     describe 'without a current transaction' do
       it 'simply returns' do
-        expect_any_instance_of(Gitlab::Metrics::Transaction).
-          not_to receive(:increment)
+        expect_any_instance_of(Gitlab::Metrics::Transaction)
+          .not_to receive(:increment)
 
         subscriber.sql(event)
       end
@@ -21,15 +21,15 @@ describe Gitlab::Metrics::Subscribers::ActiveRecord do
 
     describe 'with a current transaction' do
       it 'increments the :sql_duration value' do
-        expect(subscriber).to receive(:current_transaction).
-          at_least(:once).
-          and_return(transaction)
+        expect(subscriber).to receive(:current_transaction)
+          .at_least(:once)
+          .and_return(transaction)
 
-        expect(transaction).to receive(:increment).
-          with(:sql_duration, 0.2)
+        expect(transaction).to receive(:increment)
+          .with(:sql_duration, 0.2)
 
-        expect(transaction).to receive(:increment).
-          with(:sql_count, 1)
+        expect(transaction).to receive(:increment)
+          .with(:sql_count, 1)
 
         subscriber.sql(event)
       end
diff --git a/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb b/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
index d986c6fac4380224837888008be0933918c1c686..f04dc8dcc02b58991b454721d831753c6a93ffd5 100644
--- a/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
@@ -8,26 +8,26 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
 
   describe '#cache_read' do
     it 'increments the cache_read duration' do
-      expect(subscriber).to receive(:increment).
-        with(:cache_read, event.duration)
+      expect(subscriber).to receive(:increment)
+        .with(:cache_read, event.duration)
 
       subscriber.cache_read(event)
     end
 
     context 'with a transaction' do
       before do
-        allow(subscriber).to receive(:current_transaction).
-          and_return(transaction)
+        allow(subscriber).to receive(:current_transaction)
+          .and_return(transaction)
       end
 
       context 'with hit event' do
         let(:event) { double(:event, duration: 15.2, payload: { hit: true }) }
 
         it 'increments the cache_read_hit count' do
-          expect(transaction).to receive(:increment).
-            with(:cache_read_hit_count, 1)
-          expect(transaction).to receive(:increment).
-            with(any_args).at_least(1) # Other calls
+          expect(transaction).to receive(:increment)
+            .with(:cache_read_hit_count, 1)
+          expect(transaction).to receive(:increment)
+            .with(any_args).at_least(1) # Other calls
 
           subscriber.cache_read(event)
         end
@@ -36,8 +36,8 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
           let(:event) { double(:event, duration: 15.2, payload: { hit: true, super_operation: :fetch }) }
 
           it 'does not increment cache read miss' do
-            expect(transaction).not_to receive(:increment).
-              with(:cache_read_hit_count, 1)
+            expect(transaction).not_to receive(:increment)
+              .with(:cache_read_hit_count, 1)
 
             subscriber.cache_read(event)
           end
@@ -48,10 +48,10 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
         let(:event) { double(:event, duration: 15.2, payload: { hit: false }) }
 
         it 'increments the cache_read_miss count' do
-          expect(transaction).to receive(:increment).
-            with(:cache_read_miss_count, 1)
-          expect(transaction).to receive(:increment).
-            with(any_args).at_least(1) # Other calls
+          expect(transaction).to receive(:increment)
+            .with(:cache_read_miss_count, 1)
+          expect(transaction).to receive(:increment)
+            .with(any_args).at_least(1) # Other calls
 
           subscriber.cache_read(event)
         end
@@ -60,8 +60,8 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
           let(:event) { double(:event, duration: 15.2, payload: { hit: false, super_operation: :fetch }) }
 
           it 'does not increment cache read miss' do
-            expect(transaction).not_to receive(:increment).
-              with(:cache_read_miss_count, 1)
+            expect(transaction).not_to receive(:increment)
+              .with(:cache_read_miss_count, 1)
 
             subscriber.cache_read(event)
           end
@@ -72,8 +72,8 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
 
   describe '#cache_write' do
     it 'increments the cache_write duration' do
-      expect(subscriber).to receive(:increment).
-        with(:cache_write, event.duration)
+      expect(subscriber).to receive(:increment)
+        .with(:cache_write, event.duration)
 
       subscriber.cache_write(event)
     end
@@ -81,8 +81,8 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
 
   describe '#cache_delete' do
     it 'increments the cache_delete duration' do
-      expect(subscriber).to receive(:increment).
-        with(:cache_delete, event.duration)
+      expect(subscriber).to receive(:increment)
+        .with(:cache_delete, event.duration)
 
       subscriber.cache_delete(event)
     end
@@ -90,8 +90,8 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
 
   describe '#cache_exist?' do
     it 'increments the cache_exists duration' do
-      expect(subscriber).to receive(:increment).
-        with(:cache_exists, event.duration)
+      expect(subscriber).to receive(:increment)
+        .with(:cache_exists, event.duration)
 
       subscriber.cache_exist?(event)
     end
@@ -108,13 +108,13 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
 
     context 'with a transaction' do
       before do
-        allow(subscriber).to receive(:current_transaction).
-          and_return(transaction)
+        allow(subscriber).to receive(:current_transaction)
+          .and_return(transaction)
       end
 
       it 'increments the cache_read_hit count' do
-        expect(transaction).to receive(:increment).
-          with(:cache_read_hit_count, 1)
+        expect(transaction).to receive(:increment)
+          .with(:cache_read_hit_count, 1)
 
         subscriber.cache_fetch_hit(event)
       end
@@ -132,13 +132,13 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
 
     context 'with a transaction' do
       before do
-        allow(subscriber).to receive(:current_transaction).
-          and_return(transaction)
+        allow(subscriber).to receive(:current_transaction)
+          .and_return(transaction)
       end
 
       it 'increments the cache_fetch_miss count' do
-        expect(transaction).to receive(:increment).
-          with(:cache_read_miss_count, 1)
+        expect(transaction).to receive(:increment)
+          .with(:cache_read_miss_count, 1)
 
         subscriber.cache_generate(event)
       end
@@ -156,22 +156,22 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
 
     context 'with a transaction' do
       before do
-        allow(subscriber).to receive(:current_transaction).
-          and_return(transaction)
+        allow(subscriber).to receive(:current_transaction)
+          .and_return(transaction)
       end
 
       it 'increments the total and specific cache duration' do
-        expect(transaction).to receive(:increment).
-          with(:cache_duration, event.duration)
+        expect(transaction).to receive(:increment)
+          .with(:cache_duration, event.duration)
 
-        expect(transaction).to receive(:increment).
-          with(:cache_count, 1)
+        expect(transaction).to receive(:increment)
+          .with(:cache_count, 1)
 
-        expect(transaction).to receive(:increment).
-          with(:cache_delete_duration, event.duration)
+        expect(transaction).to receive(:increment)
+          .with(:cache_delete_duration, event.duration)
 
-        expect(transaction).to receive(:increment).
-          with(:cache_delete_count, 1)
+        expect(transaction).to receive(:increment)
+          .with(:cache_delete_count, 1)
 
         subscriber.increment(:cache_delete, event.duration)
       end
diff --git a/spec/lib/gitlab/metrics/transaction_spec.rb b/spec/lib/gitlab/metrics/transaction_spec.rb
index 0c5a6246d85156eed8714fc9955f5c662bdf08a7..3779af8151207b65d2e3b0d744f654b1eade0537 100644
--- a/spec/lib/gitlab/metrics/transaction_spec.rb
+++ b/spec/lib/gitlab/metrics/transaction_spec.rb
@@ -39,8 +39,8 @@ describe Gitlab::Metrics::Transaction do
 
   describe '#add_metric' do
     it 'adds a metric to the transaction' do
-      expect(Gitlab::Metrics::Metric).to receive(:new).
-        with('rails_foo', { number: 10 }, {})
+      expect(Gitlab::Metrics::Metric).to receive(:new)
+        .with('rails_foo', { number: 10 }, {})
 
       transaction.add_metric('foo', number: 10)
     end
@@ -61,8 +61,8 @@ describe Gitlab::Metrics::Transaction do
 
       values = { duration: 0.0, time: 3, allocated_memory: a_kind_of(Numeric) }
 
-      expect(transaction).to receive(:add_metric).
-        with('transactions', values, {})
+      expect(transaction).to receive(:add_metric)
+        .with('transactions', values, {})
 
       transaction.track_self
     end
@@ -78,8 +78,8 @@ describe Gitlab::Metrics::Transaction do
         allocated_memory: a_kind_of(Numeric)
       }
 
-      expect(transaction).to receive(:add_metric).
-        with('transactions', values, {})
+      expect(transaction).to receive(:add_metric)
+        .with('transactions', values, {})
 
       transaction.track_self
     end
@@ -109,8 +109,8 @@ describe Gitlab::Metrics::Transaction do
         allocated_memory: a_kind_of(Numeric)
       }
 
-      expect(transaction).to receive(:add_metric).
-        with('transactions', values, {})
+      expect(transaction).to receive(:add_metric)
+        .with('transactions', values, {})
 
       transaction.track_self
     end
@@ -120,8 +120,8 @@ describe Gitlab::Metrics::Transaction do
     it 'submits the metrics to Sidekiq' do
       transaction.track_self
 
-      expect(Gitlab::Metrics).to receive(:submit_metrics).
-        with([an_instance_of(Hash)])
+      expect(Gitlab::Metrics).to receive(:submit_metrics)
+        .with([an_instance_of(Hash)])
 
       transaction.submit
     end
@@ -137,8 +137,8 @@ describe Gitlab::Metrics::Transaction do
         timestamp: a_kind_of(Integer)
       }
 
-      expect(Gitlab::Metrics).to receive(:submit_metrics).
-        with([hash])
+      expect(Gitlab::Metrics).to receive(:submit_metrics)
+        .with([hash])
 
       transaction.submit
     end
@@ -154,8 +154,8 @@ describe Gitlab::Metrics::Transaction do
         timestamp: a_kind_of(Integer)
       }
 
-      expect(Gitlab::Metrics).to receive(:submit_metrics).
-        with([hash])
+      expect(Gitlab::Metrics).to receive(:submit_metrics)
+        .with([hash])
 
       transaction.submit
     end
diff --git a/spec/lib/gitlab/metrics_spec.rb b/spec/lib/gitlab/metrics_spec.rb
index 5a87b906609b2e8d799c89315cfec6d430b5b352..599b8807d8dbf281efd01f5870deb7fdc89ce1f3 100644
--- a/spec/lib/gitlab/metrics_spec.rb
+++ b/spec/lib/gitlab/metrics_spec.rb
@@ -15,6 +15,36 @@ describe Gitlab::Metrics do
     end
   end
 
+  describe '.prometheus_metrics_enabled_unmemoized' do
+    subject { described_class.send(:prometheus_metrics_enabled_unmemoized) }
+
+    context 'prometheus metrics enabled in config' do
+      before do
+        allow(described_class).to receive(:current_application_settings).and_return(prometheus_metrics_enabled: true)
+      end
+
+      context 'when metrics folder is present' do
+        before do
+          allow(described_class).to receive(:metrics_folder_present?).and_return(true)
+        end
+
+        it 'metrics are enabled' do
+          expect(subject).to eq(true)
+        end
+      end
+
+      context 'when metrics folder is missing' do
+        before do
+          allow(described_class).to receive(:metrics_folder_present?).and_return(false)
+        end
+
+        it 'metrics are disabled' do
+          expect(subject).to eq(false)
+        end
+      end
+    end
+  end
+
   describe '.prometheus_metrics_enabled?' do
     it 'returns a boolean' do
       expect(described_class.prometheus_metrics_enabled?).to be_in([true, false])
@@ -42,8 +72,8 @@ describe Gitlab::Metrics do
 
   describe '.prepare_metrics' do
     it 'returns a Hash with the keys as Symbols' do
-      metrics = described_class.
-        prepare_metrics([{ 'values' => {}, 'tags' => {} }])
+      metrics = described_class
+        .prepare_metrics([{ 'values' => {}, 'tags' => {} }])
 
       expect(metrics).to eq([{ values: {}, tags: {} }])
     end
@@ -88,19 +118,19 @@ describe Gitlab::Metrics do
       let(:transaction) { Gitlab::Metrics::Transaction.new }
 
       before do
-        allow(described_class).to receive(:current_transaction).
-          and_return(transaction)
+        allow(described_class).to receive(:current_transaction)
+          .and_return(transaction)
       end
 
       it 'adds a metric to the current transaction' do
-        expect(transaction).to receive(:increment).
-          with('foo_real_time', a_kind_of(Numeric))
+        expect(transaction).to receive(:increment)
+          .with('foo_real_time', a_kind_of(Numeric))
 
-        expect(transaction).to receive(:increment).
-          with('foo_cpu_time', a_kind_of(Numeric))
+        expect(transaction).to receive(:increment)
+          .with('foo_cpu_time', a_kind_of(Numeric))
 
-        expect(transaction).to receive(:increment).
-          with('foo_call_count', 1)
+        expect(transaction).to receive(:increment)
+          .with('foo_call_count', 1)
 
         described_class.measure(:foo) { 10 }
       end
@@ -116,8 +146,8 @@ describe Gitlab::Metrics do
   describe '.tag_transaction' do
     context 'without a transaction' do
       it 'does nothing' do
-        expect_any_instance_of(Gitlab::Metrics::Transaction).
-          not_to receive(:add_tag)
+        expect_any_instance_of(Gitlab::Metrics::Transaction)
+          .not_to receive(:add_tag)
 
         described_class.tag_transaction(:foo, 'bar')
       end
@@ -127,11 +157,11 @@ describe Gitlab::Metrics do
       let(:transaction) { Gitlab::Metrics::Transaction.new }
 
       it 'adds the tag to the transaction' do
-        expect(described_class).to receive(:current_transaction).
-          and_return(transaction)
+        expect(described_class).to receive(:current_transaction)
+          .and_return(transaction)
 
-        expect(transaction).to receive(:add_tag).
-          with(:foo, 'bar')
+        expect(transaction).to receive(:add_tag)
+          .with(:foo, 'bar')
 
         described_class.tag_transaction(:foo, 'bar')
       end
@@ -141,8 +171,8 @@ describe Gitlab::Metrics do
   describe '.action=' do
     context 'without a transaction' do
       it 'does nothing' do
-        expect_any_instance_of(Gitlab::Metrics::Transaction).
-          not_to receive(:action=)
+        expect_any_instance_of(Gitlab::Metrics::Transaction)
+          .not_to receive(:action=)
 
         described_class.action = 'foo'
       end
@@ -152,8 +182,8 @@ describe Gitlab::Metrics do
       it 'sets the action of a transaction' do
         trans = Gitlab::Metrics::Transaction.new
 
-        expect(described_class).to receive(:current_transaction).
-          and_return(trans)
+        expect(described_class).to receive(:current_transaction)
+          .and_return(trans)
 
         expect(trans).to receive(:action=).with('foo')
 
@@ -171,8 +201,8 @@ describe Gitlab::Metrics do
   describe '.add_event' do
     context 'without a transaction' do
       it 'does nothing' do
-        expect_any_instance_of(Gitlab::Metrics::Transaction).
-          not_to receive(:add_event)
+        expect_any_instance_of(Gitlab::Metrics::Transaction)
+          .not_to receive(:add_event)
 
         described_class.add_event(:meow)
       end
@@ -184,8 +214,8 @@ describe Gitlab::Metrics do
 
         expect(transaction).to receive(:add_event).with(:meow)
 
-        expect(described_class).to receive(:current_transaction).
-          and_return(transaction)
+        expect(described_class).to receive(:current_transaction)
+          .and_return(transaction)
 
         described_class.add_event(:meow)
       end
diff --git a/spec/lib/gitlab/project_authorizations_spec.rb b/spec/lib/gitlab/project_authorizations_spec.rb
index 67321f43710bef3b1a094a5f61c4ccabca516cfc..9ce33685697f9576f4b6a3ed4b912cf67b51262d 100644
--- a/spec/lib/gitlab/project_authorizations_spec.rb
+++ b/spec/lib/gitlab/project_authorizations_spec.rb
@@ -34,8 +34,8 @@ describe Gitlab::ProjectAuthorizations do
   end
 
   it 'includes the correct projects' do
-    expect(authorizations.pluck(:project_id)).
-      to include(owned_project.id, other_project.id, group_project.id)
+    expect(authorizations.pluck(:project_id))
+      .to include(owned_project.id, other_project.id, group_project.id)
   end
 
   it 'includes the correct access levels' do
diff --git a/spec/lib/gitlab/slash_commands/command_definition_spec.rb b/spec/lib/gitlab/quick_actions/command_definition_spec.rb
similarity index 99%
rename from spec/lib/gitlab/slash_commands/command_definition_spec.rb
rename to spec/lib/gitlab/quick_actions/command_definition_spec.rb
index 5b9173d3d3f515710be8cb64bec28e5859a90025..f44a562dc633b564573a3a0cea79619b5c567475 100644
--- a/spec/lib/gitlab/slash_commands/command_definition_spec.rb
+++ b/spec/lib/gitlab/quick_actions/command_definition_spec.rb
@@ -1,6 +1,6 @@
 require 'spec_helper'
 
-describe Gitlab::SlashCommands::CommandDefinition do
+describe Gitlab::QuickActions::CommandDefinition do
   subject { described_class.new(:command) }
 
   describe "#all_names" do
diff --git a/spec/lib/gitlab/slash_commands/dsl_spec.rb b/spec/lib/gitlab/quick_actions/dsl_spec.rb
similarity index 97%
rename from spec/lib/gitlab/slash_commands/dsl_spec.rb
rename to spec/lib/gitlab/quick_actions/dsl_spec.rb
index 33b49a5ddf98bd475dcfe3b81a4d2653243d069c..a4bb3f911d7e9953943ad7d0cd3b4b5e57f2b40e 100644
--- a/spec/lib/gitlab/slash_commands/dsl_spec.rb
+++ b/spec/lib/gitlab/quick_actions/dsl_spec.rb
@@ -1,9 +1,9 @@
 require 'spec_helper'
 
-describe Gitlab::SlashCommands::Dsl do
+describe Gitlab::QuickActions::Dsl do
   before :all do
     DummyClass = Struct.new(:project) do
-      include Gitlab::SlashCommands::Dsl # rubocop:disable RSpec/DescribedClass
+      include Gitlab::QuickActions::Dsl # rubocop:disable RSpec/DescribedClass
 
       desc 'A command with no args'
       command :no_args, :none do
diff --git a/spec/lib/gitlab/slash_commands/extractor_spec.rb b/spec/lib/gitlab/quick_actions/extractor_spec.rb
similarity index 98%
rename from spec/lib/gitlab/slash_commands/extractor_spec.rb
rename to spec/lib/gitlab/quick_actions/extractor_spec.rb
index d7f77486b3e6ebab612aed81d4b861d3e297fd45..9d32938e155a5f440266ae290f3af2dc60451555 100644
--- a/spec/lib/gitlab/slash_commands/extractor_spec.rb
+++ b/spec/lib/gitlab/quick_actions/extractor_spec.rb
@@ -1,9 +1,9 @@
 require 'spec_helper'
 
-describe Gitlab::SlashCommands::Extractor do
+describe Gitlab::QuickActions::Extractor do
   let(:definitions) do
     Class.new do
-      include Gitlab::SlashCommands::Dsl
+      include Gitlab::QuickActions::Dsl
 
       command(:reopen, :open) { }
       command(:assign) { }
diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb
index 0bee892fe0c3afc5fb5e718d506bc8108294566c..979f4fefcb645dbed2bca20c30f1d710d3df4440 100644
--- a/spec/lib/gitlab/regex_spec.rb
+++ b/spec/lib/gitlab/regex_spec.rb
@@ -20,6 +20,18 @@ describe Gitlab::Regex, lib: true do
     it { is_expected.to match('foo@bar') }
   end
 
+  describe '.environment_slug_regex' do
+    subject { described_class.environment_name_regex }
+
+    it { is_expected.to match('foo') }
+    it { is_expected.to match('foo-1') }
+    it { is_expected.to match('FOO') }
+    it { is_expected.to match('foo/1') }
+    it { is_expected.to match('foo.1') }
+    it { is_expected.not_to match('9&foo') }
+    it { is_expected.not_to match('foo-^') }
+  end
+
   describe '.environment_slug_regex' do
     subject { described_class.environment_slug_regex }
 
diff --git a/spec/lib/gitlab/repo_path_spec.rb b/spec/lib/gitlab/repo_path_spec.rb
index f90253971075d031ece9d211ab73e122eb6fa69b..efea4f429bfa358924d11430d5e4dda186b8cf24 100644
--- a/spec/lib/gitlab/repo_path_spec.rb
+++ b/spec/lib/gitlab/repo_path_spec.rb
@@ -4,24 +4,44 @@ describe ::Gitlab::RepoPath do
   describe '.parse' do
     set(:project) { create(:project) }
 
-    it 'parses a full repository path' do
-      expect(described_class.parse(project.repository.path)).to eq([project, false])
-    end
+    context 'a repository storage path' do
+      it 'parses a full repository path' do
+        expect(described_class.parse(project.repository.path)).to eq([project, false, nil])
+      end
 
-    it 'parses a full wiki path' do
-      expect(described_class.parse(project.wiki.repository.path)).to eq([project, true])
+      it 'parses a full wiki path' do
+        expect(described_class.parse(project.wiki.repository.path)).to eq([project, true, nil])
+      end
     end
 
-    it 'parses a relative repository path' do
-      expect(described_class.parse(project.full_path + '.git')).to eq([project, false])
-    end
+    context 'a relative path' do
+      it 'parses a relative repository path' do
+        expect(described_class.parse(project.full_path + '.git')).to eq([project, false, nil])
+      end
 
-    it 'parses a relative wiki path' do
-      expect(described_class.parse(project.full_path + '.wiki.git')).to eq([project, true])
-    end
+      it 'parses a relative wiki path' do
+        expect(described_class.parse(project.full_path + '.wiki.git')).to eq([project, true, nil])
+      end
+
+      it 'parses a relative path starting with /' do
+        expect(described_class.parse('/' + project.full_path + '.git')).to eq([project, false, nil])
+      end
+
+      context 'of a redirected project' do
+        let(:redirect) { project.route.create_redirect('foo/bar') }
+
+        it 'parses a relative repository path' do
+          expect(described_class.parse(redirect.path + '.git')).to eq([project, false, 'foo/bar'])
+        end
+
+        it 'parses a relative wiki path' do
+          expect(described_class.parse(redirect.path + '.wiki.git')).to eq([project, true, 'foo/bar.wiki'])
+        end
 
-    it 'parses a relative path starting with /' do
-      expect(described_class.parse('/' + project.full_path + '.git')).to eq([project, false])
+        it 'parses a relative path starting with /' do
+          expect(described_class.parse('/' + redirect.path + '.git')).to eq([project, false, 'foo/bar'])
+        end
+      end
     end
   end
 
@@ -43,4 +63,33 @@ describe ::Gitlab::RepoPath do
       )
     end
   end
+
+  describe '.find_project' do
+    let(:project) { create(:empty_project) }
+    let(:redirect) { project.route.create_redirect('foo/bar/baz') }
+
+    context 'when finding a project by its canonical path' do
+      context 'when the cases match' do
+        it 'returns the project and false' do
+          expect(described_class.find_project(project.full_path)).to eq([project, false])
+        end
+      end
+
+      context 'when the cases do not match' do
+        # This is slightly different than web behavior because on the web it is
+        # easy and safe to redirect someone to the correctly-cased URL. For git
+        # requests, we should accept wrongly-cased URLs because it is a pain to
+        # block people's git operations and force them to update remote URLs.
+        it 'returns the project and false' do
+          expect(described_class.find_project(project.full_path.upcase)).to eq([project, false])
+        end
+      end
+    end
+
+    context 'when finding a project via a redirect' do
+      it 'returns the project and true' do
+        expect(described_class.find_project(redirect.path)).to eq([project, true])
+      end
+    end
+  end
 end
diff --git a/spec/lib/gitlab/route_map_spec.rb b/spec/lib/gitlab/route_map_spec.rb
index 2370f56a6131a994a7c27e0c7e74456a519b2029..21c00c6e5b80a3c511bb74e5364c54cbc4499ff2 100644
--- a/spec/lib/gitlab/route_map_spec.rb
+++ b/spec/lib/gitlab/route_map_spec.rb
@@ -4,43 +4,43 @@ describe Gitlab::RouteMap, lib: true do
   describe '#initialize' do
     context 'when the data is not YAML' do
       it 'raises an error' do
-        expect { described_class.new('"') }.
-          to raise_error(Gitlab::RouteMap::FormatError, /valid YAML/)
+        expect { described_class.new('"') }
+          .to raise_error(Gitlab::RouteMap::FormatError, /valid YAML/)
       end
     end
 
     context 'when the data is not a YAML array' do
       it 'raises an error' do
-        expect { described_class.new(YAML.dump('foo')) }.
-          to raise_error(Gitlab::RouteMap::FormatError, /an array/)
+        expect { described_class.new(YAML.dump('foo')) }
+          .to raise_error(Gitlab::RouteMap::FormatError, /an array/)
       end
     end
 
     context 'when an entry is not a hash' do
       it 'raises an error' do
-        expect { described_class.new(YAML.dump(['foo'])) }.
-          to raise_error(Gitlab::RouteMap::FormatError, /a hash/)
+        expect { described_class.new(YAML.dump(['foo'])) }
+          .to raise_error(Gitlab::RouteMap::FormatError, /a hash/)
       end
     end
 
     context 'when an entry does not have a source key' do
       it 'raises an error' do
-        expect { described_class.new(YAML.dump([{ 'public' => 'index.html' }])) }.
-          to raise_error(Gitlab::RouteMap::FormatError, /source key/)
+        expect { described_class.new(YAML.dump([{ 'public' => 'index.html' }])) }
+          .to raise_error(Gitlab::RouteMap::FormatError, /source key/)
       end
     end
 
     context 'when an entry does not have a public key' do
       it 'raises an error' do
-        expect { described_class.new(YAML.dump([{ 'source' => '/index\.html/' }])) }.
-          to raise_error(Gitlab::RouteMap::FormatError, /public key/)
+        expect { described_class.new(YAML.dump([{ 'source' => '/index\.html/' }])) }
+          .to raise_error(Gitlab::RouteMap::FormatError, /public key/)
       end
     end
 
     context 'when an entry source is not a valid regex' do
       it 'raises an error' do
-        expect { described_class.new(YAML.dump([{ 'source' => '/[/', 'public' => 'index.html' }])) }.
-          to raise_error(Gitlab::RouteMap::FormatError, /regular expression/)
+        expect { described_class.new(YAML.dump([{ 'source' => '/[/', 'public' => 'index.html' }])) }
+          .to raise_error(Gitlab::RouteMap::FormatError, /regular expression/)
       end
     end
 
diff --git a/spec/lib/gitlab/sherlock/file_sample_spec.rb b/spec/lib/gitlab/sherlock/file_sample_spec.rb
index cadf8bbce78057558264b159703a2d95517227bc..4989d14def30a83e641092039fb5f441eaa93c6e 100644
--- a/spec/lib/gitlab/sherlock/file_sample_spec.rb
+++ b/spec/lib/gitlab/sherlock/file_sample_spec.rb
@@ -35,8 +35,8 @@ describe Gitlab::Sherlock::FileSample, lib: true do
 
   describe '#relative_path' do
     it 'returns the relative path' do
-      expect(sample.relative_path).
-        to eq('spec/lib/gitlab/sherlock/file_sample_spec.rb')
+      expect(sample.relative_path)
+        .to eq('spec/lib/gitlab/sherlock/file_sample_spec.rb')
     end
   end
 
diff --git a/spec/lib/gitlab/sherlock/line_profiler_spec.rb b/spec/lib/gitlab/sherlock/line_profiler_spec.rb
index d57627bba2b4ab885d793a8313067fae8e21b816..39c6b2a48445da4d396f758cbc507f1b390d156a 100644
--- a/spec/lib/gitlab/sherlock/line_profiler_spec.rb
+++ b/spec/lib/gitlab/sherlock/line_profiler_spec.rb
@@ -20,9 +20,9 @@ describe Gitlab::Sherlock::LineProfiler, lib: true do
 
   describe '#profile_mri' do
     it 'returns an Array containing the return value and profiling samples' do
-      allow(profiler).to receive(:lineprof).
-        and_yield.
-        and_return({ __FILE__ => [[0, 0, 0, 0]] })
+      allow(profiler).to receive(:lineprof)
+        .and_yield
+        .and_return({ __FILE__ => [[0, 0, 0, 0]] })
 
       retval, samples = profiler.profile_mri { 42 }
 
diff --git a/spec/lib/gitlab/sherlock/middleware_spec.rb b/spec/lib/gitlab/sherlock/middleware_spec.rb
index 2bbeb25ce98341bbaa68d16b9615f9e89c557ac4..b98ab0b14a2abe3a69a665d3a2e9c947a26b15a8 100644
--- a/spec/lib/gitlab/sherlock/middleware_spec.rb
+++ b/spec/lib/gitlab/sherlock/middleware_spec.rb
@@ -72,8 +72,8 @@ describe Gitlab::Sherlock::Middleware, lib: true do
         'REQUEST_URI' => '/cats'
       }
 
-      expect(middleware.transaction_from_env(env)).
-        to be_an_instance_of(Gitlab::Sherlock::Transaction)
+      expect(middleware.transaction_from_env(env))
+        .to be_an_instance_of(Gitlab::Sherlock::Transaction)
     end
   end
 end
diff --git a/spec/lib/gitlab/sherlock/query_spec.rb b/spec/lib/gitlab/sherlock/query_spec.rb
index 0a62042813883267a2952d42897be0ccea68d868..d97b5eef573930d5288834827b1cd4e807dd5d5c 100644
--- a/spec/lib/gitlab/sherlock/query_spec.rb
+++ b/spec/lib/gitlab/sherlock/query_spec.rb
@@ -13,8 +13,8 @@ describe Gitlab::Sherlock::Query, lib: true do
       sql = 'SELECT COUNT(*) FROM users WHERE id = $1'
       bindings = [[double(:column), 10]]
 
-      query = described_class.
-        new_with_bindings(sql, bindings, started_at, finished_at)
+      query = described_class
+        .new_with_bindings(sql, bindings, started_at, finished_at)
 
       expect(query.query).to eq('SELECT COUNT(*) FROM users WHERE id = 10;')
     end
diff --git a/spec/lib/gitlab/sherlock/transaction_spec.rb b/spec/lib/gitlab/sherlock/transaction_spec.rb
index 9fe18f253f0bc3dea82f887f6cc7bf1769c87b9a..6ae1aa20ea720d12ad1d53e28d38a68b5f555e6d 100644
--- a/spec/lib/gitlab/sherlock/transaction_spec.rb
+++ b/spec/lib/gitlab/sherlock/transaction_spec.rb
@@ -109,8 +109,8 @@ describe Gitlab::Sherlock::Transaction, lib: true do
 
       query1 = Gitlab::Sherlock::Query.new('SELECT 1', start_time, start_time)
 
-      query2 = Gitlab::Sherlock::Query.
-        new('SELECT 2', start_time, start_time + 5)
+      query2 = Gitlab::Sherlock::Query
+        .new('SELECT 2', start_time, start_time + 5)
 
       transaction.queries << query1
       transaction.queries << query2
@@ -162,11 +162,11 @@ describe Gitlab::Sherlock::Transaction, lib: true do
   describe '#profile_lines' do
     describe 'when line profiling is enabled' do
       it 'yields the block using the line profiler' do
-        allow(Gitlab::Sherlock).to receive(:enable_line_profiler?).
-          and_return(true)
+        allow(Gitlab::Sherlock).to receive(:enable_line_profiler?)
+          .and_return(true)
 
-        allow_any_instance_of(Gitlab::Sherlock::LineProfiler).
-          to receive(:profile).and_return('cats are amazing', [])
+        allow_any_instance_of(Gitlab::Sherlock::LineProfiler)
+          .to receive(:profile).and_return('cats are amazing', [])
 
         retval = transaction.profile_lines { 'cats are amazing' }
 
@@ -176,8 +176,8 @@ describe Gitlab::Sherlock::Transaction, lib: true do
 
     describe 'when line profiling is disabled' do
       it 'yields the block' do
-        allow(Gitlab::Sherlock).to receive(:enable_line_profiler?).
-          and_return(false)
+        allow(Gitlab::Sherlock).to receive(:enable_line_profiler?)
+          .and_return(false)
 
         retval = transaction.profile_lines { 'cats are amazing' }
 
@@ -196,8 +196,8 @@ describe Gitlab::Sherlock::Transaction, lib: true do
     end
 
     it 'tracks executed queries' do
-      expect(transaction).to receive(:track_query).
-        with('SELECT 1', [], time, time)
+      expect(transaction).to receive(:track_query)
+        .with('SELECT 1', [], time, time)
 
       subscription.publish('test', time, time, nil, query_data)
     end
@@ -205,8 +205,8 @@ describe Gitlab::Sherlock::Transaction, lib: true do
     it 'only tracks queries triggered from the transaction thread' do
       expect(transaction).not_to receive(:track_query)
 
-      Thread.new { subscription.publish('test', time, time, nil, query_data) }.
-        join
+      Thread.new { subscription.publish('test', time, time, nil, query_data) }
+        .join
     end
   end
 
@@ -228,8 +228,8 @@ describe Gitlab::Sherlock::Transaction, lib: true do
     it 'only tracks views rendered from the transaction thread' do
       expect(transaction).not_to receive(:track_view)
 
-      Thread.new { subscription.publish('test', time, time, nil, view_data) }.
-        join
+      Thread.new { subscription.publish('test', time, time, nil, view_data) }
+        .join
     end
   end
 end
diff --git a/spec/lib/gitlab/sidekiq_status/client_middleware_spec.rb b/spec/lib/gitlab/sidekiq_status/client_middleware_spec.rb
index 6307f8c16a3f3b28927a576f3294aad96b40aef6..37d9e1d3e6b0426ab722aa9249227b17c462c1bd 100644
--- a/spec/lib/gitlab/sidekiq_status/client_middleware_spec.rb
+++ b/spec/lib/gitlab/sidekiq_status/client_middleware_spec.rb
@@ -5,8 +5,8 @@ describe Gitlab::SidekiqStatus::ClientMiddleware do
     it 'tracks the job in Redis' do
       expect(Gitlab::SidekiqStatus).to receive(:set).with('123', Gitlab::SidekiqStatus::DEFAULT_EXPIRATION)
 
-      described_class.new.
-        call('Foo', { 'jid' => '123' }, double(:queue), double(:pool)) { nil }
+      described_class.new
+        .call('Foo', { 'jid' => '123' }, double(:queue), double(:pool)) { nil }
     end
   end
 end
diff --git a/spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb b/spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb
index 80728197b8cf313fe960f880ca69f498a7499f12..04e09d3dec81befa74414ae974c055f5aa823f73 100644
--- a/spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb
+++ b/spec/lib/gitlab/sidekiq_status/server_middleware_spec.rb
@@ -5,8 +5,8 @@ describe Gitlab::SidekiqStatus::ServerMiddleware do
     it 'stops tracking of a job upon completion' do
       expect(Gitlab::SidekiqStatus).to receive(:unset).with('123')
 
-      ret = described_class.new.
-        call(double(:worker), { 'jid' => '123' }, double(:queue)) { 10 }
+      ret = described_class.new
+        .call(double(:worker), { 'jid' => '123' }, double(:queue)) { 10 }
 
       expect(ret).to eq(10)
     end
diff --git a/spec/lib/gitlab/chat_commands/command_spec.rb b/spec/lib/gitlab/slash_commands/command_spec.rb
similarity index 93%
rename from spec/lib/gitlab/chat_commands/command_spec.rb
rename to spec/lib/gitlab/slash_commands/command_spec.rb
index 13e6953147bca8246bd4f9d8241c8946ca7b25ca..28d7f9858c33b858be16bcd81b4d344ad9683e02 100644
--- a/spec/lib/gitlab/chat_commands/command_spec.rb
+++ b/spec/lib/gitlab/slash_commands/command_spec.rb
@@ -1,6 +1,6 @@
 require 'spec_helper'
 
-describe Gitlab::ChatCommands::Command, service: true do
+describe Gitlab::SlashCommands::Command, service: true do
   let(:project) { create(:empty_project) }
   let(:user) { create(:user) }
 
@@ -93,19 +93,19 @@ describe Gitlab::ChatCommands::Command, service: true do
     context 'IssueShow is triggered' do
       let(:params) { { text: 'issue show 123' } }
 
-      it { is_expected.to eq(Gitlab::ChatCommands::IssueShow) }
+      it { is_expected.to eq(Gitlab::SlashCommands::IssueShow) }
     end
 
     context 'IssueCreate is triggered' do
       let(:params) { { text: 'issue create my title' } }
 
-      it { is_expected.to eq(Gitlab::ChatCommands::IssueNew) }
+      it { is_expected.to eq(Gitlab::SlashCommands::IssueNew) }
     end
 
     context 'IssueSearch is triggered' do
       let(:params) { { text: 'issue search my query' } }
 
-      it { is_expected.to eq(Gitlab::ChatCommands::IssueSearch) }
+      it { is_expected.to eq(Gitlab::SlashCommands::IssueSearch) }
     end
   end
 end
diff --git a/spec/lib/gitlab/chat_commands/deploy_spec.rb b/spec/lib/gitlab/slash_commands/deploy_spec.rb
similarity index 98%
rename from spec/lib/gitlab/chat_commands/deploy_spec.rb
rename to spec/lib/gitlab/slash_commands/deploy_spec.rb
index 46dbdeae37cea2475caca685462c01da3eef5814..d919f7260dbf3621b08de5e64392667d61d275ab 100644
--- a/spec/lib/gitlab/chat_commands/deploy_spec.rb
+++ b/spec/lib/gitlab/slash_commands/deploy_spec.rb
@@ -1,6 +1,6 @@
 require 'spec_helper'
 
-describe Gitlab::ChatCommands::Deploy, service: true do
+describe Gitlab::SlashCommands::Deploy, service: true do
   describe '#execute' do
     let(:project) { create(:empty_project) }
     let(:user) { create(:user) }
diff --git a/spec/lib/gitlab/chat_commands/issue_new_spec.rb b/spec/lib/gitlab/slash_commands/issue_new_spec.rb
similarity index 97%
rename from spec/lib/gitlab/chat_commands/issue_new_spec.rb
rename to spec/lib/gitlab/slash_commands/issue_new_spec.rb
index 84c22328064e7f6390ba5e6361e315aa34c3f3bc..4de50d4a8bb638fe9406d68f54b8a9703f2446ea 100644
--- a/spec/lib/gitlab/chat_commands/issue_new_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_new_spec.rb
@@ -1,6 +1,6 @@
 require 'spec_helper'
 
-describe Gitlab::ChatCommands::IssueNew, service: true do
+describe Gitlab::SlashCommands::IssueNew, service: true do
   describe '#execute' do
     let(:project) { create(:empty_project) }
     let(:user) { create(:user) }
diff --git a/spec/lib/gitlab/chat_commands/issue_search_spec.rb b/spec/lib/gitlab/slash_commands/issue_search_spec.rb
similarity index 95%
rename from spec/lib/gitlab/chat_commands/issue_search_spec.rb
rename to spec/lib/gitlab/slash_commands/issue_search_spec.rb
index 551ccb79a5866f13176dc95d335511445123e002..06fff0afc50333fc5a95cba777d3e89536362ceb 100644
--- a/spec/lib/gitlab/chat_commands/issue_search_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_search_spec.rb
@@ -1,6 +1,6 @@
 require 'spec_helper'
 
-describe Gitlab::ChatCommands::IssueSearch, service: true do
+describe Gitlab::SlashCommands::IssueSearch, service: true do
   describe '#execute' do
     let!(:issue) { create(:issue, project: project, title: 'find me') }
     let!(:confidential) { create(:issue, :confidential, project: project, title: 'mepmep find') }
diff --git a/spec/lib/gitlab/chat_commands/issue_show_spec.rb b/spec/lib/gitlab/slash_commands/issue_show_spec.rb
similarity index 96%
rename from spec/lib/gitlab/chat_commands/issue_show_spec.rb
rename to spec/lib/gitlab/slash_commands/issue_show_spec.rb
index 1f20d0a44ce56eb9c1d5af108c15b8a2c7a7b46e..1899f664ccd32f23612a274f15ec59c6110371b5 100644
--- a/spec/lib/gitlab/chat_commands/issue_show_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_show_spec.rb
@@ -1,6 +1,6 @@
 require 'spec_helper'
 
-describe Gitlab::ChatCommands::IssueShow, service: true do
+describe Gitlab::SlashCommands::IssueShow, service: true do
   describe '#execute' do
     let(:issue) { create(:issue, project: project) }
     let(:project) { create(:empty_project) }
diff --git a/spec/lib/gitlab/chat_commands/presenters/access_spec.rb b/spec/lib/gitlab/slash_commands/presenters/access_spec.rb
similarity index 95%
rename from spec/lib/gitlab/chat_commands/presenters/access_spec.rb
rename to spec/lib/gitlab/slash_commands/presenters/access_spec.rb
index ae41d75ab0c2654df66e2f946d7a43ad0c6bf5f9..ef3d217f7bec159ab8fe92f9b4136780a1e01850 100644
--- a/spec/lib/gitlab/chat_commands/presenters/access_spec.rb
+++ b/spec/lib/gitlab/slash_commands/presenters/access_spec.rb
@@ -1,6 +1,6 @@
 require 'spec_helper'
 
-describe Gitlab::ChatCommands::Presenters::Access do
+describe Gitlab::SlashCommands::Presenters::Access do
   describe '#access_denied' do
     subject { described_class.new.access_denied }
 
diff --git a/spec/lib/gitlab/chat_commands/presenters/deploy_spec.rb b/spec/lib/gitlab/slash_commands/presenters/deploy_spec.rb
similarity index 96%
rename from spec/lib/gitlab/chat_commands/presenters/deploy_spec.rb
rename to spec/lib/gitlab/slash_commands/presenters/deploy_spec.rb
index dc2dd300072f5bd5c63275ad4658e1e7356900c4..dee3c77db2760ca871c632e490857d506a6308d6 100644
--- a/spec/lib/gitlab/chat_commands/presenters/deploy_spec.rb
+++ b/spec/lib/gitlab/slash_commands/presenters/deploy_spec.rb
@@ -1,6 +1,6 @@
 require 'spec_helper'
 
-describe Gitlab::ChatCommands::Presenters::Deploy do
+describe Gitlab::SlashCommands::Presenters::Deploy do
   let(:build) { create(:ci_build) }
 
   describe '#present' do
diff --git a/spec/lib/gitlab/chat_commands/presenters/issue_new_spec.rb b/spec/lib/gitlab/slash_commands/presenters/issue_new_spec.rb
similarity index 88%
rename from spec/lib/gitlab/chat_commands/presenters/issue_new_spec.rb
rename to spec/lib/gitlab/slash_commands/presenters/issue_new_spec.rb
index 17fcdbc2452c69700d310fe1c7dbf86acfacdf4a..7f81ebb47dbfc487fef04dcadbb0408807b1ff69 100644
--- a/spec/lib/gitlab/chat_commands/presenters/issue_new_spec.rb
+++ b/spec/lib/gitlab/slash_commands/presenters/issue_new_spec.rb
@@ -1,6 +1,6 @@
 require 'spec_helper'
 
-describe Gitlab::ChatCommands::Presenters::IssueNew do
+describe Gitlab::SlashCommands::Presenters::IssueNew do
   let(:project) { create(:empty_project) }
   let(:issue) { create(:issue, project: project) }
   let(:attachment) { subject[:attachments].first }
diff --git a/spec/lib/gitlab/chat_commands/presenters/issue_search_spec.rb b/spec/lib/gitlab/slash_commands/presenters/issue_search_spec.rb
similarity index 90%
rename from spec/lib/gitlab/chat_commands/presenters/issue_search_spec.rb
rename to spec/lib/gitlab/slash_commands/presenters/issue_search_spec.rb
index 3799a324db4a43a5d8d59e0d403f80d030f69708..7e57a0addcb10a4a97b5fa5d4294b7e163304be0 100644
--- a/spec/lib/gitlab/chat_commands/presenters/issue_search_spec.rb
+++ b/spec/lib/gitlab/slash_commands/presenters/issue_search_spec.rb
@@ -1,6 +1,6 @@
 require 'spec_helper'
 
-describe Gitlab::ChatCommands::Presenters::IssueSearch do
+describe Gitlab::SlashCommands::Presenters::IssueSearch do
   let(:project) { create(:empty_project) }
   let(:message) { subject[:text] }
 
diff --git a/spec/lib/gitlab/chat_commands/presenters/issue_show_spec.rb b/spec/lib/gitlab/slash_commands/presenters/issue_show_spec.rb
similarity index 96%
rename from spec/lib/gitlab/chat_commands/presenters/issue_show_spec.rb
rename to spec/lib/gitlab/slash_commands/presenters/issue_show_spec.rb
index 3916fc704a4d91ab57eae93db1b48172bed6bba6..2a6ed86073741dc6b39562f0b5d8af3d12b68a23 100644
--- a/spec/lib/gitlab/chat_commands/presenters/issue_show_spec.rb
+++ b/spec/lib/gitlab/slash_commands/presenters/issue_show_spec.rb
@@ -1,6 +1,6 @@
 require 'spec_helper'
 
-describe Gitlab::ChatCommands::Presenters::IssueShow do
+describe Gitlab::SlashCommands::Presenters::IssueShow do
   let(:project) { create(:empty_project) }
   let(:issue) { create(:issue, project: project) }
   let(:attachment) { subject[:attachments].first }
diff --git a/spec/lib/gitlab/url_builder_spec.rb b/spec/lib/gitlab/url_builder_spec.rb
index e8a37e8d77be6355f7a420cd3d2e2abe53ccd19b..e9a6e2735169d052160bc17a8107d9a11eb3176c 100644
--- a/spec/lib/gitlab/url_builder_spec.rb
+++ b/spec/lib/gitlab/url_builder_spec.rb
@@ -112,8 +112,8 @@ describe Gitlab::UrlBuilder, lib: true do
         it 'returns a proper URL' do
           project = build_stubbed(:empty_project)
 
-          expect { described_class.build(project) }.
-            to raise_error(NotImplementedError, 'No URL builder defined for Project')
+          expect { described_class.build(project) }
+            .to raise_error(NotImplementedError, 'No URL builder defined for Project')
         end
       end
     end
diff --git a/spec/lib/gitlab/view/presenter/delegated_spec.rb b/spec/lib/gitlab/view/presenter/delegated_spec.rb
index e9d4af54389aac41a73237ea89b0b9d3709265b6..940a2ce6ebd9fa7d5894b15c88acb7fcb360c0df 100644
--- a/spec/lib/gitlab/view/presenter/delegated_spec.rb
+++ b/spec/lib/gitlab/view/presenter/delegated_spec.rb
@@ -18,8 +18,8 @@ describe Gitlab::View::Presenter::Delegated do
     end
 
     it 'raise an error if the presentee already respond to method' do
-      expect { presenter_class.new(project, user: 'Jane Doe') }.
-        to raise_error Gitlab::View::Presenter::CannotOverrideMethodError
+      expect { presenter_class.new(project, user: 'Jane Doe') }
+        .to raise_error Gitlab::View::Presenter::CannotOverrideMethodError
     end
   end
 
diff --git a/spec/lib/gitlab/visibility_level_spec.rb b/spec/lib/gitlab/visibility_level_spec.rb
index 3255c6f1ef73dc762d723a1a5e13c297db4d55f7..84d2484cc8aaf5f54f24375c7dbeeb4cc091dbd6 100644
--- a/spec/lib/gitlab/visibility_level_spec.rb
+++ b/spec/lib/gitlab/visibility_level_spec.rb
@@ -18,4 +18,35 @@ describe Gitlab::VisibilityLevel, lib: true do
       expect(described_class.level_value(100)).to eq(Gitlab::VisibilityLevel::PRIVATE)
     end
   end
+
+  describe '.levels_for_user' do
+    it 'returns all levels for an admin' do
+      user = double(:user, admin?: true)
+
+      expect(described_class.levels_for_user(user))
+        .to eq([Gitlab::VisibilityLevel::PRIVATE,
+                Gitlab::VisibilityLevel::INTERNAL,
+                Gitlab::VisibilityLevel::PUBLIC])
+    end
+
+    it 'returns INTERNAL and PUBLIC for internal users' do
+      user = double(:user, admin?: false, external?: false)
+
+      expect(described_class.levels_for_user(user))
+        .to eq([Gitlab::VisibilityLevel::INTERNAL,
+                Gitlab::VisibilityLevel::PUBLIC])
+    end
+
+    it 'returns PUBLIC for external users' do
+      user = double(:user, admin?: false, external?: true)
+
+      expect(described_class.levels_for_user(user))
+        .to eq([Gitlab::VisibilityLevel::PUBLIC])
+    end
+
+    it 'returns PUBLIC when no user is given' do
+      expect(described_class.levels_for_user)
+        .to eq([Gitlab::VisibilityLevel::PUBLIC])
+    end
+  end
 end
diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb
index ad19998dff4a015320a18f620d1e2a28c6914806..493ff3bb5fbc3605267f95208267acb7bc096cbb 100644
--- a/spec/lib/gitlab/workhorse_spec.rb
+++ b/spec/lib/gitlab/workhorse_spec.rb
@@ -202,7 +202,11 @@ describe Gitlab::Workhorse, lib: true do
     context 'when Gitaly is enabled' do
       let(:gitaly_params) do
         {
-          GitalyAddress: Gitlab::GitalyClient.address('default')
+          GitalyAddress: Gitlab::GitalyClient.address('default'),
+          GitalyServer: {
+            address: Gitlab::GitalyClient.address('default'),
+            token: Gitlab::GitalyClient.token('default')
+          }
         }
       end
 
@@ -212,7 +216,6 @@ describe Gitlab::Workhorse, lib: true do
 
       it 'includes a Repository param' do
         repo_param = { Repository: {
-          path: '', # deprecated field; grpc automatically creates it anyway
           storage_name: 'default',
           relative_path: project.full_path + '.git'
         } }
diff --git a/spec/lib/mattermost/command_spec.rb b/spec/lib/mattermost/command_spec.rb
index 4b5938edeb9a6f681083933eed36efdaf18e7895..369e7b181b9cc7d237f826e16e429d1667f067d2 100644
--- a/spec/lib/mattermost/command_spec.rb
+++ b/spec/lib/mattermost/command_spec.rb
@@ -6,8 +6,8 @@ describe Mattermost::Command do
   before do
     Mattermost::Session.base_uri('http://mattermost.example.com')
 
-    allow_any_instance_of(Mattermost::Client).to receive(:with_session).
-      and_yield(Mattermost::Session.new(nil))
+    allow_any_instance_of(Mattermost::Client).to receive(:with_session)
+      .and_yield(Mattermost::Session.new(nil))
   end
 
   describe '#create' do
@@ -20,12 +20,12 @@ describe Mattermost::Command do
 
     context 'for valid trigger word' do
       before do
-        stub_request(:post, 'http://mattermost.example.com/api/v3/teams/abc/commands/create').
-          with(body: {
+        stub_request(:post, 'http://mattermost.example.com/api/v3/teams/abc/commands/create')
+          .with(body: {
             team_id: 'abc',
             trigger: 'gitlab'
-          }.to_json).
-          to_return(
+          }.to_json)
+          .to_return(
             status: 200,
             headers: { 'Content-Type' => 'application/json' },
             body: { token: 'token' }.to_json
@@ -39,8 +39,8 @@ describe Mattermost::Command do
 
     context 'for error message' do
       before do
-        stub_request(:post, 'http://mattermost.example.com/api/v3/teams/abc/commands/create').
-          to_return(
+        stub_request(:post, 'http://mattermost.example.com/api/v3/teams/abc/commands/create')
+          .to_return(
             status: 500,
             headers: { 'Content-Type' => 'application/json' },
             body: {
diff --git a/spec/lib/mattermost/session_spec.rb b/spec/lib/mattermost/session_spec.rb
index 74d12e3718105f58afdc828b83a3196220d4c0fd..be3908e8f6ab4b33fe9018e27ce27a7fe2e593b2 100644
--- a/spec/lib/mattermost/session_spec.rb
+++ b/spec/lib/mattermost/session_spec.rb
@@ -21,8 +21,8 @@ describe Mattermost::Session, type: :request do
   describe '#with session' do
     let(:location) { 'http://location.tld' }
     let!(:stub) do
-      WebMock.stub_request(:get, "#{mattermost_url}/api/v3/oauth/gitlab/login").
-        to_return(headers: { 'location' => location }, status: 307)
+      WebMock.stub_request(:get, "#{mattermost_url}/api/v3/oauth/gitlab/login")
+        .to_return(headers: { 'location' => location }, status: 307)
     end
 
     context 'without oauth uri' do
@@ -60,9 +60,9 @@ describe Mattermost::Session, type: :request do
         end
 
         before do
-          WebMock.stub_request(:get, "#{mattermost_url}/signup/gitlab/complete").
-            with(query: hash_including({ 'state' => state })).
-            to_return do |request|
+          WebMock.stub_request(:get, "#{mattermost_url}/signup/gitlab/complete")
+            .with(query: hash_including({ 'state' => state }))
+            .to_return do |request|
               post "/oauth/token",
                 client_id: doorkeeper.uid,
                 client_secret: doorkeeper.secret,
@@ -75,8 +75,8 @@ describe Mattermost::Session, type: :request do
               end
             end
 
-          WebMock.stub_request(:post, "#{mattermost_url}/api/v3/users/logout").
-            to_return(headers: { Authorization: 'token thisworksnow' }, status: 200)
+          WebMock.stub_request(:post, "#{mattermost_url}/api/v3/users/logout")
+            .to_return(headers: { Authorization: 'token thisworksnow' }, status: 200)
         end
 
         it 'can setup a session' do
diff --git a/spec/lib/mattermost/team_spec.rb b/spec/lib/mattermost/team_spec.rb
index ac493fdb20f7aa224bbb14f9cf5574f4afdebc56..e638ad7a2c9413a54a8728b9f0072dae987fcf45 100644
--- a/spec/lib/mattermost/team_spec.rb
+++ b/spec/lib/mattermost/team_spec.rb
@@ -4,8 +4,8 @@ describe Mattermost::Team do
   before do
     Mattermost::Session.base_uri('http://mattermost.example.com')
 
-    allow_any_instance_of(Mattermost::Client).to receive(:with_session).
-      and_yield(Mattermost::Session.new(nil))
+    allow_any_instance_of(Mattermost::Client).to receive(:with_session)
+      .and_yield(Mattermost::Session.new(nil))
   end
 
   describe '#all' do
@@ -30,8 +30,8 @@ describe Mattermost::Team do
       end
 
       before do
-        stub_request(:get, 'http://mattermost.example.com/api/v3/teams/all').
-          to_return(
+        stub_request(:get, 'http://mattermost.example.com/api/v3/teams/all')
+          .to_return(
             status: 200,
             headers: { 'Content-Type' => 'application/json' },
             body: response.to_json
@@ -45,8 +45,8 @@ describe Mattermost::Team do
 
     context 'for error message' do
       before do
-        stub_request(:get, 'http://mattermost.example.com/api/v3/teams/all').
-          to_return(
+        stub_request(:get, 'http://mattermost.example.com/api/v3/teams/all')
+          .to_return(
             status: 500,
             headers: { 'Content-Type' => 'application/json' },
             body: {
diff --git a/spec/mailers/abuse_report_mailer_spec.rb b/spec/mailers/abuse_report_mailer_spec.rb
index eb433c38873e7279d498c0841438e68f04f7f7ce..bda892083b377febc277987dd6bf2221379ff9c5 100644
--- a/spec/mailers/abuse_report_mailer_spec.rb
+++ b/spec/mailers/abuse_report_mailer_spec.rb
@@ -30,8 +30,8 @@ describe AbuseReportMailer do
       it 'returns early' do
         stub_application_setting(admin_notification_email: nil)
 
-        expect { described_class.notify(spy).deliver_now }.
-          not_to change { ActionMailer::Base.deliveries.count }
+        expect { described_class.notify(spy).deliver_now }
+          .not_to change { ActionMailer::Base.deliveries.count }
       end
     end
   end
diff --git a/spec/migrations/migrate_build_stage_reference_spec.rb b/spec/migrations/migrate_build_stage_reference_again_spec.rb
similarity index 93%
rename from spec/migrations/migrate_build_stage_reference_spec.rb
rename to spec/migrations/migrate_build_stage_reference_again_spec.rb
index 80b321860c22c945e78970d11bd9fe13f100c9c3..6be480ce58efd5fe17be9ac1ba4a3055704d8fa7 100644
--- a/spec/migrations/migrate_build_stage_reference_spec.rb
+++ b/spec/migrations/migrate_build_stage_reference_again_spec.rb
@@ -1,7 +1,7 @@
 require 'spec_helper'
-require Rails.root.join('db', 'post_migrate', '20170526185921_migrate_build_stage_reference.rb')
+require Rails.root.join('db', 'post_migrate', '20170526190000_migrate_build_stage_reference_again.rb')
 
-describe MigrateBuildStageReference, :migration do
+describe MigrateBuildStageReferenceAgain, :migration do
   ##
   # Create test data - pipeline and CI/CD jobs.
   #
diff --git a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
index 3db57595fa6fae1d74c30689ffbd63899a1f30f0..4223d2337a80432552e8545cd7422805f13d53ef 100644
--- a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
+++ b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
@@ -11,33 +11,33 @@ describe MigrateProcessCommitWorkerJobs do
   describe 'Project' do
     describe 'find_including_path' do
       it 'returns Project instances' do
-        expect(described_class::Project.find_including_path(project.id)).
-          to be_an_instance_of(described_class::Project)
+        expect(described_class::Project.find_including_path(project.id))
+          .to be_an_instance_of(described_class::Project)
       end
 
       it 'selects the full path for every Project' do
-        migration_project = described_class::Project.
-          find_including_path(project.id)
+        migration_project = described_class::Project
+          .find_including_path(project.id)
 
-        expect(migration_project[:path_with_namespace]).
-          to eq(project.path_with_namespace)
+        expect(migration_project[:path_with_namespace])
+          .to eq(project.path_with_namespace)
       end
     end
 
     describe '#repository_storage_path' do
       it 'returns the storage path for the repository' do
-        migration_project = described_class::Project.
-          find_including_path(project.id)
+        migration_project = described_class::Project
+          .find_including_path(project.id)
 
-        expect(File.directory?(migration_project.repository_storage_path)).
-          to eq(true)
+        expect(File.directory?(migration_project.repository_storage_path))
+          .to eq(true)
       end
     end
 
     describe '#repository_path' do
       it 'returns the path to the repository' do
-        migration_project = described_class::Project.
-          find_including_path(project.id)
+        migration_project = described_class::Project
+          .find_including_path(project.id)
 
         expect(File.directory?(migration_project.repository_path)).to eq(true)
       end
@@ -45,11 +45,11 @@ describe MigrateProcessCommitWorkerJobs do
 
     describe '#repository' do
       it 'returns a Rugged::Repository' do
-        migration_project = described_class::Project.
-          find_including_path(project.id)
+        migration_project = described_class::Project
+          .find_including_path(project.id)
 
-        expect(migration_project.repository).
-          to be_an_instance_of(Rugged::Repository)
+        expect(migration_project.repository)
+          .to be_an_instance_of(Rugged::Repository)
       end
     end
   end
@@ -73,9 +73,9 @@ describe MigrateProcessCommitWorkerJobs do
     end
 
     it 'skips jobs using a project that no longer exists' do
-      allow(described_class::Project).to receive(:find_including_path).
-        with(project.id).
-        and_return(nil)
+      allow(described_class::Project).to receive(:find_including_path)
+        .with(project.id)
+        .and_return(nil)
 
       migration.up
 
@@ -83,9 +83,9 @@ describe MigrateProcessCommitWorkerJobs do
     end
 
     it 'skips jobs using commits that no longer exist' do
-      allow_any_instance_of(Rugged::Repository).to receive(:lookup).
-        with(commit.oid).
-        and_raise(Rugged::OdbError)
+      allow_any_instance_of(Rugged::Repository).to receive(:lookup)
+        .with(commit.oid)
+        .and_raise(Rugged::OdbError)
 
       migration.up
 
@@ -99,12 +99,12 @@ describe MigrateProcessCommitWorkerJobs do
     end
 
     it 'encodes data to UTF-8' do
-      allow_any_instance_of(Rugged::Repository).to receive(:lookup).
-        with(commit.oid).
-        and_return(commit)
+      allow_any_instance_of(Rugged::Repository).to receive(:lookup)
+        .with(commit.oid)
+        .and_return(commit)
 
-      allow(commit).to receive(:message).
-        and_return('김치'.force_encoding('BINARY'))
+      allow(commit).to receive(:message)
+        .and_return('김치'.force_encoding('BINARY'))
 
       migration.up
 
diff --git a/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb b/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb
index 175bf1876b2c207f7a598650481647047ff3dbed..42109fd074396dd0e9d7f09014ea55a363c13ce1 100644
--- a/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb
+++ b/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb
@@ -31,8 +31,8 @@ describe TurnNestedGroupsIntoRegularGroupsForMysql do
     end
 
     it 'adds members of parent groups as members to the migrated group' do
-      is_member = child_group.members.
-        where(user_id: member, access_level: Gitlab::Access::DEVELOPER).any?
+      is_member = child_group.members
+        .where(user_id: member, access_level: Gitlab::Access::DEVELOPER).any?
 
       expect(is_member).to eq(true)
     end
@@ -44,21 +44,21 @@ describe TurnNestedGroupsIntoRegularGroupsForMysql do
     end
 
     it 'renames projects of the nested group' do
-      expect(updated_project.path_with_namespace).
-        to eq("#{parent_group.name}-#{child_group.name}/#{updated_project.path}")
+      expect(updated_project.path_with_namespace)
+        .to eq("#{parent_group.name}-#{child_group.name}/#{updated_project.path}")
     end
 
     it 'renames the repository of any projects' do
-      expect(updated_project.repository.path).
-        to end_with("#{parent_group.name}-#{child_group.name}/#{updated_project.path}.git")
+      expect(updated_project.repository.path)
+        .to end_with("#{parent_group.name}-#{child_group.name}/#{updated_project.path}.git")
 
       expect(File.directory?(updated_project.repository.path)).to eq(true)
     end
 
     it 'creates a redirect route for renamed projects' do
-      exists = RedirectRoute.
-        where(source_type: 'Project', source_id: project.id).
-        any?
+      exists = RedirectRoute
+        .where(source_type: 'Project', source_id: project.id)
+        .any?
 
       expect(exists).to eq(true)
     end
diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb
index 92d70cfc64c199f96a675de3314db0454792e569..090f9e70c503687c55aaff63319a30983bade195 100644
--- a/spec/models/ability_spec.rb
+++ b/spec/models/ability_spec.rb
@@ -69,8 +69,8 @@ describe Ability, lib: true do
         project = create(:empty_project, :public)
         user = build(:user)
 
-        expect(described_class.users_that_can_read_project([user], project)).
-          to eq([user])
+        expect(described_class.users_that_can_read_project([user], project))
+          .to eq([user])
       end
     end
 
@@ -80,8 +80,8 @@ describe Ability, lib: true do
       it 'returns users that are administrators' do
         user = build(:user, admin: true)
 
-        expect(described_class.users_that_can_read_project([user], project)).
-          to eq([user])
+        expect(described_class.users_that_can_read_project([user], project))
+          .to eq([user])
       end
 
       it 'returns internal users while skipping external users' do
@@ -89,8 +89,8 @@ describe Ability, lib: true do
         user2 = build(:user, external: true)
         users = [user1, user2]
 
-        expect(described_class.users_that_can_read_project(users, project)).
-          to eq([user1])
+        expect(described_class.users_that_can_read_project(users, project))
+          .to eq([user1])
       end
 
       it 'returns external users if they are the project owner' do
@@ -100,8 +100,8 @@ describe Ability, lib: true do
 
         expect(project).to receive(:owner).twice.and_return(user1)
 
-        expect(described_class.users_that_can_read_project(users, project)).
-          to eq([user1])
+        expect(described_class.users_that_can_read_project(users, project))
+          .to eq([user1])
       end
 
       it 'returns external users if they are project members' do
@@ -111,8 +111,8 @@ describe Ability, lib: true do
 
         expect(project.team).to receive(:members).twice.and_return([user1])
 
-        expect(described_class.users_that_can_read_project(users, project)).
-          to eq([user1])
+        expect(described_class.users_that_can_read_project(users, project))
+          .to eq([user1])
       end
 
       it 'returns an empty Array if all users are external users without access' do
@@ -120,8 +120,8 @@ describe Ability, lib: true do
         user2 = build(:user, external: true)
         users = [user1, user2]
 
-        expect(described_class.users_that_can_read_project(users, project)).
-          to eq([])
+        expect(described_class.users_that_can_read_project(users, project))
+          .to eq([])
       end
     end
 
@@ -131,8 +131,8 @@ describe Ability, lib: true do
       it 'returns users that are administrators' do
         user = build(:user, admin: true)
 
-        expect(described_class.users_that_can_read_project([user], project)).
-          to eq([user])
+        expect(described_class.users_that_can_read_project([user], project))
+          .to eq([user])
       end
 
       it 'returns external users if they are the project owner' do
@@ -142,8 +142,8 @@ describe Ability, lib: true do
 
         expect(project).to receive(:owner).twice.and_return(user1)
 
-        expect(described_class.users_that_can_read_project(users, project)).
-          to eq([user1])
+        expect(described_class.users_that_can_read_project(users, project))
+          .to eq([user1])
       end
 
       it 'returns external users if they are project members' do
@@ -153,8 +153,8 @@ describe Ability, lib: true do
 
         expect(project.team).to receive(:members).twice.and_return([user1])
 
-        expect(described_class.users_that_can_read_project(users, project)).
-          to eq([user1])
+        expect(described_class.users_that_can_read_project(users, project))
+          .to eq([user1])
       end
 
       it 'returns an empty Array if all users are internal users without access' do
@@ -162,8 +162,8 @@ describe Ability, lib: true do
         user2 = build(:user)
         users = [user1, user2]
 
-        expect(described_class.users_that_can_read_project(users, project)).
-          to eq([])
+        expect(described_class.users_that_can_read_project(users, project))
+          .to eq([])
       end
 
       it 'returns an empty Array if all users are external users without access' do
@@ -171,8 +171,8 @@ describe Ability, lib: true do
         user2 = build(:user, external: true)
         users = [user1, user2]
 
-        expect(described_class.users_that_can_read_project(users, project)).
-          to eq([])
+        expect(described_class.users_that_can_read_project(users, project))
+          .to eq([])
       end
     end
   end
@@ -210,8 +210,8 @@ describe Ability, lib: true do
         user = build(:user, admin: true)
         issue = build(:issue)
 
-        expect(described_class.issues_readable_by_user([issue], user)).
-          to eq([issue])
+        expect(described_class.issues_readable_by_user([issue], user))
+          .to eq([issue])
       end
     end
 
@@ -222,8 +222,8 @@ describe Ability, lib: true do
 
         expect(issue).to receive(:readable_by?).with(user).and_return(true)
 
-        expect(described_class.issues_readable_by_user([issue], user)).
-          to eq([issue])
+        expect(described_class.issues_readable_by_user([issue], user))
+          .to eq([issue])
       end
 
       it 'returns an empty Array when no issues are readable' do
@@ -244,8 +244,8 @@ describe Ability, lib: true do
         expect(hidden_issue).to receive(:publicly_visible?).and_return(false)
         expect(visible_issue).to receive(:publicly_visible?).and_return(true)
 
-        issues = described_class.
-          issues_readable_by_user([hidden_issue, visible_issue])
+        issues = described_class
+          .issues_readable_by_user([hidden_issue, visible_issue])
 
         expect(issues).to eq([visible_issue])
       end
diff --git a/spec/models/abuse_report_spec.rb b/spec/models/abuse_report_spec.rb
index 90aec2b45e6870b399162357e11ee9f8e9934c55..c1bf5551fe032103bdbc3bb3bac9fb124e836614 100644
--- a/spec/models/abuse_report_spec.rb
+++ b/spec/models/abuse_report_spec.rb
@@ -36,8 +36,8 @@ RSpec.describe AbuseReport, type: :model do
 
   describe '#notify' do
     it 'delivers' do
-      expect(AbuseReportMailer).to receive(:notify).with(subject.id).
-        and_return(spy)
+      expect(AbuseReportMailer).to receive(:notify).with(subject.id)
+        .and_return(spy)
 
       subject.notify
     end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 3816422fec6e1b4fd4b0220ad25ca71475b2bbc7..488697f74ebd5bb54b39b0ad9770d78fe38c447e 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -451,42 +451,6 @@ describe Ci::Build, :models do
       end
     end
 
-    describe '#environment_url' do
-      subject { job.environment_url }
-
-      context 'when yaml environment uses $CI_COMMIT_REF_NAME' do
-        let(:job) do
-          create(:ci_build,
-                 ref: 'master',
-                 options: { environment: { url: 'http://review/$CI_COMMIT_REF_NAME' } })
-        end
-
-        it { is_expected.to eq('http://review/master') }
-      end
-
-      context 'when yaml environment uses yaml_variables containing symbol keys' do
-        let(:job) do
-          create(:ci_build,
-                 yaml_variables: [{ key: :APP_HOST, value: 'host' }],
-                 options: { environment: { url: 'http://review/$APP_HOST' } })
-        end
-
-        it { is_expected.to eq('http://review/host') }
-      end
-
-      context 'when yaml environment does not have url' do
-        let(:job) { create(:ci_build, environment: 'staging') }
-
-        let!(:environment) do
-          create(:environment, project: job.project, name: job.environment)
-        end
-
-        it 'returns the external_url from persisted environment' do
-          is_expected.to eq(environment.external_url)
-        end
-      end
-    end
-
     describe '#starts_environment?' do
       subject { build.starts_environment? }
 
@@ -899,8 +863,8 @@ describe Ci::Build, :models do
         pipeline2 = create(:ci_pipeline, project: project)
         @build2 = create(:ci_build, pipeline: pipeline2)
 
-        allow(@merge_request).to receive(:commits_sha).
-          and_return([pipeline.sha, pipeline2.sha])
+        allow(@merge_request).to receive(:commits_sha)
+          .and_return([pipeline.sha, pipeline2.sha])
         allow(MergeRequest).to receive_message_chain(:includes, :where, :reorder).and_return([@merge_request])
       end
 
@@ -1292,10 +1256,20 @@ describe Ci::Build, :models do
 
         context 'when the URL was set from the job' do
           before do
-            build.update(options: { environment: { url: 'http://host/$CI_JOB_NAME' } })
+            build.update(options: { environment: { url: url } })
           end
 
           it_behaves_like 'containing environment variables'
+
+          context 'when variables are used in the URL, it does not expand' do
+            let(:url) { 'http://$CI_PROJECT_NAME-$CI_ENVIRONMENT_SLUG' }
+
+            it_behaves_like 'containing environment variables'
+
+            it 'puts $CI_ENVIRONMENT_URL in the last so all other variables are available to be used when runners are trying to expand it' do
+              expect(subject.last).to eq(environment_variables.last)
+            end
+          end
         end
 
         context 'when the URL was not set from the job, but environment' do
diff --git a/spec/models/ci/pipeline_schedule_spec.rb b/spec/models/ci/pipeline_schedule_spec.rb
index b00e7a735712fb29396e7789e15e19d8bdd22651..56817baf79d65d679a0da8b1e9c729e2c6e0f7b5 100644
--- a/spec/models/ci/pipeline_schedule_spec.rb
+++ b/spec/models/ci/pipeline_schedule_spec.rb
@@ -40,8 +40,8 @@ describe Ci::PipelineSchedule, models: true do
 
     context 'when creates new pipeline schedule' do
       let(:expected_next_run_at) do
-        Gitlab::Ci::CronParser.new(pipeline_schedule.cron, pipeline_schedule.cron_timezone).
-          next_time_from(Time.now)
+        Gitlab::Ci::CronParser.new(pipeline_schedule.cron, pipeline_schedule.cron_timezone)
+          .next_time_from(Time.now)
       end
 
       it 'updates next_run_at automatically' do
@@ -53,8 +53,8 @@ describe Ci::PipelineSchedule, models: true do
       let(:new_cron) { '0 0 1 1 *' }
 
       let(:expected_next_run_at) do
-        Gitlab::Ci::CronParser.new(new_cron, pipeline_schedule.cron_timezone).
-          next_time_from(Time.now)
+        Gitlab::Ci::CronParser.new(new_cron, pipeline_schedule.cron_timezone)
+          .next_time_from(Time.now)
       end
 
       it 'updates next_run_at automatically' do
@@ -72,8 +72,8 @@ describe Ci::PipelineSchedule, models: true do
       let(:future_time) { 10.days.from_now }
 
       let(:expected_next_run_at) do
-        Gitlab::Ci::CronParser.new(pipeline_schedule.cron, pipeline_schedule.cron_timezone).
-          next_time_from(future_time)
+        Gitlab::Ci::CronParser.new(pipeline_schedule.cron, pipeline_schedule.cron_timezone)
+          .next_time_from(future_time)
       end
 
       it 'points to proper next_run_at' do
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index e86cbe8498a5b72d95f3a15dc326fbd8f8e315e7..dab8e8ca43231813f2553ad101cb83bc956b3b2f 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -608,8 +608,8 @@ describe Ci::Pipeline, models: true do
 
       it 'returns the latest pipeline for the same ref and different sha' do
         expect(pipelines.map(&:sha)).to contain_exactly('A', 'B', 'C')
-        expect(pipelines.map(&:status)).
-          to contain_exactly('success', 'failed', 'skipped')
+        expect(pipelines.map(&:status))
+          .to contain_exactly('success', 'failed', 'skipped')
       end
     end
 
@@ -618,8 +618,8 @@ describe Ci::Pipeline, models: true do
 
       it 'returns the latest pipeline for ref and different sha' do
         expect(pipelines.map(&:sha)).to contain_exactly('A', 'B')
-        expect(pipelines.map(&:status)).
-          to contain_exactly('success', 'failed')
+        expect(pipelines.map(&:status))
+          .to contain_exactly('success', 'failed')
       end
     end
   end
@@ -654,8 +654,8 @@ describe Ci::Pipeline, models: true do
     end
 
     it 'returns the latest successful pipeline' do
-      expect(described_class.latest_successful_for('ref')).
-        to eq(latest_successful_pipeline)
+      expect(described_class.latest_successful_for('ref'))
+        .to eq(latest_successful_pipeline)
     end
   end
 
@@ -1201,8 +1201,8 @@ describe Ci::Pipeline, models: true do
     before do
       project.team << [pipeline.user, Gitlab::Access::DEVELOPER]
 
-      pipeline.user.global_notification_setting.
-        update(level: 'custom', failed_pipeline: true, success_pipeline: true)
+      pipeline.user.global_notification_setting
+        .update(level: 'custom', failed_pipeline: true, success_pipeline: true)
 
       reset_delivered_emails!
 
diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb
index 077b10227d7357960c8c1775a8ec65ea8de09b10..83494af24baf4b800aed6fb996aaa4994d2dc194 100644
--- a/spec/models/ci/variable_spec.rb
+++ b/spec/models/ci/variable_spec.rb
@@ -54,8 +54,8 @@ describe Ci::Variable, models: true do
     it 'fails to decrypt if iv is incorrect' do
       subject.encrypted_value_iv = SecureRandom.hex
       subject.instance_variable_set(:@value, nil)
-      expect { subject.value }.
-        to raise_error(OpenSSL::Cipher::CipherError, 'bad decrypt')
+      expect { subject.value }
+        .to raise_error(OpenSSL::Cipher::CipherError, 'bad decrypt')
     end
   end
 
diff --git a/spec/models/commit_range_spec.rb b/spec/models/commit_range_spec.rb
index e4bddf670964e5101629134fe607cf36b4cecc49..ba9c3f66d213a196dcfb8924d5b8a69d2aca60d9 100644
--- a/spec/models/commit_range_spec.rb
+++ b/spec/models/commit_range_spec.rb
@@ -147,9 +147,9 @@ describe CommitRange, models: true do
              note: commit1.revert_description(user),
              project: issue.project)
 
-      expect_any_instance_of(Commit).to receive(:reverts_commit?).
-        with(commit1, user).
-        and_return(true)
+      expect_any_instance_of(Commit).to receive(:reverts_commit?)
+        .with(commit1, user)
+        .and_return(true)
 
       expect(commit1.has_been_reverted?(user, issue)).to eq(true)
     end
diff --git a/spec/models/concerns/case_sensitivity_spec.rb b/spec/models/concerns/case_sensitivity_spec.rb
index 92fdc5cd65da4008f691869bdbd8ea87649dc077..a6fccb668e3a8762a378a3a9eeb2070d82b1ab71 100644
--- a/spec/models/concerns/case_sensitivity_spec.rb
+++ b/spec/models/concerns/case_sensitivity_spec.rb
@@ -15,13 +15,13 @@ describe CaseSensitivity, models: true do
         it 'returns the criteria for a column and a value' do
           criteria = double(:criteria)
 
-          expect(connection).to receive(:quote_table_name).
-            with(:foo).
-            and_return('"foo"')
+          expect(connection).to receive(:quote_table_name)
+            .with(:foo)
+            .and_return('"foo"')
 
-          expect(model).to receive(:where).
-            with(%q{LOWER("foo") = LOWER(:value)}, value: 'bar').
-            and_return(criteria)
+          expect(model).to receive(:where)
+            .with(%q{LOWER("foo") = LOWER(:value)}, value: 'bar')
+            .and_return(criteria)
 
           expect(model.iwhere(foo: 'bar')).to eq(criteria)
         end
@@ -29,13 +29,13 @@ describe CaseSensitivity, models: true do
         it 'returns the criteria for a column with a table, and a value' do
           criteria = double(:criteria)
 
-          expect(connection).to receive(:quote_table_name).
-            with(:'foo.bar').
-            and_return('"foo"."bar"')
+          expect(connection).to receive(:quote_table_name)
+            .with(:'foo.bar')
+            .and_return('"foo"."bar"')
 
-          expect(model).to receive(:where).
-            with(%q{LOWER("foo"."bar") = LOWER(:value)}, value: 'bar').
-            and_return(criteria)
+          expect(model).to receive(:where)
+            .with(%q{LOWER("foo"."bar") = LOWER(:value)}, value: 'bar')
+            .and_return(criteria)
 
           expect(model.iwhere('foo.bar'.to_sym => 'bar')).to eq(criteria)
         end
@@ -46,21 +46,21 @@ describe CaseSensitivity, models: true do
           initial = double(:criteria)
           final   = double(:criteria)
 
-          expect(connection).to receive(:quote_table_name).
-            with(:foo).
-            and_return('"foo"')
+          expect(connection).to receive(:quote_table_name)
+            .with(:foo)
+            .and_return('"foo"')
 
-          expect(connection).to receive(:quote_table_name).
-            with(:bar).
-            and_return('"bar"')
+          expect(connection).to receive(:quote_table_name)
+            .with(:bar)
+            .and_return('"bar"')
 
-          expect(model).to receive(:where).
-            with(%q{LOWER("foo") = LOWER(:value)}, value: 'bar').
-            and_return(initial)
+          expect(model).to receive(:where)
+            .with(%q{LOWER("foo") = LOWER(:value)}, value: 'bar')
+            .and_return(initial)
 
-          expect(initial).to receive(:where).
-            with(%q{LOWER("bar") = LOWER(:value)}, value: 'baz').
-            and_return(final)
+          expect(initial).to receive(:where)
+            .with(%q{LOWER("bar") = LOWER(:value)}, value: 'baz')
+            .and_return(final)
 
           got = model.iwhere(foo: 'bar', bar: 'baz')
 
@@ -71,21 +71,21 @@ describe CaseSensitivity, models: true do
           initial = double(:criteria)
           final   = double(:criteria)
 
-          expect(connection).to receive(:quote_table_name).
-            with(:'foo.bar').
-            and_return('"foo"."bar"')
+          expect(connection).to receive(:quote_table_name)
+            .with(:'foo.bar')
+            .and_return('"foo"."bar"')
 
-          expect(connection).to receive(:quote_table_name).
-            with(:'foo.baz').
-            and_return('"foo"."baz"')
+          expect(connection).to receive(:quote_table_name)
+            .with(:'foo.baz')
+            .and_return('"foo"."baz"')
 
-          expect(model).to receive(:where).
-            with(%q{LOWER("foo"."bar") = LOWER(:value)}, value: 'bar').
-            and_return(initial)
+          expect(model).to receive(:where)
+            .with(%q{LOWER("foo"."bar") = LOWER(:value)}, value: 'bar')
+            .and_return(initial)
 
-          expect(initial).to receive(:where).
-            with(%q{LOWER("foo"."baz") = LOWER(:value)}, value: 'baz').
-            and_return(final)
+          expect(initial).to receive(:where)
+            .with(%q{LOWER("foo"."baz") = LOWER(:value)}, value: 'baz')
+            .and_return(final)
 
           got = model.iwhere('foo.bar'.to_sym => 'bar',
                              'foo.baz'.to_sym => 'baz')
@@ -105,13 +105,13 @@ describe CaseSensitivity, models: true do
         it 'returns the criteria for a column and a value' do
           criteria = double(:criteria)
 
-          expect(connection).to receive(:quote_table_name).
-            with(:foo).
-            and_return('`foo`')
+          expect(connection).to receive(:quote_table_name)
+            .with(:foo)
+            .and_return('`foo`')
 
-          expect(model).to receive(:where).
-            with(%q{`foo` = :value}, value: 'bar').
-            and_return(criteria)
+          expect(model).to receive(:where)
+            .with(%q{`foo` = :value}, value: 'bar')
+            .and_return(criteria)
 
           expect(model.iwhere(foo: 'bar')).to eq(criteria)
         end
@@ -119,16 +119,16 @@ describe CaseSensitivity, models: true do
         it 'returns the criteria for a column with a table, and a value' do
           criteria = double(:criteria)
 
-          expect(connection).to receive(:quote_table_name).
-            with(:'foo.bar').
-            and_return('`foo`.`bar`')
+          expect(connection).to receive(:quote_table_name)
+            .with(:'foo.bar')
+            .and_return('`foo`.`bar`')
 
-          expect(model).to receive(:where).
-            with(%q{`foo`.`bar` = :value}, value: 'bar').
-            and_return(criteria)
+          expect(model).to receive(:where)
+            .with(%q{`foo`.`bar` = :value}, value: 'bar')
+            .and_return(criteria)
 
-          expect(model.iwhere('foo.bar'.to_sym => 'bar')).
-            to eq(criteria)
+          expect(model.iwhere('foo.bar'.to_sym => 'bar'))
+            .to eq(criteria)
         end
       end
 
@@ -137,21 +137,21 @@ describe CaseSensitivity, models: true do
           initial = double(:criteria)
           final   = double(:criteria)
 
-          expect(connection).to receive(:quote_table_name).
-            with(:foo).
-            and_return('`foo`')
+          expect(connection).to receive(:quote_table_name)
+            .with(:foo)
+            .and_return('`foo`')
 
-          expect(connection).to receive(:quote_table_name).
-            with(:bar).
-            and_return('`bar`')
+          expect(connection).to receive(:quote_table_name)
+            .with(:bar)
+            .and_return('`bar`')
 
-          expect(model).to receive(:where).
-            with(%q{`foo` = :value}, value: 'bar').
-            and_return(initial)
+          expect(model).to receive(:where)
+            .with(%q{`foo` = :value}, value: 'bar')
+            .and_return(initial)
 
-          expect(initial).to receive(:where).
-            with(%q{`bar` = :value}, value: 'baz').
-            and_return(final)
+          expect(initial).to receive(:where)
+            .with(%q{`bar` = :value}, value: 'baz')
+            .and_return(final)
 
           got = model.iwhere(foo: 'bar', bar: 'baz')
 
@@ -162,21 +162,21 @@ describe CaseSensitivity, models: true do
           initial = double(:criteria)
           final   = double(:criteria)
 
-          expect(connection).to receive(:quote_table_name).
-            with(:'foo.bar').
-            and_return('`foo`.`bar`')
+          expect(connection).to receive(:quote_table_name)
+            .with(:'foo.bar')
+            .and_return('`foo`.`bar`')
 
-          expect(connection).to receive(:quote_table_name).
-            with(:'foo.baz').
-            and_return('`foo`.`baz`')
+          expect(connection).to receive(:quote_table_name)
+            .with(:'foo.baz')
+            .and_return('`foo`.`baz`')
 
-          expect(model).to receive(:where).
-            with(%q{`foo`.`bar` = :value}, value: 'bar').
-            and_return(initial)
+          expect(model).to receive(:where)
+            .with(%q{`foo`.`bar` = :value}, value: 'bar')
+            .and_return(initial)
 
-          expect(initial).to receive(:where).
-            with(%q{`foo`.`baz` = :value}, value: 'baz').
-            and_return(final)
+          expect(initial).to receive(:where)
+            .with(%q{`foo`.`baz` = :value}, value: 'baz')
+            .and_return(final)
 
           got = model.iwhere('foo.bar'.to_sym => 'bar',
                              'foo.baz'.to_sym => 'baz')
diff --git a/spec/models/concerns/has_status_spec.rb b/spec/models/concerns/has_status_spec.rb
index 67dae7cf4c0d0beaed9c67ff534f34fe16d680b7..101567998c9a99113e68197229d21f327282a908 100644
--- a/spec/models/concerns/has_status_spec.rb
+++ b/spec/models/concerns/has_status_spec.rb
@@ -168,8 +168,8 @@ describe HasStatus do
 
           describe ".#{status}" do
             it 'contains the job' do
-              expect(CommitStatus.public_send(status).all).
-                to contain_exactly(job)
+              expect(CommitStatus.public_send(status).all)
+                .to contain_exactly(job)
             end
           end
 
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 1a9bda64191841eadd70b2751c2ef64316f9c7cc..ac9303370abc0bd7dabaee30f5845a4895959ee8 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -69,8 +69,8 @@ describe Issuable do
     let!(:searchable_issue) { create(:issue, title: "Searchable issue") }
 
     it 'returns notes with a matching title' do
-      expect(issuable_class.search(searchable_issue.title)).
-        to eq([searchable_issue])
+      expect(issuable_class.search(searchable_issue.title))
+        .to eq([searchable_issue])
     end
 
     it 'returns notes with a partially matching title' do
@@ -78,8 +78,8 @@ describe Issuable do
     end
 
     it 'returns notes with a matching title regardless of the casing' do
-      expect(issuable_class.search(searchable_issue.title.upcase)).
-        to eq([searchable_issue])
+      expect(issuable_class.search(searchable_issue.title.upcase))
+        .to eq([searchable_issue])
     end
   end
 
@@ -89,8 +89,8 @@ describe Issuable do
     end
 
     it 'returns notes with a matching title' do
-      expect(issuable_class.full_search(searchable_issue.title)).
-        to eq([searchable_issue])
+      expect(issuable_class.full_search(searchable_issue.title))
+        .to eq([searchable_issue])
     end
 
     it 'returns notes with a partially matching title' do
@@ -98,23 +98,23 @@ describe Issuable do
     end
 
     it 'returns notes with a matching title regardless of the casing' do
-      expect(issuable_class.full_search(searchable_issue.title.upcase)).
-        to eq([searchable_issue])
+      expect(issuable_class.full_search(searchable_issue.title.upcase))
+        .to eq([searchable_issue])
     end
 
     it 'returns notes with a matching description' do
-      expect(issuable_class.full_search(searchable_issue.description)).
-        to eq([searchable_issue])
+      expect(issuable_class.full_search(searchable_issue.description))
+        .to eq([searchable_issue])
     end
 
     it 'returns notes with a partially matching description' do
-      expect(issuable_class.full_search(searchable_issue.description)).
-        to eq([searchable_issue])
+      expect(issuable_class.full_search(searchable_issue.description))
+        .to eq([searchable_issue])
     end
 
     it 'returns notes with a matching description regardless of the casing' do
-      expect(issuable_class.full_search(searchable_issue.description.upcase)).
-        to eq([searchable_issue])
+      expect(issuable_class.full_search(searchable_issue.description.upcase))
+        .to eq([searchable_issue])
     end
   end
 
diff --git a/spec/models/concerns/milestoneish_spec.rb b/spec/models/concerns/milestoneish_spec.rb
index 675b730c5575ab80a4a8aa2c4a9d2fe8d9325a85..cefe7fb6feadf6698ca06a86d4a679a599038a10 100644
--- a/spec/models/concerns/milestoneish_spec.rb
+++ b/spec/models/concerns/milestoneish_spec.rb
@@ -19,12 +19,43 @@ describe Milestone, 'Milestoneish' do
   let!(:closed_security_issue_3) { create(:issue, :confidential, :closed, project: project, author: author, milestone: milestone) }
   let!(:closed_security_issue_4) { create(:issue, :confidential, :closed, project: project, assignees: [assignee], milestone: milestone) }
   let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, milestone: milestone) }
+  let(:label_1) { create(:label, title: 'label_1', project: project, priority: 1) }
+  let(:label_2) { create(:label, title: 'label_2', project: project, priority: 2) }
+  let(:label_3) { create(:label, title: 'label_3', project: project) }
 
   before do
     project.team << [member, :developer]
     project.team << [guest, :guest]
   end
 
+  describe '#sorted_issues' do
+    it 'sorts issues by label priority' do
+      issue.labels << label_1
+      security_issue_1.labels << label_2
+      closed_issue_1.labels << label_3
+
+      issues = milestone.sorted_issues(member)
+
+      expect(issues.first).to eq(issue)
+      expect(issues.second).to eq(security_issue_1)
+      expect(issues.third).not_to eq(closed_issue_1)
+    end
+  end
+
+  describe '#sorted_merge_requests' do
+    it 'sorts merge requests by label priority' do
+      merge_request_1 = create(:labeled_merge_request, labels: [label_2], source_project: project, source_branch: 'branch_1', milestone: milestone)
+      merge_request_2 = create(:labeled_merge_request, labels: [label_1], source_project: project, source_branch: 'branch_2', milestone: milestone)
+      merge_request_3 = create(:labeled_merge_request, labels: [label_3], source_project: project, source_branch: 'branch_3', milestone: milestone)
+
+      merge_requests = milestone.sorted_merge_requests
+
+      expect(merge_requests.first).to eq(merge_request_2)
+      expect(merge_requests.second).to eq(merge_request_1)
+      expect(merge_requests.third).to eq(merge_request_3)
+    end
+  end
+
   describe '#closed_items_count' do
     it 'does not count confidential issues for non project members' do
       expect(milestone.closed_items_count(non_member)).to eq 2
diff --git a/spec/models/concerns/resolvable_discussion_spec.rb b/spec/models/concerns/resolvable_discussion_spec.rb
index 18327fe262db2e4e6fe1d3a53a6e9d98ec3cc2ec..3934992c14333ab6601ad7f80fe87b6361c1e9d7 100644
--- a/spec/models/concerns/resolvable_discussion_spec.rb
+++ b/spec/models/concerns/resolvable_discussion_spec.rb
@@ -306,22 +306,22 @@ describe Discussion, ResolvableDiscussion, models: true do
         it "doesn't change resolved_at on the resolved note" do
           expect(first_note.resolved_at).not_to be_nil
 
-          expect { subject.resolve!(current_user) }.
-            not_to change { first_note.reload.resolved_at }
+          expect { subject.resolve!(current_user) }
+            .not_to change { first_note.reload.resolved_at }
         end
 
         it "doesn't change resolved_by on the resolved note" do
           expect(first_note.resolved_by).to eq(user)
 
-          expect { subject.resolve!(current_user) }.
-            not_to change { first_note.reload && first_note.resolved_by }
+          expect { subject.resolve!(current_user) }
+            .not_to change { first_note.reload && first_note.resolved_by }
         end
 
         it "doesn't change the resolved state on the resolved note" do
           expect(first_note.resolved?).to be true
 
-          expect { subject.resolve!(current_user) }.
-            not_to change { first_note.reload && first_note.resolved? }
+          expect { subject.resolve!(current_user) }
+            .not_to change { first_note.reload && first_note.resolved? }
         end
 
         it "sets resolved_at on the unresolved note" do
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index b8cb967c4cc16098d51b6beab2146548fb0180b4..10b9bf9f43a76f735e3952a671aa23ee2afa0664 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -266,8 +266,8 @@ describe Event, models: true do
       it 'does not update the project' do
         project.update(last_activity_at: Time.now)
 
-        expect(project).not_to receive(:update_column).
-          with(:last_activity_at, a_kind_of(Time))
+        expect(project).not_to receive(:update_column)
+          .with(:last_activity_at, a_kind_of(Time))
 
         create_push_event(project, project.owner)
       end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 449b7c2f7d7f2dbd63a703151621aa2b58b16c3c..4de1683b21c4538a9362a773042ee46524b7f43f 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -374,8 +374,8 @@ describe Group, models: true do
       group.add_user(master, GroupMember::MASTER)
       group.add_user(developer, GroupMember::DEVELOPER)
 
-      expect(group.user_ids_for_project_authorizations).
-        to include(master.id, developer.id)
+      expect(group.user_ids_for_project_authorizations)
+        .to include(master.id, developer.id)
     end
   end
 
diff --git a/spec/models/issue_collection_spec.rb b/spec/models/issue_collection_spec.rb
index 93c2c538e1056053c74009a12bce5fda0246d0e4..04d23d4c4fdb73ad13ddc5ff534b1dfb0dc3b8a6 100644
--- a/spec/models/issue_collection_spec.rb
+++ b/spec/models/issue_collection_spec.rb
@@ -50,8 +50,8 @@ describe IssueCollection do
 
     context 'using a user that is the owner of a project' do
       it 'returns the issues of the project' do
-        expect(collection.updatable_by_user(project.namespace.owner)).
-          to eq([issue1, issue2])
+        expect(collection.updatable_by_user(project.namespace.owner))
+          .to eq([issue1, issue2])
       end
     end
   end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 12e7d646382df068be35e1cb098f475d65d09dd9..bf97c6ececd8d6c1f7eb237bfef379a128139b7d 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -33,8 +33,8 @@ describe Issue, models: true do
     let!(:issue4) { create(:issue, project: project, relative_position: 200) }
 
     it 'returns ordered list' do
-      expect(project.issues.order_by_position_and_priority).
-        to match [issue3, issue4, issue1, issue2]
+      expect(project.issues.order_by_position_and_priority)
+        .to match [issue3, issue4, issue1, issue2]
     end
   end
 
@@ -43,16 +43,16 @@ describe Issue, models: true do
       allow(subject).to receive(:author).and_return(double(name: 'Robert'))
       allow(subject).to receive(:assignees).and_return([])
 
-      expect(subject.card_attributes).
-        to eq({ 'Author' => 'Robert', 'Assignee' => '' })
+      expect(subject.card_attributes)
+        .to eq({ 'Author' => 'Robert', 'Assignee' => '' })
     end
 
     it 'includes the assignee name' do
       allow(subject).to receive(:author).and_return(double(name: 'Robert'))
       allow(subject).to receive(:assignees).and_return([double(name: 'Douwe')])
 
-      expect(subject.card_attributes).
-        to eq({ 'Author' => 'Robert', 'Assignee' => 'Douwe' })
+      expect(subject.card_attributes)
+        .to eq({ 'Author' => 'Robert', 'Assignee' => 'Douwe' })
     end
   end
 
@@ -299,8 +299,8 @@ describe Issue, models: true do
     let(:user) { build(:admin) }
 
     before do
-      allow(subject.project.repository).to receive(:branch_names).
-                                            and_return(["mpempe", "#{subject.iid}mepmep", subject.to_branch_name, "#{subject.iid}-branch"])
+      allow(subject.project.repository).to receive(:branch_names)
+                                            .and_return(["mpempe", "#{subject.iid}mepmep", subject.to_branch_name, "#{subject.iid}-branch"])
 
       # Without this stub, the `create(:merge_request)` above fails because it can't find
       # the source branch. This seems like a reasonable compromise, in comparison with
@@ -322,8 +322,8 @@ describe Issue, models: true do
     end
 
     it 'excludes stable branches from the related branches' do
-      allow(subject.project.repository).to receive(:branch_names).
-        and_return(["#{subject.iid}-0-stable"])
+      allow(subject.project.repository).to receive(:branch_names)
+        .and_return(["#{subject.iid}-0-stable"])
 
       expect(subject.related_branches(user)).to eq []
     end
diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb
index f1e2a2cc518da7141695e7b20447c32778ed0913..f27920f9feb576ab74156feaa786174a54ee7cad 100644
--- a/spec/models/key_spec.rb
+++ b/spec/models/key_spec.rb
@@ -34,8 +34,8 @@ describe Key, models: true do
 
       context 'when key was not updated during the last day' do
         before do
-          allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).
-            and_return('000000')
+          allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain)
+            .and_return('000000')
         end
 
         it 'enqueues a UseKeyWorker job' do
@@ -46,8 +46,8 @@ describe Key, models: true do
 
       context 'when key was updated during the last day' do
         before do
-          allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).
-            and_return(false)
+          allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain)
+            .and_return(false)
         end
 
         it 'does not enqueue a UseKeyWorker job' do
diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb
index 84867e3d96b1fcc7a6f928d8efb8cdfa5c7e7548..31190fe5685e1970f6736e1c0cc7a9b9085f1164 100644
--- a/spec/models/label_spec.rb
+++ b/spec/models/label_spec.rb
@@ -59,8 +59,8 @@ describe Label, models: true do
 
   describe '#text_color' do
     it 'uses default color if color is missing' do
-      expect(LabelsHelper).to receive(:text_color_for_bg).with(Label::DEFAULT_COLOR).
-        and_return(spy)
+      expect(LabelsHelper).to receive(:text_color_for_bg).with(Label::DEFAULT_COLOR)
+        .and_return(spy)
 
       label = described_class.new(color: nil)
 
diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb
index ccc3deac199b0b17601a96ea8d861674cc37f3a6..494a88368ba25931969d5f9ad4f91b9a37774236 100644
--- a/spec/models/member_spec.rb
+++ b/spec/models/member_spec.rb
@@ -83,8 +83,8 @@ describe Member, models: true do
       @accepted_invite_member = create(:project_member, :developer,
                                       project: project,
                                       invite_token: '1234',
-                                      invite_email: 'toto2@example.com').
-                                      tap { |u| u.accept_invite!(accepted_invite_user) }
+                                      invite_email: 'toto2@example.com')
+                                      .tap { |u| u.accept_invite!(accepted_invite_user) }
 
       requested_user = create(:user).tap { |u| project.request_access(u) }
       @requested_member = project.requesters.find_by(user_id: requested_user.id)
@@ -265,8 +265,8 @@ describe Member, models: true do
               expect(source.users).not_to include(user)
               expect(source.requesters.exists?(user_id: user)).to be_truthy
 
-              expect { described_class.add_user(source, user, :master) }.
-                to raise_error(Gitlab::Access::AccessDeniedError)
+              expect { described_class.add_user(source, user, :master) }
+                .to raise_error(Gitlab::Access::AccessDeniedError)
 
               expect(source.users.reload).not_to include(user)
               expect(source.requesters.reload.exists?(user_id: user)).to be_truthy
diff --git a/spec/models/members/group_member_spec.rb b/spec/models/members/group_member_spec.rb
index 17765b25856071fa199ac720af30e4ec340c2bff..37014268a70acf02f42350290c1f16ae59bd1565 100644
--- a/spec/models/members/group_member_spec.rb
+++ b/spec/models/members/group_member_spec.rb
@@ -33,8 +33,8 @@ describe GroupMember, models: true do
       it "sends email to user" do
         membership = build(:group_member)
 
-        allow(membership).to receive(:notification_service).
-          and_return(double('NotificationService').as_null_object)
+        allow(membership).to receive(:notification_service)
+          .and_return(double('NotificationService').as_null_object)
         expect(membership).to receive(:notification_service)
 
         membership.save
@@ -44,8 +44,8 @@ describe GroupMember, models: true do
     describe "#after_update" do
       before do
         @group_member = create :group_member
-        allow(@group_member).to receive(:notification_service).
-          and_return(double('NotificationService').as_null_object)
+        allow(@group_member).to receive(:notification_service)
+          .and_return(double('NotificationService').as_null_object)
       end
 
       it "sends email to user" do
diff --git a/spec/models/merge_request_diff_file_spec.rb b/spec/models/merge_request_diff_file_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7276f5b5061d472ff4620d4ba8069dcbedec1293
--- /dev/null
+++ b/spec/models/merge_request_diff_file_spec.rb
@@ -0,0 +1,11 @@
+require 'rails_helper'
+
+describe MergeRequestDiffFile, type: :model do
+  describe '#utf8_diff' do
+    it 'does not raise error when a hash value is in binary' do
+      subject.diff = "\x05\x00\x68\x65\x6c\x6c\x6f"
+
+      expect { subject.utf8_diff }.not_to raise_error
+    end
+  end
+end
diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb
index 25f7062860b0dc238a9cf62c5767b480091934f7..4ad4abaa57219b14e35433bd39d07281920fa4ac 100644
--- a/spec/models/merge_request_diff_spec.rb
+++ b/spec/models/merge_request_diff_spec.rb
@@ -37,7 +37,7 @@ describe MergeRequestDiff, models: true do
 
     context 'when the raw diffs are empty' do
       before do
-        mr_diff.update_attributes(st_diffs: '')
+        MergeRequestDiffFile.delete_all(merge_request_diff_id: mr_diff.id)
       end
 
       it 'returns an empty DiffCollection' do
@@ -48,6 +48,7 @@ describe MergeRequestDiff, models: true do
 
     context 'when the raw diffs have invalid content' do
       before do
+        MergeRequestDiffFile.delete_all(merge_request_diff_id: mr_diff.id)
         mr_diff.update_attributes(st_diffs: ["--broken-diff"])
       end
 
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index cd2f11dec96204ac7f559cb1e7e4597cedddd221..1240c9745e2924bd89046c7d69cf44443722c4b8 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -92,16 +92,16 @@ describe MergeRequest, models: true do
       allow(subject).to receive(:author).and_return(double(name: 'Robert'))
       allow(subject).to receive(:assignee).and_return(nil)
 
-      expect(subject.card_attributes).
-        to eq({ 'Author' => 'Robert', 'Assignee' => nil })
+      expect(subject.card_attributes)
+        .to eq({ 'Author' => 'Robert', 'Assignee' => nil })
     end
 
     it 'includes the assignee name' do
       allow(subject).to receive(:author).and_return(double(name: 'Robert'))
       allow(subject).to receive(:assignee).and_return(double(name: 'Douwe'))
 
-      expect(subject.card_attributes).
-        to eq({ 'Author' => 'Robert', 'Assignee' => 'Douwe' })
+      expect(subject.card_attributes)
+        .to eq({ 'Author' => 'Robert', 'Assignee' => 'Douwe' })
     end
   end
 
@@ -361,8 +361,8 @@ describe MergeRequest, models: true do
     end
 
     it 'accesses the set of issues that will be closed on acceptance' do
-      allow(subject.project).to receive(:default_branch).
-        and_return(subject.target_branch)
+      allow(subject.project).to receive(:default_branch)
+        .and_return(subject.target_branch)
 
       closed = subject.closes_issues
 
@@ -388,8 +388,8 @@ describe MergeRequest, models: true do
       subject.description = "Is related to #{mentioned_issue.to_reference} and #{closing_issue.to_reference}"
 
       allow(subject).to receive(:commits).and_return([commit])
-      allow(subject.project).to receive(:default_branch).
-        and_return(subject.target_branch)
+      allow(subject.project).to receive(:default_branch)
+        .and_return(subject.target_branch)
 
       expect(subject.issues_mentioned_but_not_closing(subject.author)).to match_array([mentioned_issue])
     end
@@ -537,8 +537,8 @@ describe MergeRequest, models: true do
       subject.project.team << [subject.author, :developer]
       subject.description = "This issue Closes #{issue.to_reference}"
 
-      allow(subject.project).to receive(:default_branch).
-        and_return(subject.target_branch)
+      allow(subject.project).to receive(:default_branch)
+        .and_return(subject.target_branch)
 
       expect(subject.merge_commit_message)
         .to match("Closes #{issue.to_reference}")
@@ -663,18 +663,18 @@ describe MergeRequest, models: true do
       end
 
       it 'caches the output' do
-        expect(subject).to receive(:compute_diverged_commits_count).
-          once.
-          and_return(2)
+        expect(subject).to receive(:compute_diverged_commits_count)
+          .once
+          .and_return(2)
 
         subject.diverged_commits_count
         subject.diverged_commits_count
       end
 
       it 'invalidates the cache when the source sha changes' do
-        expect(subject).to receive(:compute_diverged_commits_count).
-          twice.
-          and_return(2)
+        expect(subject).to receive(:compute_diverged_commits_count)
+          .twice
+          .and_return(2)
 
         subject.diverged_commits_count
         allow(subject).to receive(:source_branch_sha).and_return('123abc')
@@ -682,9 +682,9 @@ describe MergeRequest, models: true do
       end
 
       it 'invalidates the cache when the target sha changes' do
-        expect(subject).to receive(:compute_diverged_commits_count).
-          twice.
-          and_return(2)
+        expect(subject).to receive(:compute_diverged_commits_count)
+          .twice
+          .and_return(2)
 
         subject.diverged_commits_count
         allow(subject).to receive(:target_branch_sha).and_return('123abc')
@@ -706,8 +706,8 @@ describe MergeRequest, models: true do
 
   describe '#commits_sha' do
     before do
-      allow(subject.merge_request_diff).to receive(:commits_sha).
-        and_return(['sha1'])
+      allow(subject.merge_request_diff).to receive(:commits_sha)
+        .and_return(['sha1'])
     end
 
     it 'delegates to merge request diff' do
@@ -1397,7 +1397,7 @@ describe MergeRequest, models: true do
     end
   end
 
-  describe '#mergeable_with_slash_command?' do
+  describe '#mergeable_with_quick_action?' do
     def create_pipeline(status)
       pipeline = create(:ci_pipeline_with_one_job,
         project: project,
@@ -1421,21 +1421,21 @@ describe MergeRequest, models: true do
 
     context 'when autocomplete_precheck is set to true' do
       it 'is mergeable by developer' do
-        expect(merge_request.mergeable_with_slash_command?(developer, autocomplete_precheck: true)).to be_truthy
+        expect(merge_request.mergeable_with_quick_action?(developer, autocomplete_precheck: true)).to be_truthy
       end
 
       it 'is not mergeable by normal user' do
-        expect(merge_request.mergeable_with_slash_command?(user, autocomplete_precheck: true)).to be_falsey
+        expect(merge_request.mergeable_with_quick_action?(user, autocomplete_precheck: true)).to be_falsey
       end
     end
 
     context 'when autocomplete_precheck is set to false' do
       it 'is mergeable by developer' do
-        expect(merge_request.mergeable_with_slash_command?(developer, last_diff_sha: mr_sha)).to be_truthy
+        expect(merge_request.mergeable_with_quick_action?(developer, last_diff_sha: mr_sha)).to be_truthy
       end
 
       it 'is not mergeable by normal user' do
-        expect(merge_request.mergeable_with_slash_command?(user, last_diff_sha: mr_sha)).to be_falsey
+        expect(merge_request.mergeable_with_quick_action?(user, last_diff_sha: mr_sha)).to be_falsey
       end
 
       context 'closed MR'  do
@@ -1444,7 +1444,7 @@ describe MergeRequest, models: true do
         end
 
         it 'is not mergeable' do
-          expect(merge_request.mergeable_with_slash_command?(developer, last_diff_sha: mr_sha)).to be_falsey
+          expect(merge_request.mergeable_with_quick_action?(developer, last_diff_sha: mr_sha)).to be_falsey
         end
       end
 
@@ -1454,19 +1454,19 @@ describe MergeRequest, models: true do
         end
 
         it 'is not mergeable' do
-          expect(merge_request.mergeable_with_slash_command?(developer, last_diff_sha: mr_sha)).to be_falsey
+          expect(merge_request.mergeable_with_quick_action?(developer, last_diff_sha: mr_sha)).to be_falsey
         end
       end
 
       context 'sha differs from the MR diff_head_sha'  do
         it 'is not mergeable' do
-          expect(merge_request.mergeable_with_slash_command?(developer, last_diff_sha: 'some other sha')).to be_falsey
+          expect(merge_request.mergeable_with_quick_action?(developer, last_diff_sha: 'some other sha')).to be_falsey
         end
       end
 
       context 'sha is not provided'  do
         it 'is not mergeable' do
-          expect(merge_request.mergeable_with_slash_command?(developer)).to be_falsey
+          expect(merge_request.mergeable_with_quick_action?(developer)).to be_falsey
         end
       end
 
@@ -1476,7 +1476,7 @@ describe MergeRequest, models: true do
         end
 
         it 'is mergeable' do
-          expect(merge_request.mergeable_with_slash_command?(developer, last_diff_sha: mr_sha)).to be_truthy
+          expect(merge_request.mergeable_with_quick_action?(developer, last_diff_sha: mr_sha)).to be_truthy
         end
       end
 
@@ -1486,7 +1486,7 @@ describe MergeRequest, models: true do
         end
 
         it 'is not mergeable' do
-          expect(merge_request.mergeable_with_slash_command?(developer, last_diff_sha: mr_sha)).to be_falsey
+          expect(merge_request.mergeable_with_quick_action?(developer, last_diff_sha: mr_sha)).to be_falsey
         end
       end
 
@@ -1496,7 +1496,7 @@ describe MergeRequest, models: true do
         end
 
         it 'is mergeable' do
-          expect(merge_request.mergeable_with_slash_command?(developer, last_diff_sha: mr_sha)).to be_truthy
+          expect(merge_request.mergeable_with_quick_action?(developer, last_diff_sha: mr_sha)).to be_truthy
         end
       end
     end
@@ -1504,8 +1504,8 @@ describe MergeRequest, models: true do
 
   describe '#has_commits?' do
     before do
-      allow(subject.merge_request_diff).to receive(:commits_count).
-        and_return(2)
+      allow(subject.merge_request_diff).to receive(:commits_count)
+        .and_return(2)
     end
 
     it 'returns true when merge request diff has commits' do
@@ -1515,8 +1515,8 @@ describe MergeRequest, models: true do
 
   describe '#has_no_commits?' do
     before do
-      allow(subject.merge_request_diff).to receive(:commits_count).
-        and_return(0)
+      allow(subject.merge_request_diff).to receive(:commits_count)
+        .and_return(0)
     end
 
     it 'returns true when merge request diff has 0 commits' do
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index aa1ce89ffd7f713ad7d7c43f20b7b33fe8df056d..45953023a366caf935ea39279c07724bfb8e85a7 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -144,35 +144,6 @@ describe Milestone, models: true do
     end
   end
 
-  describe '#sort_issues' do
-    let(:milestone) { create(:milestone) }
-
-    let(:issue1) { create(:issue, milestone: milestone, position: 1) }
-    let(:issue2) { create(:issue, milestone: milestone, position: 2) }
-    let(:issue3) { create(:issue, milestone: milestone, position: 3) }
-    let(:issue4) { create(:issue, position: 42) }
-
-    it 'sorts the given issues' do
-      milestone.sort_issues([issue3.id, issue2.id, issue1.id])
-
-      issue1.reload
-      issue2.reload
-      issue3.reload
-
-      expect(issue1.position).to eq(3)
-      expect(issue2.position).to eq(2)
-      expect(issue3.position).to eq(1)
-    end
-
-    it 'ignores issues not part of the milestone' do
-      milestone.sort_issues([issue3.id, issue2.id, issue1.id, issue4.id])
-
-      issue4.reload
-
-      expect(issue4.position).to eq(42)
-    end
-  end
-
   describe '.search' do
     let(:milestone) { create(:milestone, title: 'foo', description: 'bar') }
 
@@ -193,13 +164,13 @@ describe Milestone, models: true do
     end
 
     it 'returns milestones with a partially matching description' do
-      expect(described_class.search(milestone.description[0..2])).
-        to eq([milestone])
+      expect(described_class.search(milestone.description[0..2]))
+        .to eq([milestone])
     end
 
     it 'returns milestones with a matching description regardless of the casing' do
-      expect(described_class.search(milestone.description.upcase)).
-        to eq([milestone])
+      expect(described_class.search(milestone.description.upcase))
+        .to eq([milestone])
     end
   end
 
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 145c7ad5770f18c96934e4f7ae64ef3dbe8ff405..e7c3acf19eb87dc576fe7dce6db4b63ac5105f2f 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -325,8 +325,8 @@ describe Namespace, models: true do
 
   describe '#user_ids_for_project_authorizations' do
     it 'returns the user IDs for which to refresh authorizations' do
-      expect(namespace.user_ids_for_project_authorizations).
-        to eq([namespace.owner_id])
+      expect(namespace.user_ids_for_project_authorizations)
+        .to eq([namespace.owner_id])
     end
   end
 
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index d4d4fc86343ecf3861fc4073ecbc25498f253854..e2b80cb6e61b62ea098bf8bac125d08ce515d780 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -152,8 +152,8 @@ describe Note, models: true do
     let!(:note2) { create(:note_on_issue) }
 
     it "reads the rendered note body from the cache" do
-      expect(Banzai::Renderer).to receive(:cache_collection_render).
-        with([{
+      expect(Banzai::Renderer).to receive(:cache_collection_render)
+        .with([{
           text: note1.note,
           context: {
             skip_project_check: false,
@@ -164,8 +164,8 @@ describe Note, models: true do
           }
         }]).and_call_original
 
-      expect(Banzai::Renderer).to receive(:cache_collection_render).
-        with([{
+      expect(Banzai::Renderer).to receive(:cache_collection_render)
+        .with([{
           text: note2.note,
           context: {
             skip_project_check: false,
@@ -406,8 +406,8 @@ describe Note, models: true do
       let(:note) { build(:note_on_project_snippet) }
 
       before do
-        expect(Banzai::Renderer).to receive(:cacheless_render_field).
-          with(note, :note, { skip_project_check: false }).and_return(html)
+        expect(Banzai::Renderer).to receive(:cacheless_render_field)
+          .with(note, :note, { skip_project_check: false }).and_return(html)
 
         note.save
       end
@@ -421,8 +421,8 @@ describe Note, models: true do
       let(:note) { build(:note_on_personal_snippet) }
 
       before do
-        expect(Banzai::Renderer).to receive(:cacheless_render_field).
-          with(note, :note, { skip_project_check: true }).and_return(html)
+        expect(Banzai::Renderer).to receive(:cacheless_render_field)
+          .with(note, :note, { skip_project_check: true }).and_return(html)
 
         note.save
       end
diff --git a/spec/models/project_authorization_spec.rb b/spec/models/project_authorization_spec.rb
index cd0a4a94809082df692c1b7d2e4456e8d3bd7cd2..ee6bdc39c8cc4081a582a24bee5c3f88272083a2 100644
--- a/spec/models/project_authorization_spec.rb
+++ b/spec/models/project_authorization_spec.rb
@@ -7,8 +7,8 @@ describe ProjectAuthorization do
 
   describe '.insert_authorizations' do
     it 'inserts the authorizations' do
-      described_class.
-        insert_authorizations([[user.id, project1.id, Gitlab::Access::MASTER]])
+      described_class
+        .insert_authorizations([[user.id, project1.id, Gitlab::Access::MASTER]])
 
       expect(user.project_authorizations.count).to eq(1)
     end
diff --git a/spec/models/project_feature_spec.rb b/spec/models/project_feature_spec.rb
index 09a4448d387ad4e0f84599f3fcec9d245083e42e..580c83c12c0e34a2c9a08de9f8b3207ca35ee77d 100644
--- a/spec/models/project_feature_spec.rb
+++ b/spec/models/project_feature_spec.rb
@@ -4,6 +4,18 @@ describe ProjectFeature do
   let(:project) { create(:empty_project) }
   let(:user) { create(:user) }
 
+  describe '.quoted_access_level_column' do
+    it 'returns the table name and quoted column name for a feature' do
+      expected = if Gitlab::Database.postgresql?
+                   '"project_features"."issues_access_level"'
+                 else
+                   '`project_features`.`issues_access_level`'
+                 end
+
+      expect(described_class.quoted_access_level_column(:issues)).to eq(expected)
+    end
+  end
+
   describe '#feature_available?' do
     let(:features) { %w(issues wiki builds merge_requests snippets repository) }
 
diff --git a/spec/models/project_services/chat_message/pipeline_message_spec.rb b/spec/models/project_services/chat_message/pipeline_message_spec.rb
index 7d2599dc70337ed0bb29c4528409b231534a3e68..43b02568cb98aa80239f5819af14c8a36b9ee03b 100644
--- a/spec/models/project_services/chat_message/pipeline_message_spec.rb
+++ b/spec/models/project_services/chat_message/pipeline_message_spec.rb
@@ -62,7 +62,7 @@ describe ChatMessage::PipelineMessage do
     def build_message(status_text = status, name = user[:name])
       "<http://example.gitlab.com|project_name>:" \
         " Pipeline <http://example.gitlab.com/pipelines/123|#123>" \
-        " of branch `<http://example.gitlab.com/commits/develop|develop>`" \
+        " of branch <http://example.gitlab.com/commits/develop|develop>" \
         " by #{name} #{status_text} in 02:00:10"
     end
   end
@@ -81,7 +81,7 @@ describe ChatMessage::PipelineMessage do
         expect(subject.pretext).to be_empty
         expect(subject.attachments).to eq(message)
         expect(subject.activity).to eq({
-          title: 'Pipeline [#123](http://example.gitlab.com/pipelines/123) of branch `[develop](http://example.gitlab.com/commits/develop)` by hacker passed',
+          title: 'Pipeline [#123](http://example.gitlab.com/pipelines/123) of branch [develop](http://example.gitlab.com/commits/develop) by hacker passed',
           subtitle: 'in [project_name](http://example.gitlab.com)',
           text: 'in 02:00:10',
           image: ''
@@ -98,7 +98,7 @@ describe ChatMessage::PipelineMessage do
         expect(subject.pretext).to be_empty
         expect(subject.attachments).to eq(message)
         expect(subject.activity).to eq({
-          title: 'Pipeline [#123](http://example.gitlab.com/pipelines/123) of branch `[develop](http://example.gitlab.com/commits/develop)` by hacker failed',
+          title: 'Pipeline [#123](http://example.gitlab.com/pipelines/123) of branch [develop](http://example.gitlab.com/commits/develop) by hacker failed',
           subtitle: 'in [project_name](http://example.gitlab.com)',
           text: 'in 02:00:10',
           image: ''
@@ -113,7 +113,7 @@ describe ChatMessage::PipelineMessage do
           expect(subject.pretext).to be_empty
           expect(subject.attachments).to eq(message)
           expect(subject.activity).to eq({
-            title: 'Pipeline [#123](http://example.gitlab.com/pipelines/123) of branch `[develop](http://example.gitlab.com/commits/develop)` by API failed',
+            title: 'Pipeline [#123](http://example.gitlab.com/pipelines/123) of branch [develop](http://example.gitlab.com/commits/develop) by API failed',
             subtitle: 'in [project_name](http://example.gitlab.com)',
             text: 'in 02:00:10',
             image: ''
@@ -125,7 +125,7 @@ describe ChatMessage::PipelineMessage do
     def build_markdown_message(status_text = status, name = user[:name])
       "[project_name](http://example.gitlab.com):" \
         " Pipeline [#123](http://example.gitlab.com/pipelines/123)" \
-        " of branch `[develop](http://example.gitlab.com/commits/develop)`" \
+        " of branch [develop](http://example.gitlab.com/commits/develop)" \
         " by #{name} #{status_text} in 02:00:10"
     end
   end
diff --git a/spec/models/project_services/chat_message/push_message_spec.rb b/spec/models/project_services/chat_message/push_message_spec.rb
index e38117b75f6ca26b1ae6ea6686bee9eb88e96efc..c794f659c4175a3ae8d3a41b0c56275601612db1 100644
--- a/spec/models/project_services/chat_message/push_message_spec.rb
+++ b/spec/models/project_services/chat_message/push_message_spec.rb
@@ -28,7 +28,7 @@ describe ChatMessage::PushMessage, models: true do
     context 'without markdown' do
       it 'returns a message regarding pushes' do
         expect(subject.pretext).to eq(
-          'test.user pushed to branch `<http://url.com/commits/master|master>` of '\
+          'test.user pushed to branch <http://url.com/commits/master|master> of '\
             '<http://url.com|project_name> (<http://url.com/compare/before...after|Compare changes>)')
         expect(subject.attachments).to eq([{
           text: "<http://url1.com|abcdefgh>: message1 - author1\n\n"\
@@ -45,7 +45,7 @@ describe ChatMessage::PushMessage, models: true do
 
       it 'returns a message regarding pushes' do
         expect(subject.pretext).to eq(
-          'test.user pushed to branch `[master](http://url.com/commits/master)` of [project_name](http://url.com) ([Compare changes](http://url.com/compare/before...after))')
+          'test.user pushed to branch [master](http://url.com/commits/master) of [project_name](http://url.com) ([Compare changes](http://url.com/compare/before...after))')
         expect(subject.attachments).to eq(
           "[abcdefgh](http://url1.com): message1 - author1\n\n[12345678](http://url2.com): message2 - author2")
         expect(subject.activity).to eq({
@@ -74,7 +74,7 @@ describe ChatMessage::PushMessage, models: true do
     context 'without markdown' do
       it 'returns a message regarding pushes' do
         expect(subject.pretext).to eq('test.user pushed new tag ' \
-          '`<http://url.com/commits/new_tag|new_tag>` to ' \
+          '<http://url.com/commits/new_tag|new_tag> to ' \
           '<http://url.com|project_name>')
         expect(subject.attachments).to be_empty
       end
@@ -87,7 +87,7 @@ describe ChatMessage::PushMessage, models: true do
 
       it 'returns a message regarding pushes' do
         expect(subject.pretext).to eq(
-          'test.user pushed new tag `[new_tag](http://url.com/commits/new_tag)` to [project_name](http://url.com)')
+          'test.user pushed new tag [new_tag](http://url.com/commits/new_tag) to [project_name](http://url.com)')
         expect(subject.attachments).to be_empty
         expect(subject.activity).to eq({
           title: 'test.user created tag',
@@ -107,7 +107,7 @@ describe ChatMessage::PushMessage, models: true do
     context 'without markdown' do
       it 'returns a message regarding a new branch' do
         expect(subject.pretext).to eq(
-          'test.user pushed new branch `<http://url.com/commits/master|master>` to '\
+          'test.user pushed new branch <http://url.com/commits/master|master> to '\
             '<http://url.com|project_name>')
         expect(subject.attachments).to be_empty
       end
@@ -120,7 +120,7 @@ describe ChatMessage::PushMessage, models: true do
 
       it 'returns a message regarding a new branch' do
         expect(subject.pretext).to eq(
-          'test.user pushed new branch `[master](http://url.com/commits/master)` to [project_name](http://url.com)')
+          'test.user pushed new branch [master](http://url.com/commits/master) to [project_name](http://url.com)')
         expect(subject.attachments).to be_empty
         expect(subject.activity).to eq({
           title: 'test.user created branch',
@@ -140,7 +140,7 @@ describe ChatMessage::PushMessage, models: true do
     context 'without markdown' do
       it 'returns a message regarding a removed branch' do
         expect(subject.pretext).to eq(
-          'test.user removed branch `master` from <http://url.com|project_name>')
+          'test.user removed branch master from <http://url.com|project_name>')
         expect(subject.attachments).to be_empty
       end
     end
@@ -152,7 +152,7 @@ describe ChatMessage::PushMessage, models: true do
 
       it 'returns a message regarding a removed branch' do
         expect(subject.pretext).to eq(
-          'test.user removed branch `master` from [project_name](http://url.com)')
+          'test.user removed branch master from [project_name](http://url.com)')
         expect(subject.attachments).to be_empty
         expect(subject.activity).to eq({
           title: 'test.user removed branch',
diff --git a/spec/models/project_services/mattermost_slash_commands_service_spec.rb b/spec/models/project_services/mattermost_slash_commands_service_spec.rb
index f9531be5d251e8e32e9bd9af2ddce15415b2ac35..fa38d23e82faab94c37a8f3e155953007a9dd053 100644
--- a/spec/models/project_services/mattermost_slash_commands_service_spec.rb
+++ b/spec/models/project_services/mattermost_slash_commands_service_spec.rb
@@ -11,8 +11,8 @@ describe MattermostSlashCommandsService, :models do
     before do
       Mattermost::Session.base_uri("http://mattermost.example.com")
 
-      allow_any_instance_of(Mattermost::Client).to receive(:with_session).
-        and_yield(Mattermost::Session.new(nil))
+      allow_any_instance_of(Mattermost::Client).to receive(:with_session)
+        .and_yield(Mattermost::Session.new(nil))
     end
 
     describe '#configure' do
@@ -24,8 +24,8 @@ describe MattermostSlashCommandsService, :models do
 
       context 'the requests succeeds' do
         before do
-          stub_request(:post, 'http://mattermost.example.com/api/v3/teams/abc/commands/create').
-            with(body: {
+          stub_request(:post, 'http://mattermost.example.com/api/v3/teams/abc/commands/create')
+            .with(body: {
               team_id: 'abc',
               trigger: 'gitlab',
               url: 'http://trigger.url',
@@ -37,8 +37,8 @@ describe MattermostSlashCommandsService, :models do
               display_name: "GitLab / #{project.name_with_namespace}",
               method: 'P',
               username: 'GitLab'
-            }.to_json).
-            to_return(
+            }.to_json)
+            .to_return(
               status: 200,
               headers: { 'Content-Type' => 'application/json' },
               body: { token: 'token' }.to_json
@@ -58,8 +58,8 @@ describe MattermostSlashCommandsService, :models do
 
       context 'an error is received' do
         before do
-          stub_request(:post, 'http://mattermost.example.com/api/v3/teams/abc/commands/create').
-            to_return(
+          stub_request(:post, 'http://mattermost.example.com/api/v3/teams/abc/commands/create')
+            .to_return(
               status: 500,
               headers: { 'Content-Type' => 'application/json' },
               body: {
@@ -88,8 +88,8 @@ describe MattermostSlashCommandsService, :models do
 
       context 'the requests succeeds' do
         before do
-          stub_request(:get, 'http://mattermost.example.com/api/v3/teams/all').
-            to_return(
+          stub_request(:get, 'http://mattermost.example.com/api/v3/teams/all')
+            .to_return(
               status: 200,
               headers: { 'Content-Type' => 'application/json' },
               body: { 'list' => true }.to_json
@@ -103,8 +103,8 @@ describe MattermostSlashCommandsService, :models do
 
       context 'an error is received' do
         before do
-          stub_request(:get, 'http://mattermost.example.com/api/v3/teams/all').
-            to_return(
+          stub_request(:get, 'http://mattermost.example.com/api/v3/teams/all')
+            .to_return(
               status: 500,
               headers: { 'Content-Type' => 'application/json' },
               body: {
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 63333b7af1f28496ae033ed0c244ebc4573e2e1a..d7fcadb895e129b84bccab73a1ca4c2923b6cfa7 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1195,23 +1195,23 @@ describe Project, models: true do
     it 'renames a repository' do
       stub_container_registry_config(enabled: false)
 
-      expect(gitlab_shell).to receive(:mv_repository).
-        ordered.
-        with(project.repository_storage_path, "#{project.namespace.full_path}/foo", "#{project.full_path}").
-        and_return(true)
+      expect(gitlab_shell).to receive(:mv_repository)
+        .ordered
+        .with(project.repository_storage_path, "#{project.namespace.full_path}/foo", "#{project.full_path}")
+        .and_return(true)
 
-      expect(gitlab_shell).to receive(:mv_repository).
-        ordered.
-        with(project.repository_storage_path, "#{project.namespace.full_path}/foo.wiki", "#{project.full_path}.wiki").
-        and_return(true)
+      expect(gitlab_shell).to receive(:mv_repository)
+        .ordered
+        .with(project.repository_storage_path, "#{project.namespace.full_path}/foo.wiki", "#{project.full_path}.wiki")
+        .and_return(true)
 
-      expect_any_instance_of(SystemHooksService).
-        to receive(:execute_hooks_for).
-        with(project, :rename)
+      expect_any_instance_of(SystemHooksService)
+        .to receive(:execute_hooks_for)
+        .with(project, :rename)
 
-      expect_any_instance_of(Gitlab::UploadsTransfer).
-        to receive(:rename_project).
-        with('foo', project.path, project.namespace.full_path)
+      expect_any_instance_of(Gitlab::UploadsTransfer)
+        .to receive(:rename_project)
+        .with('foo', project.path, project.namespace.full_path)
 
       expect(project).to receive(:expire_caches_before_rename)
 
@@ -1239,13 +1239,13 @@ describe Project, models: true do
     let(:wiki)    { double(:wiki, exists?: true) }
 
     it 'expires the caches of the repository and wiki' do
-      allow(Repository).to receive(:new).
-        with('foo', project).
-        and_return(repo)
+      allow(Repository).to receive(:new)
+        .with('foo', project)
+        .and_return(repo)
 
-      allow(Repository).to receive(:new).
-        with('foo.wiki', project).
-        and_return(wiki)
+      allow(Repository).to receive(:new)
+        .with('foo.wiki', project)
+        .and_return(wiki)
 
       expect(repo).to receive(:before_delete)
       expect(wiki).to receive(:before_delete)
@@ -1296,9 +1296,9 @@ describe Project, models: true do
 
     context 'using a regular repository' do
       it 'creates the repository' do
-        expect(shell).to receive(:add_repository).
-          with(project.repository_storage_path, project.path_with_namespace).
-          and_return(true)
+        expect(shell).to receive(:add_repository)
+          .with(project.repository_storage_path, project.path_with_namespace)
+          .and_return(true)
 
         expect(project.repository).to receive(:after_create)
 
@@ -1306,9 +1306,9 @@ describe Project, models: true do
       end
 
       it 'adds an error if the repository could not be created' do
-        expect(shell).to receive(:add_repository).
-          with(project.repository_storage_path, project.path_with_namespace).
-          and_return(false)
+        expect(shell).to receive(:add_repository)
+          .with(project.repository_storage_path, project.path_with_namespace)
+          .and_return(false)
 
         expect(project.repository).not_to receive(:after_create)
 
@@ -1564,8 +1564,8 @@ describe Project, models: true do
       let(:project) { forked_project_link.forked_to_project }
 
       it 'schedules a RepositoryForkWorker job' do
-        expect(RepositoryForkWorker).to receive(:perform_async).
-          with(project.id, forked_from_project.repository_storage_path,
+        expect(RepositoryForkWorker).to receive(:perform_async)
+          .with(project.id, forked_from_project.repository_storage_path,
               forked_from_project.path_with_namespace, project.namespace.full_path)
 
         project.add_import_job
@@ -2041,15 +2041,15 @@ describe Project, models: true do
       error_message = 'Failed to replace merge_requests because one or more of the new records could not be saved.'\
                       ' Validate fork Source project is not a fork of the target project'
 
-      expect { project.append_or_update_attribute(:merge_requests, [create(:merge_request)]) }.
-        to raise_error(ActiveRecord::RecordNotSaved, error_message)
+      expect { project.append_or_update_attribute(:merge_requests, [create(:merge_request)]) }
+        .to raise_error(ActiveRecord::RecordNotSaved, error_message)
     end
 
     it 'updates the project succesfully' do
       merge_request = create(:merge_request, target_project: project, source_project: project)
 
-      expect { project.append_or_update_attribute(:merge_requests, [merge_request]) }.
-        not_to raise_error
+      expect { project.append_or_update_attribute(:merge_requests, [merge_request]) }
+        .not_to raise_error
     end
   end
 
@@ -2060,4 +2060,36 @@ describe Project, models: true do
       expect(project.last_repository_updated_at.to_i).to eq(project.created_at.to_i)
     end
   end
+
+  describe '.public_or_visible_to_user' do
+    let!(:user) { create(:user) }
+
+    let!(:private_project) do
+      create(:empty_project, :private, creator: user, namespace: user.namespace)
+    end
+
+    let!(:public_project) { create(:empty_project, :public) }
+
+    context 'with a user' do
+      let(:projects) do
+        Project.all.public_or_visible_to_user(user)
+      end
+
+      it 'includes projects the user has access to' do
+        expect(projects).to include(private_project)
+      end
+
+      it 'includes projects the user can see' do
+        expect(projects).to include(public_project)
+      end
+    end
+
+    context 'without a user' do
+      it 'only includes public projects' do
+        projects = Project.all.public_or_visible_to_user
+
+        expect(projects).to eq([public_project])
+      end
+    end
+  end
 end
diff --git a/spec/models/project_team_spec.rb b/spec/models/project_team_spec.rb
index ea3cd5fe10ac17665b4907a840ff5843fb260b79..49f2f8c0ad1445f7b2018297ec53deb67fbcd976 100644
--- a/spec/models/project_team_spec.rb
+++ b/spec/models/project_team_spec.rb
@@ -100,8 +100,8 @@ describe ProjectTeam, models: true do
           group_access: Gitlab::Access::GUEST
         )
 
-        expect(project.team.members).
-          to contain_exactly(group_member.user, project.owner)
+        expect(project.team.members)
+          .to contain_exactly(group_member.user, project.owner)
       end
 
       it 'returns invited members of a group of a specified level' do
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index 3f5f4eea4a12932d849d413a274a5aa70bbc2be8..bf74ac5ea25fd9e5381604ee70fffd529ad28500 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -149,15 +149,15 @@ describe ProjectWiki, models: true do
   describe '#find_file' do
     before do
       file = Gollum::File.new(subject.wiki)
-      allow_any_instance_of(Gollum::Wiki).
-                   to receive(:file).with('image.jpg', 'master', true).
-                   and_return(file)
-      allow_any_instance_of(Gollum::File).
-                   to receive(:mime_type).
-                   and_return('image/jpeg')
-      allow_any_instance_of(Gollum::Wiki).
-                   to receive(:file).with('non-existant', 'master', true).
-                   and_return(nil)
+      allow_any_instance_of(Gollum::Wiki)
+                   .to receive(:file).with('image.jpg', 'master', true)
+                   .and_return(file)
+      allow_any_instance_of(Gollum::File)
+                   .to receive(:mime_type)
+                   .and_return('image/jpeg')
+      allow_any_instance_of(Gollum::Wiki)
+                   .to receive(:file).with('non-existant', 'master', true)
+                   .and_return(nil)
     end
 
     after do
@@ -268,9 +268,9 @@ describe ProjectWiki, models: true do
 
   describe '#create_repo!' do
     it 'creates a repository' do
-      expect(subject).to receive(:init_repo).
-        with(subject.path_with_namespace).
-        and_return(true)
+      expect(subject).to receive(:init_repo)
+        .with(subject.path_with_namespace)
+        .and_return(true)
 
       expect(subject.repository).to receive(:after_create)
 
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index a6d4d92c45017a6980f11ef9ace53320a408ef91..3e984ec7588a68fdd4ec7571c537f69365fdbe6c 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -111,8 +111,8 @@ describe Repository, models: true do
 
   describe '#ref_name_for_sha' do
     it 'returns the ref' do
-      allow(repository.raw_repository).to receive(:ref_name_for_sha).
-        and_return('refs/environments/production/77')
+      allow(repository.raw_repository).to receive(:ref_name_for_sha)
+        .and_return('refs/environments/production/77')
 
       expect(repository.ref_name_for_sha('bla', '0' * 40)).to eq 'refs/environments/production/77'
     end
@@ -593,8 +593,8 @@ describe Repository, models: true do
         user, 'LICENSE', 'Copyright!',
         message: 'Add LICENSE', branch_name: 'master')
 
-      allow(repository).to receive(:file_on_head).
-        and_raise(Rugged::ReferenceError)
+      allow(repository).to receive(:file_on_head)
+        .and_raise(Rugged::ReferenceError)
 
       expect(repository.license_blob).to be_nil
     end
@@ -779,8 +779,8 @@ describe Repository, models: true do
 
     context 'when pre hooks were successful' do
       it 'runs without errors' do
-        expect_any_instance_of(GitHooksService).to receive(:execute).
-          with(user, project.repository.path_to_repo, old_rev, blank_sha, 'refs/heads/feature')
+        expect_any_instance_of(GitHooksService).to receive(:execute)
+          .with(user, project.repository.path_to_repo, old_rev, blank_sha, 'refs/heads/feature')
 
         expect { repository.rm_branch(user, 'feature') }.not_to raise_error
       end
@@ -822,14 +822,14 @@ describe Repository, models: true do
       before do
         service = GitHooksService.new
         expect(GitHooksService).to receive(:new).and_return(service)
-        expect(service).to receive(:execute).
-          with(
+        expect(service).to receive(:execute)
+          .with(
             user,
             repository.path_to_repo,
             old_rev,
             new_rev,
-            'refs/heads/feature').
-          and_yield(service).and_return(true)
+            'refs/heads/feature')
+          .and_yield(service).and_return(true)
       end
 
       it 'runs without errors' do
@@ -923,8 +923,8 @@ describe Repository, models: true do
         expect(repository).not_to receive(:expire_emptiness_caches)
         expect(repository).to     receive(:expire_branches_cache)
 
-        GitOperationService.new(user, repository).
-          with_branch('new-feature') do
+        GitOperationService.new(user, repository)
+          .with_branch('new-feature') do
             new_rev
           end
       end
@@ -1007,8 +1007,8 @@ describe Repository, models: true do
       end
 
       it 'does nothing' do
-        expect(repository.raw_repository).not_to receive(:autocrlf=).
-          with(:input)
+        expect(repository.raw_repository).not_to receive(:autocrlf=)
+          .with(:input)
 
         GitOperationService.new(nil, repository).send(:update_autocrlf_option)
       end
@@ -1027,9 +1027,9 @@ describe Repository, models: true do
     end
 
     it 'caches the output' do
-      expect(repository.raw_repository).to receive(:empty?).
-        once.
-        and_return(false)
+      expect(repository.raw_repository).to receive(:empty?)
+        .once
+        .and_return(false)
 
       repository.empty?
       repository.empty?
@@ -1042,9 +1042,9 @@ describe Repository, models: true do
     end
 
     it 'caches the output' do
-      expect(repository.raw_repository).to receive(:root_ref).
-        once.
-        and_return('master')
+      expect(repository.raw_repository).to receive(:root_ref)
+        .once
+        .and_return('master')
 
       repository.root_ref
       repository.root_ref
@@ -1055,9 +1055,9 @@ describe Repository, models: true do
     it 'expires the root reference cache' do
       repository.root_ref
 
-      expect(repository.raw_repository).to receive(:root_ref).
-        once.
-        and_return('foo')
+      expect(repository.raw_repository).to receive(:root_ref)
+        .once
+        .and_return('foo')
 
       repository.expire_root_ref_cache
 
@@ -1071,17 +1071,17 @@ describe Repository, models: true do
     let(:cache) { repository.send(:cache) }
 
     it 'expires the cache for all branches' do
-      expect(cache).to receive(:expire).
-        at_least(repository.branches.length * 2).
-        times
+      expect(cache).to receive(:expire)
+        .at_least(repository.branches.length * 2)
+        .times
 
       repository.expire_branch_cache
     end
 
     it 'expires the cache for all branches when the root branch is given' do
-      expect(cache).to receive(:expire).
-        at_least(repository.branches.length * 2).
-        times
+      expect(cache).to receive(:expire)
+        .at_least(repository.branches.length * 2)
+        .times
 
       repository.expire_branch_cache(repository.root_ref)
     end
@@ -1344,12 +1344,12 @@ describe Repository, models: true do
 
   describe '#after_push_commit' do
     it 'expires statistics caches' do
-      expect(repository).to receive(:expire_statistics_caches).
-        and_call_original
+      expect(repository).to receive(:expire_statistics_caches)
+        .and_call_original
 
-      expect(repository).to receive(:expire_branch_cache).
-        with('master').
-        and_call_original
+      expect(repository).to receive(:expire_branch_cache)
+        .with('master')
+        .and_call_original
 
       repository.after_push_commit('master')
     end
@@ -1434,9 +1434,9 @@ describe Repository, models: true do
 
   describe '#expire_branches_cache' do
     it 'expires the cache' do
-      expect(repository).to receive(:expire_method_caches).
-        with(%i(branch_names branch_count)).
-        and_call_original
+      expect(repository).to receive(:expire_method_caches)
+        .with(%i(branch_names branch_count))
+        .and_call_original
 
       repository.expire_branches_cache
     end
@@ -1444,9 +1444,9 @@ describe Repository, models: true do
 
   describe '#expire_tags_cache' do
     it 'expires the cache' do
-      expect(repository).to receive(:expire_method_caches).
-        with(%i(tag_names tag_count)).
-        and_call_original
+      expect(repository).to receive(:expire_method_caches)
+        .with(%i(tag_names tag_count))
+        .and_call_original
 
       repository.expire_tags_cache
     end
@@ -1457,11 +1457,11 @@ describe Repository, models: true do
       let(:user) { build_stubbed(:user) }
 
       it 'creates the tag using rugged' do
-        expect(repository.rugged.tags).to receive(:create).
-          with('8.5', repository.commit('master').id,
+        expect(repository.rugged.tags).to receive(:create)
+          .with('8.5', repository.commit('master').id,
             hash_including(message: 'foo',
-                           tagger: hash_including(name: user.name, email: user.email))).
-          and_call_original
+                           tagger: hash_including(name: user.name, email: user.email)))
+          .and_call_original
 
         repository.add_tag(user, '8.5', 'master', 'foo')
       end
@@ -1478,8 +1478,8 @@ describe Repository, models: true do
         update_hook = Gitlab::Git::Hook.new('update', repository.path_to_repo)
         post_receive_hook = Gitlab::Git::Hook.new('post-receive', repository.path_to_repo)
 
-        allow(Gitlab::Git::Hook).to receive(:new).
-          and_return(pre_receive_hook, update_hook, post_receive_hook)
+        allow(Gitlab::Git::Hook).to receive(:new)
+          .and_return(pre_receive_hook, update_hook, post_receive_hook)
 
         allow(pre_receive_hook).to receive(:trigger).and_call_original
         allow(update_hook).to receive(:trigger).and_call_original
@@ -1490,12 +1490,12 @@ describe Repository, models: true do
         commit_sha = repository.commit('master').id
         tag_sha = tag.target
 
-        expect(pre_receive_hook).to have_received(:trigger).
-          with(anything, anything, commit_sha, anything)
-        expect(update_hook).to have_received(:trigger).
-          with(anything, anything, commit_sha, anything)
-        expect(post_receive_hook).to have_received(:trigger).
-          with(anything, anything, tag_sha, anything)
+        expect(pre_receive_hook).to have_received(:trigger)
+          .with(anything, anything, commit_sha, anything)
+        expect(update_hook).to have_received(:trigger)
+          .with(anything, anything, commit_sha, anything)
+        expect(post_receive_hook).to have_received(:trigger)
+          .with(anything, anything, tag_sha, anything)
       end
     end
 
@@ -1529,25 +1529,25 @@ describe Repository, models: true do
 
   describe '#avatar' do
     it 'returns nil if repo does not exist' do
-      expect(repository).to receive(:file_on_head).
-        and_raise(Rugged::ReferenceError)
+      expect(repository).to receive(:file_on_head)
+        .and_raise(Rugged::ReferenceError)
 
       expect(repository.avatar).to eq(nil)
     end
 
     it 'returns the first avatar file found in the repository' do
-      expect(repository).to receive(:file_on_head).
-        with(:avatar).
-        and_return(double(:tree, path: 'logo.png'))
+      expect(repository).to receive(:file_on_head)
+        .with(:avatar)
+        .and_return(double(:tree, path: 'logo.png'))
 
       expect(repository.avatar).to eq('logo.png')
     end
 
     it 'caches the output' do
-      expect(repository).to receive(:file_on_head).
-        with(:avatar).
-        once.
-        and_return(double(:tree, path: 'logo.png'))
+      expect(repository).to receive(:file_on_head)
+        .with(:avatar)
+        .once
+        .and_return(double(:tree, path: 'logo.png'))
 
       2.times { expect(repository.avatar).to eq('logo.png') }
     end
@@ -1607,24 +1607,24 @@ describe Repository, models: true do
 
   describe '#contribution_guide', caching: true do
     it 'returns and caches the output' do
-      expect(repository).to receive(:file_on_head).
-        with(:contributing).
-        and_return(Gitlab::Git::Tree.new(path: 'CONTRIBUTING.md')).
-        once
+      expect(repository).to receive(:file_on_head)
+        .with(:contributing)
+        .and_return(Gitlab::Git::Tree.new(path: 'CONTRIBUTING.md'))
+        .once
 
       2.times do
-        expect(repository.contribution_guide).
-          to be_an_instance_of(Gitlab::Git::Tree)
+        expect(repository.contribution_guide)
+          .to be_an_instance_of(Gitlab::Git::Tree)
       end
     end
   end
 
   describe '#gitignore', caching: true do
     it 'returns and caches the output' do
-      expect(repository).to receive(:file_on_head).
-        with(:gitignore).
-        and_return(Gitlab::Git::Tree.new(path: '.gitignore')).
-        once
+      expect(repository).to receive(:file_on_head)
+        .with(:gitignore)
+        .and_return(Gitlab::Git::Tree.new(path: '.gitignore'))
+        .once
 
       2.times do
         expect(repository.gitignore).to be_an_instance_of(Gitlab::Git::Tree)
@@ -1634,10 +1634,10 @@ describe Repository, models: true do
 
   describe '#koding_yml', caching: true do
     it 'returns and caches the output' do
-      expect(repository).to receive(:file_on_head).
-        with(:koding).
-        and_return(Gitlab::Git::Tree.new(path: '.koding.yml')).
-        once
+      expect(repository).to receive(:file_on_head)
+        .with(:koding)
+        .and_return(Gitlab::Git::Tree.new(path: '.koding.yml'))
+        .once
 
       2.times do
         expect(repository.koding_yml).to be_an_instance_of(Gitlab::Git::Tree)
@@ -1673,8 +1673,8 @@ describe Repository, models: true do
 
   describe '#expire_statistics_caches' do
     it 'expires the caches' do
-      expect(repository).to receive(:expire_method_caches).
-        with(%i(size commit_count))
+      expect(repository).to receive(:expire_method_caches)
+        .with(%i(size commit_count))
 
       repository.expire_statistics_caches
     end
@@ -1691,8 +1691,8 @@ describe Repository, models: true do
 
   describe '#expire_all_method_caches' do
     it 'expires the caches of all methods' do
-      expect(repository).to receive(:expire_method_caches).
-        with(Repository::CACHED_METHODS)
+      expect(repository).to receive(:expire_method_caches)
+        .with(Repository::CACHED_METHODS)
 
       repository.expire_all_method_caches
     end
@@ -1717,8 +1717,8 @@ describe Repository, models: true do
 
     context 'with an existing repository' do
       it 'returns a Gitlab::Git::Tree' do
-        expect(repository.file_on_head(:readme)).
-          to be_an_instance_of(Gitlab::Git::Tree)
+        expect(repository.file_on_head(:readme))
+          .to be_an_instance_of(Gitlab::Git::Tree)
       end
     end
   end
@@ -1856,8 +1856,8 @@ describe Repository, models: true do
 
   describe '#refresh_method_caches' do
     it 'refreshes the caches of the given types' do
-      expect(repository).to receive(:expire_method_caches).
-        with(%i(rendered_readme license_blob license_key license))
+      expect(repository).to receive(:expire_method_caches)
+        .with(%i(rendered_readme license_blob license_key license))
 
       expect(repository).to receive(:rendered_readme)
       expect(repository).to receive(:license_blob)
diff --git a/spec/models/upload_spec.rb b/spec/models/upload_spec.rb
index 4c832c87d6afcbffaaa390db292905c39beb1147..2dea2c6015f6bd22ac7bf4032f6bf6005f1c8548 100644
--- a/spec/models/upload_spec.rb
+++ b/spec/models/upload_spec.rb
@@ -54,8 +54,8 @@ describe Upload, type: :model do
         uploader: 'AvatarUploader'
       )
 
-      expect { described_class.remove_path(__FILE__) }.
-        to change { described_class.count }.from(1).to(0)
+      expect { described_class.remove_path(__FILE__) }
+        .to change { described_class.count }.from(1).to(0)
     end
   end
 
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 1a1bbd60504f1569e97d21c289088a86df9715e4..314f8781867957864f9646bd4cddab210c37c65f 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -451,6 +451,40 @@ describe User, models: true do
     end
   end
 
+  describe '#ensure_user_rights_and_limits' do
+    describe 'with external user' do
+      let(:user) { create(:user, external: true) }
+
+      it 'receives callback when external changes' do
+        expect(user).to receive(:ensure_user_rights_and_limits)
+
+        user.update_attributes(external: false)
+      end
+
+      it 'ensures correct rights and limits for user' do
+        stub_config_setting(default_can_create_group: true)
+
+        expect { user.update_attributes(external: false) }.to change { user.can_create_group }.to(true)
+          .and change { user.projects_limit }.to(current_application_settings.default_projects_limit)
+      end
+    end
+
+    describe 'without external user' do
+      let(:user) { create(:user, external: false) }
+
+      it 'receives callback when external changes' do
+        expect(user).to receive(:ensure_user_rights_and_limits)
+
+        user.update_attributes(external: true)
+      end
+
+      it 'ensures correct rights and limits for user' do
+        expect { user.update_attributes(external: true) }.to change { user.can_create_group }.to(false)
+          .and change { user.projects_limit }.to(0)
+      end
+    end
+  end
+
   describe 'rss token' do
     it 'ensures an rss token on read' do
       user = create(:user, rss_token: nil)
@@ -878,8 +912,8 @@ describe User, models: true do
 
   describe '.find_by_username!' do
     it 'raises RecordNotFound' do
-      expect { described_class.find_by_username!('JohnDoe') }.
-        to raise_error(ActiveRecord::RecordNotFound)
+      expect { described_class.find_by_username!('JohnDoe') }
+        .to raise_error(ActiveRecord::RecordNotFound)
     end
 
     it 'is case-insensitive' do
@@ -1523,8 +1557,8 @@ describe User, models: true do
     end
 
     it 'returns the projects when using an ActiveRecord relation' do
-      projects = user.
-        projects_with_reporter_access_limited_to(Project.select(:id))
+      projects = user
+        .projects_with_reporter_access_limited_to(Project.select(:id))
 
       expect(projects).to eq([project1])
     end
diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb
index 753dc938c52dc4a8fbdb0a0dc165a51e426bf3bf..4a73552b8a6f6a230d4979fe609254132dfbc582 100644
--- a/spec/models/wiki_page_spec.rb
+++ b/spec/models/wiki_page_spec.rb
@@ -61,8 +61,8 @@ describe WikiPage, models: true do
         actual_order =
           grouped_entries.map do |page_or_dir|
             get_slugs(page_or_dir)
-          end.
-          flatten
+          end
+          .flatten
         expect(actual_order).to eq(expected_order)
       end
     end
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index 848fd547e104860f50e04200fc47c55f51490877..d70e15f006b5dd037046c27d8238139a7f5a8f70 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -80,8 +80,8 @@ describe ProjectPolicy, models: true do
 
     expect(project.team.member?(issue.author)).to eq(false)
 
-    expect(BasePolicy.class_for(project).abilities(user, project).can_set).
-      not_to include(:read_issue)
+    expect(BasePolicy.class_for(project).abilities(user, project).can_set)
+      .not_to include(:read_issue)
 
     expect(Ability.allowed?(user, :read_issue, project)).to be_falsy
   end
diff --git a/spec/presenters/ci/build_presenter_spec.rb b/spec/presenters/ci/build_presenter_spec.rb
index 2190ab0e82e297cc8f58f9610a21f2392e754d18..518e97d17a1c461e3305763d577e1d07b6675302 100644
--- a/spec/presenters/ci/build_presenter_spec.rb
+++ b/spec/presenters/ci/build_presenter_spec.rb
@@ -47,8 +47,8 @@ describe Ci::BuildPresenter do
     context 'when build is erased' do
       before do
         expect(presenter).to receive(:erased_by_user?).and_return(true)
-        expect(build).to receive(:erased_by).
-          and_return(double(:user, name: 'John Doe'))
+        expect(build).to receive(:erased_by)
+          .and_return(double(:user, name: 'John Doe'))
       end
 
       it 'returns the name of the eraser' do
diff --git a/spec/requests/api/deploy_keys_spec.rb b/spec/requests/api/deploy_keys_spec.rb
index 9c260f88f56f70a80609d836ecfe88532b7b7c73..32439981b609ad5c7e0f47207f4bee8c89165838 100644
--- a/spec/requests/api/deploy_keys_spec.rb
+++ b/spec/requests/api/deploy_keys_spec.rb
@@ -160,6 +160,16 @@ describe API::DeployKeys do
       expect(json_response['title']).to eq('new title')
       expect(json_response['can_push']).to eq(true)
     end
+
+    it 'updates a private ssh key from projects user has access with correct attributes' do
+      create(:deploy_keys_project, project: project2, deploy_key: private_deploy_key)
+
+      put api("/projects/#{project.id}/deploy_keys/#{private_deploy_key.id}", admin), { title: 'new title', can_push: true }
+
+      expect(json_response['id']).to eq(private_deploy_key.id)
+      expect(json_response['title']).to eq('new title')
+      expect(json_response['can_push']).to eq(true)
+    end
   end
 
   describe 'DELETE /projects/:id/deploy_keys/:key_id' do
diff --git a/spec/requests/api/files_spec.rb b/spec/requests/api/files_spec.rb
index c5ec8be4f219f39ef555a6d3031a90b1fff5892a..9e268adf95033c5c29cbcf6ee930f55da729659d 100644
--- a/spec/requests/api/files_spec.rb
+++ b/spec/requests/api/files_spec.rb
@@ -205,8 +205,8 @@ describe API::Files do
     end
 
     it "returns a 400 if editor fails to create file" do
-      allow_any_instance_of(Repository).to receive(:create_file).
-        and_raise(Repository::CommitError, 'Cannot create file')
+      allow_any_instance_of(Repository).to receive(:create_file)
+        .and_raise(Repository::CommitError, 'Cannot create file')
 
       post api(route("any%2Etxt"), user), valid_params
 
diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb
index bb53796cbd7c63214c82bae2e829b4c0217ac985..656f098aea8905c3a81d64eb74cc8c81db54a631 100644
--- a/spec/requests/api/groups_spec.rb
+++ b/spec/requests/api/groups_spec.rb
@@ -513,8 +513,8 @@ describe API::Groups do
     let(:project_path) { project.full_path.gsub('/', '%2F') }
 
     before(:each) do
-      allow_any_instance_of(Projects::TransferService).
-        to receive(:execute).and_return(true)
+      allow_any_instance_of(Projects::TransferService)
+        .to receive(:execute).and_return(true)
     end
 
     context "when authenticated as user" do
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index 86e15d896df7850ce98adedc0158a954c766b09a..6deaea956e03a1fd73ce230f871e93b7db9fb8a6 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -321,8 +321,6 @@ describe API::Internal do
     end
 
     context "archived project" do
-      let(:personal_project) { create(:empty_project, namespace: user.namespace) }
-
       before do
         project.team << [user, :developer]
         project.archive!
@@ -445,6 +443,42 @@ describe API::Internal do
         expect(json_response['status']).to be_truthy
       end
     end
+
+    context 'the project path was changed' do
+      let!(:old_path_to_repo) { project.repository.path_to_repo }
+      let!(:old_full_path) { project.full_path }
+      let(:project_moved_message) do
+        <<-MSG.strip_heredoc
+          Project '#{old_full_path}' was moved to '#{project.full_path}'.
+
+          Please update your Git remote and try again:
+
+            git remote set-url origin #{project.ssh_url_to_repo}
+        MSG
+      end
+
+      before do
+        project.team << [user, :developer]
+        project.path = 'new_path'
+        project.save!
+      end
+
+      it 'rejects the push' do
+        push_with_path(key, old_path_to_repo)
+
+        expect(response).to have_http_status(200)
+        expect(json_response['status']).to be_falsey
+        expect(json_response['message']).to eq(project_moved_message)
+      end
+
+      it 'rejects the SSH pull' do
+        pull_with_path(key, old_path_to_repo)
+
+        expect(response).to have_http_status(200)
+        expect(json_response['status']).to be_falsey
+        expect(json_response['message']).to eq(project_moved_message)
+      end
+    end
   end
 
   describe 'GET /internal/merge_request_urls' do
@@ -587,6 +621,17 @@ describe API::Internal do
     )
   end
 
+  def pull_with_path(key, path_to_repo, protocol = 'ssh')
+    post(
+      api("/internal/allowed"),
+      key_id: key.id,
+      project: path_to_repo,
+      action: 'git-upload-pack',
+      secret_token: secret_token,
+      protocol: protocol
+    )
+  end
+
   def push(key, project, protocol = 'ssh', env: nil)
     post(
       api("/internal/allowed"),
@@ -600,6 +645,19 @@ describe API::Internal do
     )
   end
 
+  def push_with_path(key, path_to_repo, protocol = 'ssh', env: nil)
+    post(
+      api("/internal/allowed"),
+      changes: 'd14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/master',
+      key_id: key.id,
+      project: path_to_repo,
+      action: 'git-receive-pack',
+      secret_token: secret_token,
+      protocol: protocol,
+      env: env
+    )
+  end
+
   def archive(key, project)
     post(
       api("/internal/allowed"),
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 16e5efb2f5b444776ac6e1f6a0074fe790e47a4a..4d0bd67c571a5ee7a70091cacb7bc6bf7e63bac9 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -334,14 +334,13 @@ describe API::MergeRequests do
              target_branch: 'master',
              author: user,
              labels: 'label, label2',
-             milestone_id: milestone.id,
-             remove_source_branch: true
+             milestone_id: milestone.id
 
         expect(response).to have_http_status(201)
         expect(json_response['title']).to eq('Test merge_request')
         expect(json_response['labels']).to eq(%w(label label2))
         expect(json_response['milestone']['id']).to eq(milestone.id)
-        expect(json_response['force_remove_source_branch']).to be_truthy
+        expect(json_response['force_remove_source_branch']).to be_falsy
       end
 
       it "returns 422 when source_branch equals target_branch" do
@@ -404,6 +403,27 @@ describe API::MergeRequests do
           expect(response).to have_http_status(409)
         end
       end
+
+      context 'accepts remove_source_branch parameter' do
+        let(:params) do
+          { title: 'Test merge_request',
+            source_branch: 'markdown',
+            target_branch: 'master',
+            author: user }
+        end
+
+        it 'sets force_remove_source_branch to false' do
+          post api("/projects/#{project.id}/merge_requests", user), params.merge(remove_source_branch: false)
+
+          expect(json_response['force_remove_source_branch']).to be_falsy
+        end
+
+        it 'sets force_remove_source_branch to true' do
+          post api("/projects/#{project.id}/merge_requests", user), params.merge(remove_source_branch: true)
+
+          expect(json_response['force_remove_source_branch']).to be_truthy
+        end
+      end
     end
 
     context 'forked projects' do
@@ -540,8 +560,8 @@ describe API::MergeRequests do
     end
 
     it "returns 406 if branch can't be merged" do
-      allow_any_instance_of(MergeRequest).
-        to receive(:can_be_merged?).and_return(false)
+      allow_any_instance_of(MergeRequest)
+        .to receive(:can_be_merged?).and_return(false)
 
       put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user)
 
diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb
index 40934c25afc6cd5033a8e8a27d96e085b751e76e..ab5ea3e8f2c42dba76c07240d33286963d116cbb 100644
--- a/spec/requests/api/milestones_spec.rb
+++ b/spec/requests/api/milestones_spec.rb
@@ -5,6 +5,9 @@ describe API::Milestones do
   let!(:project) { create(:empty_project, namespace: user.namespace ) }
   let!(:closed_milestone) { create(:closed_milestone, project: project, title: 'version1', description: 'closed milestone') }
   let!(:milestone) { create(:milestone, project: project, title: 'version2', description: 'open milestone') }
+  let(:label_1) { create(:label, title: 'label_1', project: project, priority: 1) }
+  let(:label_2) { create(:label, title: 'label_2', project: project, priority: 2) }
+  let(:label_3) { create(:label, title: 'label_3', project: project) }
 
   before do
     project.team << [user, :developer]
@@ -228,6 +231,18 @@ describe API::Milestones do
       expect(json_response.first['milestone']['title']).to eq(milestone.title)
     end
 
+    it 'returns project issues sorted by label priority' do
+      issue_1 = create(:labeled_issue, project: project, milestone: milestone, labels: [label_3])
+      issue_2 = create(:labeled_issue, project: project, milestone: milestone, labels: [label_1])
+      issue_3 = create(:labeled_issue, project: project, milestone: milestone, labels: [label_2])
+
+      get api("/projects/#{project.id}/milestones/#{milestone.id}/issues", user)
+
+      expect(json_response.first['id']).to eq(issue_2.id)
+      expect(json_response.second['id']).to eq(issue_3.id)
+      expect(json_response.third['id']).to eq(issue_1.id)
+    end
+
     it 'matches V4 response schema for a list of issues' do
       get api("/projects/#{project.id}/milestones/#{milestone.id}/issues", user)
 
@@ -244,8 +259,8 @@ describe API::Milestones do
     describe 'confidential issues' do
       let(:public_project) { create(:empty_project, :public) }
       let(:milestone) { create(:milestone, project: public_project) }
-      let(:issue) { create(:issue, project: public_project, position: 2) }
-      let(:confidential_issue) { create(:issue, confidential: true, project: public_project, position: 1) }
+      let(:issue) { create(:issue, project: public_project) }
+      let(:confidential_issue) { create(:issue, confidential: true, project: public_project) }
 
       before do
         public_project.team << [user, :developer]
@@ -285,7 +300,10 @@ describe API::Milestones do
         expect(json_response.map { |issue| issue['id'] }).to include(issue.id)
       end
 
-      it 'returns issues ordered by position asc' do
+      it 'returns issues ordered by label priority' do
+        issue.labels << label_2
+        confidential_issue.labels << label_1
+
         get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", user)
 
         expect(response).to have_http_status(200)
@@ -299,8 +317,8 @@ describe API::Milestones do
   end
 
   describe 'GET /projects/:id/milestones/:milestone_id/merge_requests' do
-    let(:merge_request) { create(:merge_request, source_project: project, position: 2) }
-    let(:another_merge_request) { create(:merge_request, :simple, source_project: project, position: 1) }
+    let(:merge_request) { create(:merge_request, source_project: project) }
+    let(:another_merge_request) { create(:merge_request, :simple, source_project: project) }
 
     before do
       milestone.merge_requests << merge_request
@@ -318,6 +336,18 @@ describe API::Milestones do
       expect(json_response.first['milestone']['title']).to eq(milestone.title)
     end
 
+    it 'returns project merge_requests sorted by label priority' do
+      merge_request_1 = create(:labeled_merge_request, source_branch: 'branch_1', source_project: project, milestone: milestone, labels: [label_2])
+      merge_request_2 = create(:labeled_merge_request, source_branch: 'branch_2', source_project: project, milestone: milestone, labels: [label_1])
+      merge_request_3 = create(:labeled_merge_request, source_branch: 'branch_3', source_project: project, milestone: milestone, labels: [label_3])
+
+      get api("/projects/#{project.id}/milestones/#{milestone.id}/merge_requests", user)
+
+      expect(json_response.first['id']).to eq(merge_request_2.id)
+      expect(json_response.second['id']).to eq(merge_request_1.id)
+      expect(json_response.third['id']).to eq(merge_request_3.id)
+    end
+
     it 'returns a 404 error if milestone id not found' do
       get api("/projects/#{project.id}/milestones/1234/merge_requests", user)
 
@@ -339,6 +369,8 @@ describe API::Milestones do
 
     it 'returns merge_requests ordered by position asc' do
       milestone.merge_requests << another_merge_request
+      another_merge_request.labels << label_1
+      merge_request.labels << label_2
 
       get api("/projects/#{project.id}/milestones/#{milestone.id}/merge_requests", user)
 
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index 03f2b5950ee7a1aa84939cc7622f3e51be7cbeb0..4701ad585c969ec362a5a2a8e2adb336b00e59a5 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -13,8 +13,8 @@ describe API::Notes do
   # For testing the cross-reference of a private issue in a public issue
   let(:private_user)    { create(:user) }
   let(:private_project) do
-    create(:empty_project, namespace: private_user.namespace).
-    tap { |p| p.team << [private_user, :master] }
+    create(:empty_project, namespace: private_user.namespace)
+    .tap { |p| p.team << [private_user, :master] }
   end
   let(:private_issue)    { create(:issue, project: private_project) }
 
diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb
index 4d4631322b1659ad6158257417c801413e881223..518639f45a26d4f0d5ce2794cafc7d6d4ac4caa1 100644
--- a/spec/requests/api/project_snippets_spec.rb
+++ b/spec/requests/api/project_snippets_spec.rb
@@ -102,23 +102,23 @@ describe API::ProjectSnippets do
 
       context 'when the snippet is private' do
         it 'creates the snippet' do
-          expect { create_snippet(project, visibility: 'private') }.
-            to change { Snippet.count }.by(1)
+          expect { create_snippet(project, visibility: 'private') }
+            .to change { Snippet.count }.by(1)
         end
       end
 
       context 'when the snippet is public' do
         it 'rejects the snippet' do
-          expect { create_snippet(project, visibility: 'public') }.
-            not_to change { Snippet.count }
+          expect { create_snippet(project, visibility: 'public') }
+            .not_to change { Snippet.count }
 
           expect(response).to have_http_status(400)
           expect(json_response['message']).to eq({ "error" => "Spam detected" })
         end
 
         it 'creates a spam log' do
-          expect { create_snippet(project, visibility: 'public') }.
-            to change { SpamLog.count }.by(1)
+          expect { create_snippet(project, visibility: 'public') }
+            .to change { SpamLog.count }.by(1)
         end
       end
     end
@@ -166,8 +166,8 @@ describe API::ProjectSnippets do
         let(:visibility_level) { Snippet::PRIVATE }
 
         it 'creates the snippet' do
-          expect { update_snippet(title: 'Foo') }.
-            to change { snippet.reload.title }.to('Foo')
+          expect { update_snippet(title: 'Foo') }
+            .to change { snippet.reload.title }.to('Foo')
         end
       end
 
@@ -175,13 +175,13 @@ describe API::ProjectSnippets do
         let(:visibility_level) { Snippet::PUBLIC }
 
         it 'rejects the snippet' do
-          expect { update_snippet(title: 'Foo') }.
-            not_to change { snippet.reload.title }
+          expect { update_snippet(title: 'Foo') }
+            .not_to change { snippet.reload.title }
         end
 
         it 'creates a spam log' do
-          expect { update_snippet(title: 'Foo') }.
-            to change { SpamLog.count }.by(1)
+          expect { update_snippet(title: 'Foo') }
+            .to change { SpamLog.count }.by(1)
         end
       end
 
@@ -189,16 +189,16 @@ describe API::ProjectSnippets do
         let(:visibility_level) { Snippet::PRIVATE }
 
         it 'rejects the snippet' do
-          expect { update_snippet(title: 'Foo', visibility: 'public') }.
-            not_to change { snippet.reload.title }
+          expect { update_snippet(title: 'Foo', visibility: 'public') }
+            .not_to change { snippet.reload.title }
 
           expect(response).to have_http_status(400)
           expect(json_response['message']).to eq({ "error" => "Spam detected" })
         end
 
         it 'creates a spam log' do
-          expect { update_snippet(title: 'Foo', visibility: 'public') }.
-            to change { SpamLog.count }.by(1)
+          expect { update_snippet(title: 'Foo', visibility: 'public') }
+            .to change { SpamLog.count }.by(1)
         end
       end
     end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index d92262a4c99fa54c6e5e6ed7ff4dceed4515dbef..fd7ff0b9cffd0eb7d8705198abab42d815644960 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -288,15 +288,15 @@ describe API::Projects do
     context 'maximum number of projects reached' do
       it 'does not create new project and respond with 403' do
         allow_any_instance_of(User).to receive(:projects_limit_left).and_return(0)
-        expect { post api('/projects', user2), name: 'foo' }.
-          to change {Project.count}.by(0)
+        expect { post api('/projects', user2), name: 'foo' }
+          .to change {Project.count}.by(0)
         expect(response).to have_http_status(403)
       end
     end
 
     it 'creates new project without path but with name and returns 201' do
-      expect { post api('/projects', user), name: 'Foo Project' }.
-        to change { Project.count }.by(1)
+      expect { post api('/projects', user), name: 'Foo Project' }
+        .to change { Project.count }.by(1)
       expect(response).to have_http_status(201)
 
       project = Project.first
@@ -306,8 +306,8 @@ describe API::Projects do
     end
 
     it 'creates new project without name but with path and returns 201' do
-      expect { post api('/projects', user), path: 'foo_project' }.
-        to change { Project.count }.by(1)
+      expect { post api('/projects', user), path: 'foo_project' }
+        .to change { Project.count }.by(1)
       expect(response).to have_http_status(201)
 
       project = Project.first
@@ -317,8 +317,8 @@ describe API::Projects do
     end
 
     it 'creates new project with name and path and returns 201' do
-      expect { post api('/projects', user), path: 'path-project-Foo', name: 'Foo Project' }.
-        to change { Project.count }.by(1)
+      expect { post api('/projects', user), path: 'path-project-Foo', name: 'Foo Project' }
+        .to change { Project.count }.by(1)
       expect(response).to have_http_status(201)
 
       project = Project.first
@@ -491,8 +491,8 @@ describe API::Projects do
     end
 
     it 'creates new project with name and path and returns 201' do
-      expect { post api("/projects/user/#{user.id}", admin), path: 'path-project-Foo', name: 'Foo Project' }.
-        to change { Project.count }.by(1)
+      expect { post api("/projects/user/#{user.id}", admin), path: 'path-project-Foo', name: 'Foo Project' }
+        .to change { Project.count }.by(1)
       expect(response).to have_http_status(201)
 
       project = Project.first
@@ -502,8 +502,8 @@ describe API::Projects do
     end
 
     it 'responds with 400 on failure and not project' do
-      expect { post api("/projects/user/#{user.id}", admin) }.
-        not_to change { Project.count }
+      expect { post api("/projects/user/#{user.id}", admin) }
+        .not_to change { Project.count }
 
       expect(response).to have_http_status(400)
       expect(json_response['error']).to eq('name is missing')
@@ -740,8 +740,8 @@ describe API::Projects do
             get api("/projects", user)
 
             expect(response).to have_http_status(200)
-            expect(json_response.first['permissions']['project_access']['access_level']).
-            to eq(Gitlab::Access::MASTER)
+            expect(json_response.first['permissions']['project_access']['access_level'])
+            .to eq(Gitlab::Access::MASTER)
             expect(json_response.first['permissions']['group_access']).to be_nil
           end
         end
@@ -752,8 +752,8 @@ describe API::Projects do
             get api("/projects/#{project.id}", user)
 
             expect(response).to have_http_status(200)
-            expect(json_response['permissions']['project_access']['access_level']).
-            to eq(Gitlab::Access::MASTER)
+            expect(json_response['permissions']['project_access']['access_level'])
+            .to eq(Gitlab::Access::MASTER)
             expect(json_response['permissions']['group_access']).to be_nil
           end
         end
@@ -770,8 +770,8 @@ describe API::Projects do
 
             expect(response).to have_http_status(200)
             expect(json_response['permissions']['project_access']).to be_nil
-            expect(json_response['permissions']['group_access']['access_level']).
-            to eq(Gitlab::Access::OWNER)
+            expect(json_response['permissions']['group_access']['access_level'])
+            .to eq(Gitlab::Access::OWNER)
           end
         end
       end
diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb
index d554c242916c7bbfb921b9bd08fec2f25e0be12a..339a57a1f209b02dfbb69e0816628c7587ac8df1 100644
--- a/spec/requests/api/runner_spec.rb
+++ b/spec/requests/api/runner_spec.rb
@@ -414,8 +414,8 @@ describe API::Runner do
 
           context 'when concurrently updating a job' do
             before do
-              expect_any_instance_of(Ci::Build).to receive(:run!).
-                  and_raise(ActiveRecord::StaleObjectError.new(nil, nil))
+              expect_any_instance_of(Ci::Build).to receive(:run!)
+                  .and_raise(ActiveRecord::StaleObjectError.new(nil, nil))
             end
 
             it 'returns a conflict' do
diff --git a/spec/requests/api/snippets_spec.rb b/spec/requests/api/snippets_spec.rb
index 8741cbd4e80b8a02db75054969abcb16f971440b..b20a187acfe988ad9396cb3133891d67bda9f9d8 100644
--- a/spec/requests/api/snippets_spec.rb
+++ b/spec/requests/api/snippets_spec.rb
@@ -142,23 +142,23 @@ describe API::Snippets do
 
       context 'when the snippet is private' do
         it 'creates the snippet' do
-          expect { create_snippet(visibility: 'private') }.
-            to change { Snippet.count }.by(1)
+          expect { create_snippet(visibility: 'private') }
+            .to change { Snippet.count }.by(1)
         end
       end
 
       context 'when the snippet is public' do
         it 'rejects the shippet' do
-          expect { create_snippet(visibility: 'public') }.
-            not_to change { Snippet.count }
+          expect { create_snippet(visibility: 'public') }
+            .not_to change { Snippet.count }
 
           expect(response).to have_http_status(400)
           expect(json_response['message']).to eq({ "error" => "Spam detected" })
         end
 
         it 'creates a spam log' do
-          expect { create_snippet(visibility: 'public') }.
-            to change { SpamLog.count }.by(1)
+          expect { create_snippet(visibility: 'public') }
+            .to change { SpamLog.count }.by(1)
         end
       end
     end
@@ -216,8 +216,8 @@ describe API::Snippets do
         let(:visibility_level) { Snippet::PRIVATE }
 
         it 'updates the snippet' do
-          expect { update_snippet(title: 'Foo') }.
-            to change { snippet.reload.title }.to('Foo')
+          expect { update_snippet(title: 'Foo') }
+            .to change { snippet.reload.title }.to('Foo')
         end
       end
 
@@ -225,16 +225,16 @@ describe API::Snippets do
         let(:visibility_level) { Snippet::PUBLIC }
 
         it 'rejects the shippet' do
-          expect { update_snippet(title: 'Foo') }.
-            not_to change { snippet.reload.title }
+          expect { update_snippet(title: 'Foo') }
+            .not_to change { snippet.reload.title }
 
           expect(response).to have_http_status(400)
           expect(json_response['message']).to eq({ "error" => "Spam detected" })
         end
 
         it 'creates a spam log' do
-          expect { update_snippet(title: 'Foo') }.
-            to change { SpamLog.count }.by(1)
+          expect { update_snippet(title: 'Foo') }
+            .to change { SpamLog.count }.by(1)
         end
       end
 
@@ -242,13 +242,13 @@ describe API::Snippets do
         let(:visibility_level) { Snippet::PRIVATE }
 
         it 'rejects the snippet' do
-          expect { update_snippet(title: 'Foo', visibility: 'public') }.
-            not_to change { snippet.reload.title }
+          expect { update_snippet(title: 'Foo', visibility: 'public') }
+            .not_to change { snippet.reload.title }
         end
 
         it 'creates a spam log' do
-          expect { update_snippet(title: 'Foo', visibility: 'public') }.
-            to change { SpamLog.count }.by(1)
+          expect { update_snippet(title: 'Foo', visibility: 'public') }
+            .to change { SpamLog.count }.by(1)
         end
       end
     end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 9dc4b6972a6ab67c847832d21b3718fc421fcedc..18000d917956658d742b82001b7711358e54ba2b 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -11,7 +11,7 @@ describe API::Users do
   let(:not_existing_user_id) { (User.maximum('id') || 0 ) + 10 }
   let(:not_existing_pat_id) { (PersonalAccessToken.maximum('id') || 0 ) + 10 }
 
-  describe "GET /users" do
+  describe 'GET /users' do
     context "when unauthenticated" do
       it "returns authentication error" do
         get api("/users")
@@ -76,6 +76,12 @@ describe API::Users do
 
         expect(response).to have_http_status(403)
       end
+
+      it 'does not reveal the `is_admin` flag of the user' do
+        get api('/users', user)
+
+        expect(json_response.first.keys).not_to include 'is_admin'
+      end
     end
 
     context "when admin" do
@@ -92,6 +98,7 @@ describe API::Users do
         expect(json_response.first.keys).to include 'two_factor_enabled'
         expect(json_response.first.keys).to include 'last_sign_in_at'
         expect(json_response.first.keys).to include 'confirmed_at'
+        expect(json_response.first.keys).to include 'is_admin'
       end
 
       it "returns an array of external users" do
@@ -282,14 +289,14 @@ describe API::Users do
            bio: 'g' * 256,
            projects_limit: -1
       expect(response).to have_http_status(400)
-      expect(json_response['message']['password']).
-        to eq(['is too short (minimum is 8 characters)'])
-      expect(json_response['message']['bio']).
-        to eq(['is too long (maximum is 255 characters)'])
-      expect(json_response['message']['projects_limit']).
-        to eq(['must be greater than or equal to 0'])
-      expect(json_response['message']['username']).
-        to eq([Gitlab::PathRegex.namespace_format_message])
+      expect(json_response['message']['password'])
+        .to eq(['is too short (minimum is 8 characters)'])
+      expect(json_response['message']['bio'])
+        .to eq(['is too long (maximum is 255 characters)'])
+      expect(json_response['message']['projects_limit'])
+        .to eq(['must be greater than or equal to 0'])
+      expect(json_response['message']['username'])
+        .to eq([Gitlab::PathRegex.namespace_format_message])
     end
 
     it "is not available for non admin users" do
@@ -377,6 +384,16 @@ describe API::Users do
       expect(user.reload.organization).to eq('GitLab')
     end
 
+    it 'updates user with avatar' do
+      put api("/users/#{user.id}", admin), { avatar: fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
+
+      user.reload
+
+      expect(user.avatar).to be_present
+      expect(response).to have_http_status(200)
+      expect(json_response['avatar_url']).to include(user.avatar_path)
+    end
+
     it 'updates user with his own email' do
       put api("/users/#{user.id}", admin), email: user.email
       expect(response).to have_http_status(200)
@@ -461,14 +478,14 @@ describe API::Users do
           bio: 'g' * 256,
           projects_limit: -1
       expect(response).to have_http_status(400)
-      expect(json_response['message']['password']).
-        to eq(['is too short (minimum is 8 characters)'])
-      expect(json_response['message']['bio']).
-        to eq(['is too long (maximum is 255 characters)'])
-      expect(json_response['message']['projects_limit']).
-        to eq(['must be greater than or equal to 0'])
-      expect(json_response['message']['username']).
-        to eq([Gitlab::PathRegex.namespace_format_message])
+      expect(json_response['message']['password'])
+        .to eq(['is too short (minimum is 8 characters)'])
+      expect(json_response['message']['bio'])
+        .to eq(['is too long (maximum is 255 characters)'])
+      expect(json_response['message']['projects_limit'])
+        .to eq(['must be greater than or equal to 0'])
+      expect(json_response['message']['username'])
+        .to eq([Gitlab::PathRegex.namespace_format_message])
     end
 
     it 'returns 400 if provider is missing for identity update' do
diff --git a/spec/requests/api/v3/files_spec.rb b/spec/requests/api/v3/files_spec.rb
index 378ca1720ffca683303dc98d75895a3409bc72f9..8b2d165c7633d2284b362f51b9f598a9cb4a42e7 100644
--- a/spec/requests/api/v3/files_spec.rb
+++ b/spec/requests/api/v3/files_spec.rb
@@ -126,8 +126,8 @@ describe API::V3::Files do
     end
 
     it "returns a 400 if editor fails to create file" do
-      allow_any_instance_of(Repository).to receive(:create_file).
-        and_raise(Repository::CommitError, 'Cannot create file')
+      allow_any_instance_of(Repository).to receive(:create_file)
+        .and_raise(Repository::CommitError, 'Cannot create file')
 
       post v3_api("/projects/#{project.id}/repository/files", user), valid_params
 
diff --git a/spec/requests/api/v3/groups_spec.rb b/spec/requests/api/v3/groups_spec.rb
index 98e8c954909d6a29ba35aa8ca874624bbd6bcfca..63c5707b2e452a8a3426b2f1adde6a265cac44bb 100644
--- a/spec/requests/api/v3/groups_spec.rb
+++ b/spec/requests/api/v3/groups_spec.rb
@@ -505,8 +505,8 @@ describe API::V3::Groups do
     let(:project_path) { "#{project.namespace.path}%2F#{project.path}" }
 
     before(:each) do
-      allow_any_instance_of(Projects::TransferService).
-        to receive(:execute).and_return(true)
+      allow_any_instance_of(Projects::TransferService)
+        .to receive(:execute).and_return(true)
     end
 
     context "when authenticated as user" do
diff --git a/spec/requests/api/v3/merge_requests_spec.rb b/spec/requests/api/v3/merge_requests_spec.rb
index f6ff96be5665074b7546cc70b9d8caf185c04743..4f9e63f2ace251eb8a4e4c2436b2d2b231e74790 100644
--- a/spec/requests/api/v3/merge_requests_spec.rb
+++ b/spec/requests/api/v3/merge_requests_spec.rb
@@ -432,8 +432,8 @@ describe API::MergeRequests do
     end
 
     it "returns 406 if branch can't be merged" do
-      allow_any_instance_of(MergeRequest).
-        to receive(:can_be_merged?).and_return(false)
+      allow_any_instance_of(MergeRequest)
+        .to receive(:can_be_merged?).and_return(false)
 
       put v3_api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user)
 
diff --git a/spec/requests/api/v3/notes_spec.rb b/spec/requests/api/v3/notes_spec.rb
index 2bae4a609315eef90b754ef23073d863d44a69a3..b5f98a9a545814af33d6335625c53535504d9b6e 100644
--- a/spec/requests/api/v3/notes_spec.rb
+++ b/spec/requests/api/v3/notes_spec.rb
@@ -13,8 +13,8 @@ describe API::V3::Notes do
   # For testing the cross-reference of a private issue in a public issue
   let(:private_user)    { create(:user) }
   let(:private_project) do
-    create(:empty_project, namespace: private_user.namespace).
-    tap { |p| p.team << [private_user, :master] }
+    create(:empty_project, namespace: private_user.namespace)
+    .tap { |p| p.team << [private_user, :master] }
   end
   let(:private_issue)    { create(:issue, project: private_project) }
 
diff --git a/spec/requests/api/v3/project_snippets_spec.rb b/spec/requests/api/v3/project_snippets_spec.rb
index 365e7365fda484ed6d07f4ebbeddd386a79cf47d..1950c64c6900d1d3243bc01f28b9dcbaa73f9dc7 100644
--- a/spec/requests/api/v3/project_snippets_spec.rb
+++ b/spec/requests/api/v3/project_snippets_spec.rb
@@ -85,23 +85,23 @@ describe API::ProjectSnippets do
 
       context 'when the snippet is private' do
         it 'creates the snippet' do
-          expect { create_snippet(project, visibility_level: Snippet::PRIVATE) }.
-            to change { Snippet.count }.by(1)
+          expect { create_snippet(project, visibility_level: Snippet::PRIVATE) }
+            .to change { Snippet.count }.by(1)
         end
       end
 
       context 'when the snippet is public' do
         it 'rejects the shippet' do
-          expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }.
-            not_to change { Snippet.count }
+          expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }
+            .not_to change { Snippet.count }
 
           expect(response).to have_http_status(400)
           expect(json_response['message']).to eq({ "error" => "Spam detected" })
         end
 
         it 'creates a spam log' do
-          expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }.
-            to change { SpamLog.count }.by(1)
+          expect { create_snippet(project, visibility_level: Snippet::PUBLIC) }
+            .to change { SpamLog.count }.by(1)
         end
       end
     end
@@ -147,8 +147,8 @@ describe API::ProjectSnippets do
         let(:visibility_level) { Snippet::PRIVATE }
 
         it 'creates the snippet' do
-          expect { update_snippet(title: 'Foo') }.
-            to change { snippet.reload.title }.to('Foo')
+          expect { update_snippet(title: 'Foo') }
+            .to change { snippet.reload.title }.to('Foo')
         end
       end
 
@@ -156,13 +156,13 @@ describe API::ProjectSnippets do
         let(:visibility_level) { Snippet::PUBLIC }
 
         it 'rejects the snippet' do
-          expect { update_snippet(title: 'Foo') }.
-            not_to change { snippet.reload.title }
+          expect { update_snippet(title: 'Foo') }
+            .not_to change { snippet.reload.title }
         end
 
         it 'creates a spam log' do
-          expect { update_snippet(title: 'Foo') }.
-            to change { SpamLog.count }.by(1)
+          expect { update_snippet(title: 'Foo') }
+            .to change { SpamLog.count }.by(1)
         end
       end
 
@@ -170,16 +170,16 @@ describe API::ProjectSnippets do
         let(:visibility_level) { Snippet::PRIVATE }
 
         it 'rejects the snippet' do
-          expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
-            not_to change { snippet.reload.title }
+          expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }
+            .not_to change { snippet.reload.title }
 
           expect(response).to have_http_status(400)
           expect(json_response['message']).to eq({ "error" => "Spam detected" })
         end
 
         it 'creates a spam log' do
-          expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }.
-            to change { SpamLog.count }.by(1)
+          expect { update_snippet(title: 'Foo', visibility_level: Snippet::PUBLIC) }
+            .to change { SpamLog.count }.by(1)
         end
       end
     end
diff --git a/spec/requests/api/v3/projects_spec.rb b/spec/requests/api/v3/projects_spec.rb
index 47cca4275afad0a96fe11a877fd02fdb6d95d812..cb74868324c5b835abb7c5aeb1ceacab78a82ca3 100644
--- a/spec/requests/api/v3/projects_spec.rb
+++ b/spec/requests/api/v3/projects_spec.rb
@@ -124,6 +124,36 @@ describe API::V3::Projects do
         end
       end
 
+      context 'and using archived' do
+        let!(:archived_project) { create(:empty_project, creator_id: user.id, namespace: user.namespace, archived: true) }
+
+        it 'returns archived project' do
+          get v3_api('/projects?archived=true', user)
+
+          expect(response).to have_http_status(200)
+          expect(json_response).to be_an Array
+          expect(json_response.length).to eq(1)
+          expect(json_response.first['id']).to eq(archived_project.id)
+        end
+
+        it 'returns non-archived project' do
+          get v3_api('/projects?archived=false', user)
+
+          expect(response).to have_http_status(200)
+          expect(json_response).to be_an Array
+          expect(json_response.length).to eq(1)
+          expect(json_response.first['id']).to eq(project.id)
+        end
+
+        it 'returns all project' do
+          get v3_api('/projects', user)
+
+          expect(response).to have_http_status(200)
+          expect(json_response).to be_an Array
+          expect(json_response.length).to eq(2)
+        end
+      end
+
       context 'and using sorting' do
         before do
           project2
@@ -301,15 +331,15 @@ describe API::V3::Projects do
     context 'maximum number of projects reached' do
       it 'does not create new project and respond with 403' do
         allow_any_instance_of(User).to receive(:projects_limit_left).and_return(0)
-        expect { post v3_api('/projects', user2), name: 'foo' }.
-          to change {Project.count}.by(0)
+        expect { post v3_api('/projects', user2), name: 'foo' }
+          .to change {Project.count}.by(0)
         expect(response).to have_http_status(403)
       end
     end
 
     it 'creates new project without path but with name and returns 201' do
-      expect { post v3_api('/projects', user), name: 'Foo Project' }.
-        to change { Project.count }.by(1)
+      expect { post v3_api('/projects', user), name: 'Foo Project' }
+        .to change { Project.count }.by(1)
       expect(response).to have_http_status(201)
 
       project = Project.first
@@ -319,8 +349,8 @@ describe API::V3::Projects do
     end
 
     it 'creates new project without name but with path and returns 201' do
-      expect { post v3_api('/projects', user), path: 'foo_project' }.
-        to change { Project.count }.by(1)
+      expect { post v3_api('/projects', user), path: 'foo_project' }
+        .to change { Project.count }.by(1)
       expect(response).to have_http_status(201)
 
       project = Project.first
@@ -330,8 +360,8 @@ describe API::V3::Projects do
     end
 
     it 'creates new project name and path and returns 201' do
-      expect { post v3_api('/projects', user), path: 'foo-Project', name: 'Foo Project' }.
-        to change { Project.count }.by(1)
+      expect { post v3_api('/projects', user), path: 'foo-Project', name: 'Foo Project' }
+        .to change { Project.count }.by(1)
       expect(response).to have_http_status(201)
 
       project = Project.first
@@ -489,8 +519,8 @@ describe API::V3::Projects do
     end
 
     it 'responds with 400 on failure and not project' do
-      expect { post v3_api("/projects/user/#{user.id}", admin) }.
-        not_to change { Project.count }
+      expect { post v3_api("/projects/user/#{user.id}", admin) }
+        .not_to change { Project.count }
 
       expect(response).to have_http_status(400)
       expect(json_response['error']).to eq('name is missing')
@@ -716,8 +746,8 @@ describe API::V3::Projects do
             get v3_api("/projects", user)
 
             expect(response).to have_http_status(200)
-            expect(json_response.first['permissions']['project_access']['access_level']).
-            to eq(Gitlab::Access::MASTER)
+            expect(json_response.first['permissions']['project_access']['access_level'])
+            .to eq(Gitlab::Access::MASTER)
             expect(json_response.first['permissions']['group_access']).to be_nil
           end
         end
@@ -728,8 +758,8 @@ describe API::V3::Projects do
             get v3_api("/projects/#{project.id}", user)
 
             expect(response).to have_http_status(200)
-            expect(json_response['permissions']['project_access']['access_level']).
-            to eq(Gitlab::Access::MASTER)
+            expect(json_response['permissions']['project_access']['access_level'])
+            .to eq(Gitlab::Access::MASTER)
             expect(json_response['permissions']['group_access']).to be_nil
           end
         end
@@ -744,8 +774,8 @@ describe API::V3::Projects do
 
             expect(response).to have_http_status(200)
             expect(json_response['permissions']['project_access']).to be_nil
-            expect(json_response['permissions']['group_access']['access_level']).
-            to eq(Gitlab::Access::OWNER)
+            expect(json_response['permissions']['group_access']['access_level'])
+            .to eq(Gitlab::Access::OWNER)
           end
         end
       end
diff --git a/spec/requests/api/v3/snippets_spec.rb b/spec/requests/api/v3/snippets_spec.rb
index 4f02b7b1a5483203dd4451a038bedd479d13d5b1..1bc2258ebd35ee647917fca117dee2ef0320f220 100644
--- a/spec/requests/api/v3/snippets_spec.rb
+++ b/spec/requests/api/v3/snippets_spec.rb
@@ -112,21 +112,21 @@ describe API::V3::Snippets do
 
       context 'when the snippet is private' do
         it 'creates the snippet' do
-          expect { create_snippet(visibility_level: Snippet::PRIVATE) }.
-            to change { Snippet.count }.by(1)
+          expect { create_snippet(visibility_level: Snippet::PRIVATE) }
+            .to change { Snippet.count }.by(1)
         end
       end
 
       context 'when the snippet is public' do
         it 'rejects the shippet' do
-          expect { create_snippet(visibility_level: Snippet::PUBLIC) }.
-            not_to change { Snippet.count }
+          expect { create_snippet(visibility_level: Snippet::PUBLIC) }
+            .not_to change { Snippet.count }
           expect(response).to have_http_status(400)
         end
 
         it 'creates a spam log' do
-          expect { create_snippet(visibility_level: Snippet::PUBLIC) }.
-            to change { SpamLog.count }.by(1)
+          expect { create_snippet(visibility_level: Snippet::PUBLIC) }
+            .to change { SpamLog.count }.by(1)
         end
       end
     end
diff --git a/spec/requests/api/v3/users_spec.rb b/spec/requests/api/v3/users_spec.rb
index e9c57f7c6c37cefbd2cb3135326b77376b0e4d1f..6d7401f9764439e3bc4befd35e558102f9c541c0 100644
--- a/spec/requests/api/v3/users_spec.rb
+++ b/spec/requests/api/v3/users_spec.rb
@@ -7,6 +7,38 @@ describe API::V3::Users do
   let(:email)   { create(:email, user: user) }
   let(:ldap_blocked_user) { create(:omniauth_user, provider: 'ldapmain', state: 'ldap_blocked') }
 
+  describe 'GET /users' do
+    context 'when authenticated' do
+      it 'returns an array of users' do
+        get v3_api('/users', user)
+
+        expect(response).to have_http_status(200)
+        expect(response).to include_pagination_headers
+        expect(json_response).to be_an Array
+        username = user.username
+        expect(json_response.detect do |user|
+          user['username'] == username
+        end['username']).to eq(username)
+      end
+    end
+
+    context 'when authenticated as user' do
+      it 'does not reveal the `is_admin` flag of the user' do
+        get v3_api('/users', user)
+
+        expect(json_response.first.keys).not_to include 'is_admin'
+      end
+    end
+
+    context 'when authenticated as admin' do
+      it 'reveals the `is_admin` flag of the user' do
+        get v3_api('/users', admin)
+
+        expect(json_response.first.keys).to include 'is_admin'
+      end
+    end
+  end
+
   describe 'GET /user/:id/keys' do
     before { admin }
 
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index 83c675792f4cc3486ad3f6040b7c5ed81ee89587..c969d08d0ddd529e43c6470131f1594e8f56db00 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -91,8 +91,8 @@ describe Ci::API::Builds do
 
         context 'when concurrently updating build' do
           before do
-            expect_any_instance_of(Ci::Build).to receive(:run!).
-              and_raise(ActiveRecord::StaleObjectError.new(nil, nil))
+            expect_any_instance_of(Ci::Build).to receive(:run!)
+              .and_raise(ActiveRecord::StaleObjectError.new(nil, nil))
           end
 
           it 'returns a conflict' do
@@ -670,8 +670,8 @@ describe Ci::API::Builds do
                 build.reload
                 expect(response).to have_http_status(201)
                 expect(json_response['artifacts_expire_at']).not_to be_empty
-                expect(build.artifacts_expire_at).
-                  to be_within(5.minutes).of(7.days.from_now)
+                expect(build.artifacts_expire_at)
+                  .to be_within(5.minutes).of(7.days.from_now)
               end
             end
 
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index dce78faefc9468487e3135100c28a4192d7c8632..185679e1a0fbd8b4563259768b8073f20e100f0e 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -316,6 +316,26 @@ describe 'Git HTTP requests', lib: true do
             it_behaves_like 'pushes require Basic HTTP Authentication'
           end
         end
+
+        context 'and the user requests a redirected path' do
+          let!(:redirect) { project.route.create_redirect('foo/bar') }
+          let(:path) { "#{redirect.path}.git" }
+          let(:project_moved_message) do
+            <<-MSG.strip_heredoc
+              Project '#{redirect.path}' was moved to '#{project.full_path}'.
+
+              Please update your Git remote and try again:
+
+                git remote set-url origin #{project.http_url_to_repo}
+            MSG
+          end
+
+          it 'downloads get status 404 with "project was moved" message' do
+            clone_get(path, {})
+            expect(response).to have_http_status(:not_found)
+            expect(response.body).to match(project_moved_message)
+          end
+        end
       end
 
       context "when the project is private" do
@@ -463,8 +483,8 @@ describe 'Git HTTP requests', lib: true do
                 context 'when LDAP is configured' do
                   before do
                     allow(Gitlab::LDAP::Config).to receive(:enabled?).and_return(true)
-                    allow_any_instance_of(Gitlab::LDAP::Authentication).
-                      to receive(:login).and_return(nil)
+                    allow_any_instance_of(Gitlab::LDAP::Authentication)
+                      .to receive(:login).and_return(nil)
                   end
 
                   it 'does not display the personal access token error message' do
@@ -505,6 +525,33 @@ describe 'Git HTTP requests', lib: true do
                   Rack::Attack::Allow2Ban.reset(ip, options)
                 end
               end
+
+              context 'and the user requests a redirected path' do
+                let!(:redirect) { project.route.create_redirect('foo/bar') }
+                let(:path) { "#{redirect.path}.git" }
+                let(:project_moved_message) do
+                  <<-MSG.strip_heredoc
+                    Project '#{redirect.path}' was moved to '#{project.full_path}'.
+
+                    Please update your Git remote and try again:
+
+                      git remote set-url origin #{project.http_url_to_repo}
+                  MSG
+                end
+
+                it 'downloads get status 404 with "project was moved" message' do
+                  clone_get(path, env)
+                  expect(response).to have_http_status(:not_found)
+                  expect(response.body).to match(project_moved_message)
+                end
+
+                it 'uploads get status 404 with "project was moved" message' do
+                  upload(path, env) do |response|
+                    expect(response).to have_http_status(:not_found)
+                    expect(response.body).to match(project_moved_message)
+                  end
+                end
+              end
             end
 
             context "when the user doesn't have access to the project" do
@@ -680,7 +727,7 @@ describe 'Git HTTP requests', lib: true do
         end
 
         context "POST git-receive-pack" do
-          it "failes to find a route" do
+          it "fails to find a route" do
             expect { push_post(project.path_with_namespace) }.to raise_error(ActionController::RoutingError)
           end
         end
diff --git a/spec/rubocop/cop/migration/update_column_in_batches_spec.rb b/spec/rubocop/cop/migration/update_column_in_batches_spec.rb
index 968dcd6232ef55f12ce8696b1d4c5a8c85ec902a..38b8f439a5585f94a6638f91d06822265b3da773 100644
--- a/spec/rubocop/cop/migration/update_column_in_batches_spec.rb
+++ b/spec/rubocop/cop/migration/update_column_in_batches_spec.rb
@@ -54,8 +54,8 @@ describe RuboCop::Cop::Migration::UpdateColumnInBatches do
       aggregate_failures do
         expect(cop.offenses.size).to eq(1)
         expect(cop.offenses.map(&:line)).to eq([2])
-        expect(cop.offenses.first.message).
-          to include("`#{relative_spec_filepath}`")
+        expect(cop.offenses.first.message)
+          .to include("`#{relative_spec_filepath}`")
       end
     end
   end
diff --git a/spec/services/create_deployment_service_spec.rb b/spec/services/create_deployment_service_spec.rb
index 6cf4342ad4c08964eaf9011337b5c356787f8eac..dfab6ebf372a8a9d86c4dd8825ebba07f0983f48 100644
--- a/spec/services/create_deployment_service_spec.rb
+++ b/spec/services/create_deployment_service_spec.rb
@@ -122,6 +122,61 @@ describe CreateDeploymentService, services: true do
     end
   end
 
+  describe '#expanded_environment_url' do
+    subject { service.send(:expanded_environment_url) }
+
+    context 'when yaml environment uses $CI_COMMIT_REF_NAME' do
+      let(:job) do
+        create(:ci_build,
+               ref: 'master',
+               options: { environment: { url: 'http://review/$CI_COMMIT_REF_NAME' } })
+      end
+
+      it { is_expected.to eq('http://review/master') }
+    end
+
+    context 'when yaml environment uses $CI_ENVIRONMENT_SLUG' do
+      let(:job) do
+        create(:ci_build,
+               ref: 'master',
+               environment: 'production',
+               options: { environment: { url: 'http://review/$CI_ENVIRONMENT_SLUG' } })
+      end
+
+      let!(:environment) do
+        create(:environment,
+          project: job.project,
+          name: 'production',
+          slug: 'prod-slug',
+          external_url: 'http://review/old')
+      end
+
+      it { is_expected.to eq('http://review/prod-slug') }
+    end
+
+    context 'when yaml environment uses yaml_variables containing symbol keys' do
+      let(:job) do
+        create(:ci_build,
+               yaml_variables: [{ key: :APP_HOST, value: 'host' }],
+               options: { environment: { url: 'http://review/$APP_HOST' } })
+      end
+
+      it { is_expected.to eq('http://review/host') }
+    end
+
+    context 'when yaml environment does not have url' do
+      let(:job) { create(:ci_build, environment: 'staging') }
+
+      let!(:environment) do
+        create(:environment, project: job.project, name: job.environment)
+      end
+
+      it 'returns the external_url from persisted environment' do
+        is_expected.to be_nil
+      end
+    end
+  end
+
   describe 'processing of builds' do
     shared_examples 'does not create deployment' do
       it 'does not create a new deployment' do
diff --git a/spec/services/files/update_service_spec.rb b/spec/services/files/update_service_spec.rb
index 16bca66766ac952058ccec4b4dbe28eebef64e48..cc950ae6bb3a7a0fab0ab7cf19724d50115b4e8d 100644
--- a/spec/services/files/update_service_spec.rb
+++ b/spec/services/files/update_service_spec.rb
@@ -32,8 +32,8 @@ describe Files::UpdateService do
       let(:last_commit_sha) { "foo" }
 
       it "returns a hash with the correct error message and a :error status " do
-        expect { subject.execute }.
-          to raise_error(Files::UpdateService::FileChangedError,
+        expect { subject.execute }
+          .to raise_error(Files::UpdateService::FileChangedError,
                          "You are attempting to update a file that has changed since you started editing it.")
       end
     end
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
index bcd1fb64ab973256d996f9e11e94cab206c442bf..ca827fc0f39de230bc814b870e925862b4c8dc02 100644
--- a/spec/services/git_push_service_spec.rb
+++ b/spec/services/git_push_service_spec.rb
@@ -158,8 +158,8 @@ describe GitPushService, services: true do
 
     context "Updates merge requests" do
       it "when pushing a new branch for the first time" do
-        expect(UpdateMergeRequestsWorker).to receive(:perform_async).
-                                                with(project.id, user.id, @blankrev, 'newrev', 'refs/heads/master')
+        expect(UpdateMergeRequestsWorker).to receive(:perform_async)
+                                                .with(project.id, user.id, @blankrev, 'newrev', 'refs/heads/master')
         execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master' )
       end
     end
@@ -283,8 +283,8 @@ describe GitPushService, services: true do
         author_email: commit_author.email
       )
 
-      allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit).
-        and_return(commit)
+      allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit)
+        .and_return(commit)
 
       allow(project.repository).to receive(:commits_between).and_return([commit])
     end
@@ -341,8 +341,8 @@ describe GitPushService, services: true do
         committed_date: commit_time
       )
 
-      allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit).
-        and_return(commit)
+      allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit)
+        .and_return(commit)
 
       allow(project.repository).to receive(:commits_between).and_return([commit])
     end
@@ -377,11 +377,11 @@ describe GitPushService, services: true do
         author_email: commit_author.email
       )
 
-      allow(project.repository).to receive(:commits_between).
-        and_return([closing_commit])
+      allow(project.repository).to receive(:commits_between)
+        .and_return([closing_commit])
 
-      allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit).
-        and_return(closing_commit)
+      allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit)
+        .and_return(closing_commit)
 
       project.team << [commit_author, :master]
     end
@@ -403,8 +403,8 @@ describe GitPushService, services: true do
       end
 
       it "doesn't close issues when external issue tracker is in use" do
-        allow_any_instance_of(Project).to receive(:default_issues_tracker?).
-          and_return(false)
+        allow_any_instance_of(Project).to receive(:default_issues_tracker?)
+          .and_return(false)
         external_issue_tracker = double(title: 'My Tracker', issue_path: issue.iid, reference_pattern: project.issue_reference_pattern)
         allow_any_instance_of(Project).to receive(:external_issue_tracker).and_return(external_issue_tracker)
 
@@ -598,13 +598,13 @@ describe GitPushService, services: true do
         commit = double(:commit)
         diff = double(:diff, new_path: 'README.md')
 
-        expect(commit).to receive(:raw_deltas).
-          and_return([diff])
+        expect(commit).to receive(:raw_deltas)
+          .and_return([diff])
 
         service.push_commits = [commit]
 
-        expect(ProjectCacheWorker).to receive(:perform_async).
-          with(project.id, %i(readme), %i(commit_count repository_size))
+        expect(ProjectCacheWorker).to receive(:perform_async)
+          .with(project.id, %i(readme), %i(commit_count repository_size))
 
         service.update_caches
       end
@@ -616,9 +616,9 @@ describe GitPushService, services: true do
       end
 
       it 'does not flush any conditional caches' do
-        expect(ProjectCacheWorker).to receive(:perform_async).
-          with(project.id, [], %i(commit_count repository_size)).
-          and_call_original
+        expect(ProjectCacheWorker).to receive(:perform_async)
+          .with(project.id, [], %i(commit_count repository_size))
+          .and_call_original
 
         service.update_caches
       end
@@ -635,8 +635,8 @@ describe GitPushService, services: true do
     end
 
     it 'only schedules a limited number of commits' do
-      allow(service).to receive(:push_commits).
-        and_return(Array.new(1000, double(:commit, to_hash: {}, matches_cross_reference_regex?: true)))
+      allow(service).to receive(:push_commits)
+        .and_return(Array.new(1000, double(:commit, to_hash: {}, matches_cross_reference_regex?: true)))
 
       expect(ProcessCommitWorker).to receive(:perform_async).exactly(100).times
 
@@ -644,8 +644,8 @@ describe GitPushService, services: true do
     end
 
     it "skips commits which don't include cross-references" do
-      allow(service).to receive(:push_commits).
-        and_return([double(:commit, to_hash: {}, matches_cross_reference_regex?: false)])
+      allow(service).to receive(:push_commits)
+        .and_return([double(:commit, to_hash: {}, matches_cross_reference_regex?: false)])
 
       expect(ProcessCommitWorker).not_to receive(:perform_async)
 
diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb
index be0e829880e1a28886f2dca3ad66992ef204ceb1..d6f4c69406979c284a135907fcb873fc107f0d18 100644
--- a/spec/services/issues/close_service_spec.rb
+++ b/spec/services/issues/close_service_spec.rb
@@ -18,26 +18,26 @@ describe Issues::CloseService, services: true do
     let(:service) { described_class.new(project, user) }
 
     it 'checks if the user is authorized to update the issue' do
-      expect(service).to receive(:can?).with(user, :update_issue, issue).
-        and_call_original
+      expect(service).to receive(:can?).with(user, :update_issue, issue)
+        .and_call_original
 
       service.execute(issue)
     end
 
     it 'does not close the issue when the user is not authorized to do so' do
-      allow(service).to receive(:can?).with(user, :update_issue, issue).
-        and_return(false)
+      allow(service).to receive(:can?).with(user, :update_issue, issue)
+        .and_return(false)
 
       expect(service).not_to receive(:close_issue)
       expect(service.execute(issue)).to eq(issue)
     end
 
     it 'closes the issue when the user is authorized to do so' do
-      allow(service).to receive(:can?).with(user, :update_issue, issue).
-        and_return(true)
+      allow(service).to receive(:can?).with(user, :update_issue, issue)
+        .and_return(true)
 
-      expect(service).to receive(:close_issue).
-        with(issue, commit: nil, notifications: true, system_note: true)
+      expect(service).to receive(:close_issue)
+        .with(issue, commit: nil, notifications: true, system_note: true)
 
       service.execute(issue)
     end
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index 370bd35220005fb91c3fed7a12f344190bfe99c7..ae9d2b2855d0aa0ba3731acce4107c4c1cc5ecdf 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -206,9 +206,9 @@ describe Issues::CreateService, services: true do
       end
     end
 
-    it_behaves_like 'new issuable record that supports slash commands'
+    it_behaves_like 'new issuable record that supports quick actions'
 
-    context 'Slash commands' do
+    context 'Quick actions' do
       context 'with assignee and milestone in params and command' do
         let(:opts) do
           {
diff --git a/spec/services/labels/promote_service_spec.rb b/spec/services/labels/promote_service_spec.rb
index 4b90ad19640cf07eb95540eb69b4e172fe950f92..500afdfb9161bd5e5178e4c56e0b1fe8b724c4c8 100644
--- a/spec/services/labels/promote_service_spec.rb
+++ b/spec/services/labels/promote_service_spec.rb
@@ -66,9 +66,9 @@ describe Labels::PromoteService, services: true do
       end
 
       it 'recreates the label as a group label' do
-        expect { service.execute(project_label_1_1) }.
-          to change(project_1.labels, :count).by(-1).
-          and change(group_1.labels, :count).by(1)
+        expect { service.execute(project_label_1_1) }
+          .to change(project_1.labels, :count).by(-1)
+          .and change(group_1.labels, :count).by(1)
         expect(new_label).not_to be_nil
       end
 
diff --git a/spec/services/members/destroy_service_spec.rb b/spec/services/members/destroy_service_spec.rb
index 41450c67d7e77c1798e3a46cbcc2405b104be838..9ab7839430c6e86197621ade062e019bab1a6adb 100644
--- a/spec/services/members/destroy_service_spec.rb
+++ b/spec/services/members/destroy_service_spec.rb
@@ -104,8 +104,8 @@ describe Members::DestroyService, services: true do
         let(:params) { { id: project.members.find_by!(user_id: user.id).id } }
 
         it 'destroys the member' do
-          expect { described_class.new(project, user, params).execute }.
-            to change { project.members.count }.by(-1)
+          expect { described_class.new(project, user, params).execute }
+            .to change { project.members.count }.by(-1)
         end
       end
     end
diff --git a/spec/services/merge_requests/close_service_spec.rb b/spec/services/merge_requests/close_service_spec.rb
index 154f30aac3b4ab2ed2878eebf885553e9cf9aa15..074d4672b06cbbdc39ca5d1055bdbcb47f1e20e4 100644
--- a/spec/services/merge_requests/close_service_spec.rb
+++ b/spec/services/merge_requests/close_service_spec.rb
@@ -32,8 +32,8 @@ describe MergeRequests::CloseService, services: true do
       it { expect(@merge_request).to be_closed }
 
       it 'executes hooks with close action' do
-        expect(service).to have_received(:execute_hooks).
-                               with(@merge_request, 'close')
+        expect(service).to have_received(:execute_hooks)
+                               .with(@merge_request, 'close')
       end
 
       it 'sends email to user2 about assign of new merge_request' do
diff --git a/spec/services/merge_requests/conflicts/resolve_service_spec.rb b/spec/services/merge_requests/conflicts/resolve_service_spec.rb
index c77e6e9cd50dd51ae984be2ed275c32b5b707968..6f49a65d795af795de962b85bfae4b51a238da73 100644
--- a/spec/services/merge_requests/conflicts/resolve_service_spec.rb
+++ b/spec/services/merge_requests/conflicts/resolve_service_spec.rb
@@ -64,9 +64,9 @@ describe MergeRequests::Conflicts::ResolveService do
         end
 
         it 'creates a commit with the correct parents' do
-          expect(merge_request.source_branch_head.parents.map(&:id)).
-            to eq(%w(1450cd639e0bc6721eb02800169e464f212cde06
-                     824be604a34828eb682305f0d963056cfac87b2d))
+          expect(merge_request.source_branch_head.parents.map(&:id))
+            .to eq(%w(1450cd639e0bc6721eb02800169e464f212cde06
+                      824be604a34828eb682305f0d963056cfac87b2d))
         end
       end
 
@@ -129,9 +129,8 @@ describe MergeRequests::Conflicts::ResolveService do
         it 'creates a commit with the correct parents' do
           resolve_conflicts
 
-          expect(merge_request_from_fork.source_branch_head.parents.map(&:id)).
-            to eq(['404fa3fc7c2c9b5dacff102f353bdf55b1be2813',
-                   target_head])
+          expect(merge_request_from_fork.source_branch_head.parents.map(&:id))
+            .to eq(['404fa3fc7c2c9b5dacff102f353bdf55b1be2813', target_head])
         end
       end
     end
@@ -169,9 +168,9 @@ describe MergeRequests::Conflicts::ResolveService do
       end
 
       it 'creates a commit with the correct parents' do
-        expect(merge_request.source_branch_head.parents.map(&:id)).
-          to eq(%w(1450cd639e0bc6721eb02800169e464f212cde06
-                   824be604a34828eb682305f0d963056cfac87b2d))
+        expect(merge_request.source_branch_head.parents.map(&:id))
+          .to eq(%w(1450cd639e0bc6721eb02800169e464f212cde06
+                    824be604a34828eb682305f0d963056cfac87b2d))
       end
 
       it 'sets the content to the content given' do
@@ -204,8 +203,8 @@ describe MergeRequests::Conflicts::ResolveService do
       end
 
       it 'raises a MissingResolution error' do
-        expect { service.execute(user, invalid_params) }.
-          to raise_error(Gitlab::Conflict::File::MissingResolution)
+        expect { service.execute(user, invalid_params) }
+          .to raise_error(Gitlab::Conflict::File::MissingResolution)
       end
     end
 
@@ -230,8 +229,8 @@ describe MergeRequests::Conflicts::ResolveService do
       end
 
       it 'raises a MissingResolution error' do
-        expect { service.execute(user, invalid_params) }.
-          to raise_error(Gitlab::Conflict::File::MissingResolution)
+        expect { service.execute(user, invalid_params) }
+          .to raise_error(Gitlab::Conflict::File::MissingResolution)
       end
     end
 
@@ -250,8 +249,8 @@ describe MergeRequests::Conflicts::ResolveService do
       end
 
       it 'raises a MissingFiles error' do
-        expect { service.execute(user, invalid_params) }.
-          to raise_error(described_class::MissingFiles)
+        expect { service.execute(user, invalid_params) }
+          .to raise_error(described_class::MissingFiles)
       end
     end
   end
diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb
index 13fee953e41c873102e073485d41b5711511d098..36a2b6724737aa80f2588ac8394d627b8359a6a7 100644
--- a/spec/services/merge_requests/create_service_spec.rb
+++ b/spec/services/merge_requests/create_service_spec.rb
@@ -83,9 +83,9 @@ describe MergeRequests::CreateService, services: true do
         let!(:pipeline_3) { create(:ci_pipeline, project: project, ref: "other_branch", project_id: project.id) }
 
         before do
-          project.merge_requests.
-            where(source_branch: opts[:source_branch], target_branch: opts[:target_branch]).
-            destroy_all
+          project.merge_requests
+            .where(source_branch: opts[:source_branch], target_branch: opts[:target_branch])
+            .destroy_all
         end
 
         it 'sets head pipeline' do
@@ -108,7 +108,7 @@ describe MergeRequests::CreateService, services: true do
       end
     end
 
-    it_behaves_like 'new issuable record that supports slash commands' do
+    it_behaves_like 'new issuable record that supports quick actions' do
       let(:default_params) do
         {
           source_branch: 'feature',
@@ -117,7 +117,7 @@ describe MergeRequests::CreateService, services: true do
       end
     end
 
-    context 'Slash commands' do
+    context 'Quick actions' do
       context 'with assignee and milestone in params and command' do
         let(:merge_request) { described_class.new(project, user, opts).execute }
         let(:milestone) { create(:milestone, project: project) }
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index b3b188a805f5e45e2cf66322cfb09a7016243948..711059208c1a7ee1200c06bd0c91ebbe12495637 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -141,9 +141,9 @@ describe MergeRequests::MergeService, services: true do
       end
 
       it 'removes the source branch' do
-        expect(DeleteBranchService).to receive(:new).
-          with(merge_request.source_project, merge_request.author).
-          and_call_original
+        expect(DeleteBranchService).to receive(:new)
+          .with(merge_request.source_project, merge_request.author)
+          .and_call_original
         service.execute(merge_request)
       end
     end
diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index 1f109eab268807b1400ea6402d4cc55dfa22a47b..671a932441e433347bf23609b193be1e1661afd1 100644
--- a/spec/services/merge_requests/refresh_service_spec.rb
+++ b/spec/services/merge_requests/refresh_service_spec.rb
@@ -57,8 +57,8 @@ describe MergeRequests::RefreshService, services: true do
       end
 
       it 'executes hooks with update action' do
-        expect(refresh_service).to have_received(:execute_hooks).
-          with(@merge_request, 'update', @oldrev)
+        expect(refresh_service).to have_received(:execute_hooks)
+          .with(@merge_request, 'update', @oldrev)
 
         expect(@merge_request.notes).not_to be_empty
         expect(@merge_request).to be_open
@@ -83,8 +83,8 @@ describe MergeRequests::RefreshService, services: true do
       end
 
       it 'executes hooks with update action' do
-        expect(refresh_service).to have_received(:execute_hooks).
-          with(@merge_request, 'update', @oldrev)
+        expect(refresh_service).to have_received(:execute_hooks)
+          .with(@merge_request, 'update', @oldrev)
 
         expect(@merge_request.notes).not_to be_empty
         expect(@merge_request).to be_open
@@ -146,8 +146,8 @@ describe MergeRequests::RefreshService, services: true do
         end
 
         it 'executes hooks with update action' do
-          expect(refresh_service).to have_received(:execute_hooks).
-            with(@fork_merge_request, 'update', @oldrev)
+          expect(refresh_service).to have_received(:execute_hooks)
+            .with(@fork_merge_request, 'update', @oldrev)
 
           expect(@merge_request.notes).to be_empty
           expect(@merge_request).to be_open
@@ -228,8 +228,8 @@ describe MergeRequests::RefreshService, services: true do
       let(:refresh_service) { service.new(@fork_project, @user) }
 
       it 'refreshes the merge request' do
-        expect(refresh_service).to receive(:execute_hooks).
-                                       with(@fork_merge_request, 'update', Gitlab::Git::BLANK_SHA)
+        expect(refresh_service).to receive(:execute_hooks)
+                                       .with(@fork_merge_request, 'update', Gitlab::Git::BLANK_SHA)
         allow_any_instance_of(Repository).to receive(:merge_base).and_return(@oldrev)
 
         refresh_service.execute(Gitlab::Git::BLANK_SHA, @newrev, 'refs/heads/master')
diff --git a/spec/services/merge_requests/reopen_service_spec.rb b/spec/services/merge_requests/reopen_service_spec.rb
index b6d4db2f922e638e65b49a26966e7e0b5cd799cd..6cc403bdb7fba068c1ae737ea17273d74c120433 100644
--- a/spec/services/merge_requests/reopen_service_spec.rb
+++ b/spec/services/merge_requests/reopen_service_spec.rb
@@ -31,8 +31,8 @@ describe MergeRequests::ReopenService, services: true do
       it { expect(merge_request).to be_reopened }
 
       it 'executes hooks with reopen action' do
-        expect(service).to have_received(:execute_hooks).
-                               with(merge_request, 'reopen')
+        expect(service).to have_received(:execute_hooks)
+                               .with(merge_request, 'reopen')
       end
 
       it 'sends email to user2 about reopen of merge_request' do
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index fd46020bbdb7b922747f3a3e8829ed1c3bc2945a..ec15b5cac14a15a93b69f834ffcaf4cc1e565ec3 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -78,8 +78,8 @@ describe MergeRequests::UpdateService, services: true do
       end
 
       it 'executes hooks with update action' do
-        expect(service).to have_received(:execute_hooks).
-                               with(@merge_request, 'update')
+        expect(service).to have_received(:execute_hooks)
+                               .with(@merge_request, 'update')
       end
 
       it 'sends email to user2 about assign of new merge request and email to user3 about merge request unassignment' do
@@ -195,8 +195,8 @@ describe MergeRequests::UpdateService, services: true do
             head_pipeline_of: merge_request
           )
 
-          expect(MergeRequests::MergeWhenPipelineSucceedsService).to receive(:new).with(project, user).
-            and_return(service_mock)
+          expect(MergeRequests::MergeWhenPipelineSucceedsService).to receive(:new).with(project, user)
+            .and_return(service_mock)
           expect(service_mock).to receive(:execute).with(merge_request)
         end
 
diff --git a/spec/services/notes/slash_commands_service_spec.rb b/spec/services/notes/quick_actions_service_spec.rb
similarity index 94%
rename from spec/services/notes/slash_commands_service_spec.rb
rename to spec/services/notes/quick_actions_service_spec.rb
index d5ffc1908a90de996df030b8ad4984846ff58bf0..9a98499826f863f8cc4672c937862f81363ccb70 100644
--- a/spec/services/notes/slash_commands_service_spec.rb
+++ b/spec/services/notes/quick_actions_service_spec.rb
@@ -1,6 +1,6 @@
 require 'spec_helper'
 
-describe Notes::SlashCommandsService, services: true do
+describe Notes::QuickActionsService, services: true do
   shared_context 'note on noteable' do
     let(:project) { create(:empty_project) }
     let(:master) { create(:user).tap { |u| project.team << [u, :master] } }
@@ -11,7 +11,7 @@ describe Notes::SlashCommandsService, services: true do
     end
   end
 
-  shared_examples 'note on noteable that does not support slash commands' do
+  shared_examples 'note on noteable that does not support quick actions' do
     include_context 'note on noteable'
 
     before do
@@ -45,7 +45,7 @@ describe Notes::SlashCommandsService, services: true do
     end
   end
 
-  shared_examples 'note on noteable that supports slash commands' do
+  shared_examples 'note on noteable that supports quick actions' do
     include_context 'note on noteable'
 
     before do
@@ -210,15 +210,15 @@ describe Notes::SlashCommandsService, services: true do
   describe '#execute' do
     let(:service) { described_class.new(project, master) }
 
-    it_behaves_like 'note on noteable that supports slash commands' do
+    it_behaves_like 'note on noteable that supports quick actions' do
       let(:note) { build(:note_on_issue, project: project) }
     end
 
-    it_behaves_like 'note on noteable that supports slash commands' do
+    it_behaves_like 'note on noteable that supports quick actions' do
       let(:note) { build(:note_on_merge_request, project: project) }
     end
 
-    it_behaves_like 'note on noteable that does not support slash commands' do
+    it_behaves_like 'note on noteable that does not support quick actions' do
       let(:note) { build(:note_on_commit, project: project) }
     end
   end
diff --git a/spec/services/preview_markdown_service_spec.rb b/spec/services/preview_markdown_service_spec.rb
index b2fb5c91313c084bd2f78560795d74f6fc812cf5..4fd9cb23ae18fa83d5cac524e9029205ca3f93b6 100644
--- a/spec/services/preview_markdown_service_spec.rb
+++ b/spec/services/preview_markdown_service_spec.rb
@@ -19,24 +19,24 @@ describe PreviewMarkdownService do
     end
   end
 
-  context 'new note with slash commands' do
+  context 'new note with quick actions' do
     let(:issue) { create(:issue, project: project) }
     let(:params) do
       {
         text: "Please do it\n/assign #{user.to_reference}",
-        slash_commands_target_type: 'Issue',
-        slash_commands_target_id: issue.id
+        quick_actions_target_type: 'Issue',
+        quick_actions_target_id: issue.id
       }
     end
     let(:service) { described_class.new(project, user, params) }
 
-    it 'removes slash commands from text' do
+    it 'removes quick actions from text' do
       result = service.execute
 
       expect(result[:text]).to eq 'Please do it'
     end
 
-    it 'explains slash commands effect' do
+    it 'explains quick actions effect' do
       result = service.execute
 
       expect(result[:commands]).to eq "Assigns #{user.to_reference}."
@@ -47,21 +47,21 @@ describe PreviewMarkdownService do
     let(:params) do
       {
         text: "My work\n/estimate 2y",
-        slash_commands_target_type: 'MergeRequest'
+        quick_actions_target_type: 'MergeRequest'
       }
     end
     let(:service) { described_class.new(project, user, params) }
 
-    it 'removes slash commands from text' do
+    it 'removes quick actions from text' do
       result = service.execute
 
       expect(result[:text]).to eq 'My work'
     end
 
-    it 'explains slash commands effect' do
+    it 'explains quick actions effect' do
       result = service.execute
 
       expect(result[:commands]).to eq 'Sets time estimate to 2y.'
-    end    
+    end
   end
 end
diff --git a/spec/services/projects/housekeeping_service_spec.rb b/spec/services/projects/housekeeping_service_spec.rb
index fff12beed71eea10e9b2b79ab96b0460ce02ca91..ebed802708d2b8c8a9d12bcf0f51369e75e343de 100644
--- a/spec/services/projects/housekeeping_service_spec.rb
+++ b/spec/services/projects/housekeeping_service_spec.rb
@@ -66,14 +66,14 @@ describe Projects::HousekeepingService do
     allow(subject).to receive(:lease_key).and_return(:the_lease_key)
 
     # At push 200
-    expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :gc, :the_lease_key, :the_uuid).
-      exactly(1).times
+    expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :gc, :the_lease_key, :the_uuid)
+      .exactly(1).times
     # At push 50, 100, 150
-    expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :full_repack, :the_lease_key, :the_uuid).
-      exactly(3).times
+    expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :full_repack, :the_lease_key, :the_uuid)
+      .exactly(3).times
     # At push 10, 20, ... (except those above)
-    expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :incremental_repack, :the_lease_key, :the_uuid).
-      exactly(16).times
+    expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :incremental_repack, :the_lease_key, :the_uuid)
+      .exactly(16).times
 
     201.times do
       subject.increment!
diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb
index 44db299812f48e53eb4de31a2e292f7333b627ae..e855de38037ffec7ea2d2339198c1822a12a7c89 100644
--- a/spec/services/projects/import_service_spec.rb
+++ b/spec/services/projects/import_service_spec.rb
@@ -111,11 +111,11 @@ describe Projects::ImportService, services: true do
       end
 
       it 'flushes various caches' do
-        allow_any_instance_of(Repository).to receive(:fetch_remote).
-          and_return(true)
+        allow_any_instance_of(Repository).to receive(:fetch_remote)
+          .and_return(true)
 
-        allow_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute).
-          and_return(true)
+        allow_any_instance_of(Gitlab::GithubImport::Importer).to receive(:execute)
+          .and_return(true)
 
         expect_any_instance_of(Repository).to receive(:expire_content_cache)
 
diff --git a/spec/services/projects/propagate_service_template_spec.rb b/spec/services/projects/propagate_service_template_spec.rb
index 8a6a9f09f7457631aa1606b0eff430e04c417fb3..a6d43c4f0f18c0f657f9cfe39600fcce8c84b73a 100644
--- a/spec/services/projects/propagate_service_template_spec.rb
+++ b/spec/services/projects/propagate_service_template_spec.rb
@@ -60,8 +60,8 @@ describe Projects::PropagateServiceTemplate, services: true do
       Service.build_from_template(project.id, service_template).save!
       Service.build_from_template(project.id, other_service).save!
 
-      expect { described_class.propagate(service_template) }.
-        not_to change { Service.count }
+      expect { described_class.propagate(service_template) }
+        .not_to change { Service.count }
     end
 
     it 'creates the service containing the template attributes' do
@@ -90,8 +90,8 @@ describe Projects::PropagateServiceTemplate, services: true do
       it 'updates the project external tracker' do
         service_template.update!(category: 'issue_tracker', default: false)
 
-        expect { described_class.propagate(service_template) }.
-          to change { project.reload.has_external_issue_tracker }.to(true)
+        expect { described_class.propagate(service_template) }
+          .to change { project.reload.has_external_issue_tracker }.to(true)
       end
     end
 
@@ -99,8 +99,8 @@ describe Projects::PropagateServiceTemplate, services: true do
       it 'updates the project external tracker' do
         service_template.update!(type: 'ExternalWikiService')
 
-        expect { described_class.propagate(service_template) }.
-          to change { project.reload.has_external_wiki }.to(true)
+        expect { described_class.propagate(service_template) }
+          .to change { project.reload.has_external_wiki }.to(true)
       end
     end
   end
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index 5d2f4cf17fb69871df18242e22f1966534fada38..76c52d55ae58f53d28f4f9b3cfc9ba91cbbde001 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -7,10 +7,10 @@ describe Projects::TransferService, services: true do
 
   context 'namespace -> namespace' do
     before do
-      allow_any_instance_of(Gitlab::UploadsTransfer).
-        to receive(:move_project).and_return(true)
-      allow_any_instance_of(Gitlab::PagesTransfer).
-        to receive(:move_project).and_return(true)
+      allow_any_instance_of(Gitlab::UploadsTransfer)
+        .to receive(:move_project).and_return(true)
+      allow_any_instance_of(Gitlab::PagesTransfer)
+        .to receive(:move_project).and_return(true)
       group.add_owner(user)
       @result = transfer_project(project, user, group)
     end
@@ -19,6 +19,67 @@ describe Projects::TransferService, services: true do
     it { expect(project.namespace).to eq(group) }
   end
 
+  context 'when transfer succeeds' do
+    before do
+      group.add_owner(user)
+    end
+
+    it 'sends notifications' do
+      expect_any_instance_of(NotificationService).to receive(:project_was_moved)
+
+      transfer_project(project, user, group)
+    end
+
+    it 'executes system hooks' do
+      expect_any_instance_of(Projects::TransferService).to receive(:execute_system_hooks)
+
+      transfer_project(project, user, group)
+    end
+  end
+
+  context 'when transfer fails' do
+    let!(:original_path) { project_path(project) }
+
+    def attempt_project_transfer
+      expect do
+        transfer_project(project, user, group)
+      end.to raise_error(ActiveRecord::ActiveRecordError)
+    end
+
+    before do
+      group.add_owner(user)
+
+      expect_any_instance_of(Labels::TransferService).to receive(:execute).and_raise(ActiveRecord::StatementInvalid, "PG ERROR")
+    end
+
+    def project_path(project)
+      File.join(project.repository_storage_path, "#{project.path_with_namespace}.git")
+    end
+
+    def current_path
+      project_path(project)
+    end
+
+    it 'rolls back repo location' do
+      attempt_project_transfer
+
+      expect(Dir.exist?(original_path)).to be_truthy
+      expect(original_path).to eq current_path
+    end
+
+    it "doesn't send move notifications" do
+      expect_any_instance_of(NotificationService).not_to receive(:project_was_moved)
+
+      attempt_project_transfer
+    end
+
+    it "doesn't run system hooks" do
+      expect_any_instance_of(Projects::TransferService).not_to receive(:execute_system_hooks)
+
+      attempt_project_transfer
+    end
+  end
+
   context 'namespace -> no namespace' do
     before do
       @result = transfer_project(project, user, nil)
@@ -112,9 +173,9 @@ describe Projects::TransferService, services: true do
     end
 
     it 'only schedules a single job for every user' do
-      expect(UserProjectAccessChangedService).to receive(:new).
-        with([owner.id, group_member.id]).
-        and_call_original
+      expect(UserProjectAccessChangedService).to receive(:new)
+        .with([owner.id, group_member.id])
+        .and_call_original
 
       transfer_project(project, owner, group)
     end
diff --git a/spec/services/projects/unlink_fork_service_spec.rb b/spec/services/projects/unlink_fork_service_spec.rb
index 23f5555d3e0e98af66c906adf887af50a8deaa54..d34652bd7acf9e01231c597dacef013aecd0155e 100644
--- a/spec/services/projects/unlink_fork_service_spec.rb
+++ b/spec/services/projects/unlink_fork_service_spec.rb
@@ -12,9 +12,9 @@ describe Projects::UnlinkForkService, services: true do
     let(:mr_close_service) { MergeRequests::CloseService.new(fork_project, user) }
 
     before do
-      allow(MergeRequests::CloseService).to receive(:new).
-        with(fork_project, user).
-        and_return(mr_close_service)
+      allow(MergeRequests::CloseService).to receive(:new)
+        .with(fork_project, user)
+        .and_return(mr_close_service)
     end
 
     it 'close all pending merge requests' do
diff --git a/spec/services/slash_commands/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb
similarity index 99%
rename from spec/services/slash_commands/interpret_service_spec.rb
rename to spec/services/quick_actions/interpret_service_spec.rb
index c12fb1a6e531aa457aba50e9bfa0063ee331ff60..c9e63efbc145b8f4651afd8da942e9351afd40e8 100644
--- a/spec/services/slash_commands/interpret_service_spec.rb
+++ b/spec/services/quick_actions/interpret_service_spec.rb
@@ -1,6 +1,6 @@
 require 'spec_helper'
 
-describe SlashCommands::InterpretService, services: true do
+describe QuickActions::InterpretService, services: true do
   let(:project) { create(:empty_project, :public) }
   let(:developer) { create(:user) }
   let(:developer2) { create(:user) }
diff --git a/spec/services/submit_usage_ping_service_spec.rb b/spec/services/submit_usage_ping_service_spec.rb
index 63a1e78f274c9001c64fa6ea1053bb0ce290c86d..817fa4262d5647ad59366b03ae338be2e1baa4bd 100644
--- a/spec/services/submit_usage_ping_service_spec.rb
+++ b/spec/services/submit_usage_ping_service_spec.rb
@@ -92,8 +92,8 @@ describe SubmitUsagePingService do
   end
 
   def stub_response(body)
-    stub_request(:post, 'https://version.gitlab.com/usage_data').
-      to_return(
+    stub_request(:post, 'https://version.gitlab.com/usage_data')
+      .to_return(
         headers: { 'Content-Type' => 'application/json' },
         body: body.to_json
       )
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 9295c09aefc5499941c7f419ebb346c8065e237a..8d3dafafab2932557f794516b502dd21d41e6b8d 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -333,8 +333,8 @@ describe SystemNoteService, services: true do
       end
 
       it 'sets the note text' do
-        expect(subject.note).
-          to eq "changed title from **{-Old title-}** to **{+Lorem ipsum+}**"
+        expect(subject.note)
+          .to eq "changed title from **{-Old title-}** to **{+Lorem ipsum+}**"
       end
     end
   end
@@ -521,8 +521,8 @@ describe SystemNoteService, services: true do
     context 'when mentioner is not a MergeRequest' do
       it 'is falsey' do
         mentioner = noteable.dup
-        expect(described_class.cross_reference_disallowed?(noteable, mentioner)).
-          to be_falsey
+        expect(described_class.cross_reference_disallowed?(noteable, mentioner))
+          .to be_falsey
       end
     end
 
@@ -533,14 +533,14 @@ describe SystemNoteService, services: true do
 
       it 'is truthy when noteable is in commits' do
         expect(mentioner).to receive(:commits).and_return([noteable])
-        expect(described_class.cross_reference_disallowed?(noteable, mentioner)).
-          to be_truthy
+        expect(described_class.cross_reference_disallowed?(noteable, mentioner))
+          .to be_truthy
       end
 
       it 'is falsey when noteable is not in commits' do
         expect(mentioner).to receive(:commits).and_return([])
-        expect(described_class.cross_reference_disallowed?(noteable, mentioner)).
-          to be_falsey
+        expect(described_class.cross_reference_disallowed?(noteable, mentioner))
+          .to be_falsey
       end
     end
 
@@ -548,8 +548,8 @@ describe SystemNoteService, services: true do
       let(:noteable) { ExternalIssue.new('EXT-1234', project) }
       it 'is truthy' do
         mentioner = noteable.dup
-        expect(described_class.cross_reference_disallowed?(noteable, mentioner)).
-          to be_truthy
+        expect(described_class.cross_reference_disallowed?(noteable, mentioner))
+          .to be_truthy
       end
     end
   end
@@ -566,13 +566,13 @@ describe SystemNoteService, services: true do
       end
 
       it 'is truthy when already mentioned' do
-        expect(described_class.cross_reference_exists?(noteable, commit0)).
-          to be_truthy
+        expect(described_class.cross_reference_exists?(noteable, commit0))
+          .to be_truthy
       end
 
       it 'is falsey when not already mentioned' do
-        expect(described_class.cross_reference_exists?(noteable, commit1)).
-          to be_falsey
+        expect(described_class.cross_reference_exists?(noteable, commit1))
+          .to be_falsey
       end
 
       context 'legacy capitalized cross reference' do
@@ -583,8 +583,8 @@ describe SystemNoteService, services: true do
         end
 
         it 'is truthy when already mentioned' do
-          expect(described_class.cross_reference_exists?(noteable, commit0)).
-            to be_truthy
+          expect(described_class.cross_reference_exists?(noteable, commit0))
+            .to be_truthy
         end
       end
     end
@@ -596,13 +596,13 @@ describe SystemNoteService, services: true do
       end
 
       it 'is truthy when already mentioned' do
-        expect(described_class.cross_reference_exists?(commit0, commit1)).
-          to be_truthy
+        expect(described_class.cross_reference_exists?(commit0, commit1))
+          .to be_truthy
       end
 
       it 'is falsey when not already mentioned' do
-        expect(described_class.cross_reference_exists?(commit1, commit0)).
-          to be_falsey
+        expect(described_class.cross_reference_exists?(commit1, commit0))
+          .to be_falsey
       end
 
       context 'legacy capitalized cross reference' do
@@ -613,8 +613,8 @@ describe SystemNoteService, services: true do
         end
 
         it 'is truthy when already mentioned' do
-          expect(described_class.cross_reference_exists?(commit0, commit1)).
-            to be_truthy
+          expect(described_class.cross_reference_exists?(commit0, commit1))
+            .to be_truthy
         end
       end
     end
@@ -629,8 +629,8 @@ describe SystemNoteService, services: true do
       end
 
       it 'is true when a fork mentions an external issue' do
-        expect(described_class.cross_reference_exists?(noteable, commit2)).
-            to be true
+        expect(described_class.cross_reference_exists?(noteable, commit2))
+            .to be true
       end
 
       context 'legacy capitalized cross reference' do
@@ -640,8 +640,8 @@ describe SystemNoteService, services: true do
         end
 
         it 'is true when a fork mentions an external issue' do
-          expect(described_class.cross_reference_exists?(noteable, commit2)).
-              to be true
+          expect(described_class.cross_reference_exists?(noteable, commit2))
+              .to be true
         end
       end
     end
diff --git a/spec/services/tags/create_service_spec.rb b/spec/services/tags/create_service_spec.rb
index b9121b1de499c1d0cfb6317c02dbd949b64dd4f5..9f143cc566745f79421d0053f1f01da7ef7c7723 100644
--- a/spec/services/tags/create_service_spec.rb
+++ b/spec/services/tags/create_service_spec.rb
@@ -26,9 +26,9 @@ describe Tags::CreateService, services: true do
 
     context 'when tag already exists' do
       it 'returns an error' do
-        expect(repository).to receive(:add_tag).
-          with(user, 'v1.1.0', 'master', 'Foo').
-          and_raise(Rugged::TagError)
+        expect(repository).to receive(:add_tag)
+          .with(user, 'v1.1.0', 'master', 'Foo')
+          .and_raise(Rugged::TagError)
 
         response = service.execute('v1.1.0', 'master', 'Foo')
 
@@ -39,9 +39,9 @@ describe Tags::CreateService, services: true do
 
     context 'when pre-receive hook fails' do
       it 'returns an error' do
-        expect(repository).to receive(:add_tag).
-          with(user, 'v1.1.0', 'master', 'Foo').
-          and_raise(GitHooksService::PreReceiveError, 'something went wrong')
+        expect(repository).to receive(:add_tag)
+          .with(user, 'v1.1.0', 'master', 'Foo')
+          .and_raise(GitHooksService::PreReceiveError, 'something went wrong')
 
         response = service.execute('v1.1.0', 'master', 'Foo')
 
diff --git a/spec/services/user_project_access_changed_service_spec.rb b/spec/services/user_project_access_changed_service_spec.rb
index b4efe7de4318cc64147ed9ba6423076861468889..14a5e40350a7d1bb042ccb7c27f2636dbd2c0818 100644
--- a/spec/services/user_project_access_changed_service_spec.rb
+++ b/spec/services/user_project_access_changed_service_spec.rb
@@ -3,8 +3,8 @@ require 'spec_helper'
 describe UserProjectAccessChangedService do
   describe '#execute' do
     it 'schedules the user IDs' do
-      expect(AuthorizedProjectsWorker).to receive(:bulk_perform_and_wait).
-        with([[1], [2]])
+      expect(AuthorizedProjectsWorker).to receive(:bulk_perform_and_wait)
+        .with([[1], [2]])
 
       described_class.new([1, 2]).execute
     end
diff --git a/spec/services/users/activity_service_spec.rb b/spec/services/users/activity_service_spec.rb
index 8d67ebe3231e55967d538f1a906a2caa7d454c3a..2e009d4ce1c28ea61a468c997a9c546c8aaa6547 100644
--- a/spec/services/users/activity_service_spec.rb
+++ b/spec/services/users/activity_service_spec.rb
@@ -41,8 +41,8 @@ describe Users::ActivityService, services: true do
   end
 
   def last_hour_user_ids
-    Gitlab::UserActivities.new.
-      select { |k, v| v >= 1.hour.ago.to_i.to_s }.
-      map { |k, _| k.to_i }
+    Gitlab::UserActivities.new
+      .select { |k, v| v >= 1.hour.ago.to_i.to_s }
+      .map { |k, _| k.to_i }
   end
 end
diff --git a/spec/services/users/refresh_authorized_projects_service_spec.rb b/spec/services/users/refresh_authorized_projects_service_spec.rb
index 8c40d25e00c1b3942c35d221e6e637e79affa7bc..b65cadbb2f593bb111c0d3f0a283b60342e01e2b 100644
--- a/spec/services/users/refresh_authorized_projects_service_spec.rb
+++ b/spec/services/users/refresh_authorized_projects_service_spec.rb
@@ -10,11 +10,11 @@ describe Users::RefreshAuthorizedProjectsService do
 
   describe '#execute', :redis do
     it 'refreshes the authorizations using a lease' do
-      expect_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).
-        and_return('foo')
+      expect_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain)
+        .and_return('foo')
 
-      expect(Gitlab::ExclusiveLease).to receive(:cancel).
-        with(an_instance_of(String), 'foo')
+      expect(Gitlab::ExclusiveLease).to receive(:cancel)
+        .with(an_instance_of(String), 'foo')
 
       expect(service).to receive(:execute_without_lease)
 
@@ -29,11 +29,11 @@ describe Users::RefreshAuthorizedProjectsService do
 
     it 'updates the authorized projects of the user' do
       project2 = create(:empty_project)
-      to_remove = user.project_authorizations.
-        create!(project: project2, access_level: Gitlab::Access::MASTER)
+      to_remove = user.project_authorizations
+        .create!(project: project2, access_level: Gitlab::Access::MASTER)
 
-      expect(service).to receive(:update_authorizations).
-        with([to_remove.project_id], [[user.id, project.id, Gitlab::Access::MASTER]])
+      expect(service).to receive(:update_authorizations)
+        .with([to_remove.project_id], [[user.id, project.id, Gitlab::Access::MASTER]])
 
       service.execute_without_lease
     end
@@ -41,11 +41,11 @@ describe Users::RefreshAuthorizedProjectsService do
     it 'sets the access level of a project to the highest available level' do
       user.project_authorizations.delete_all
 
-      to_remove = user.project_authorizations.
-        create!(project: project, access_level: Gitlab::Access::DEVELOPER)
+      to_remove = user.project_authorizations
+        .create!(project: project, access_level: Gitlab::Access::DEVELOPER)
 
-      expect(service).to receive(:update_authorizations).
-        with([to_remove.project_id], [[user.id, project.id, Gitlab::Access::MASTER]])
+      expect(service).to receive(:update_authorizations)
+        .with([to_remove.project_id], [[user.id, project.id, Gitlab::Access::MASTER]])
 
       service.execute_without_lease
     end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 01ac3cbd3f6b62204ff043699f073b192f8fcf5d..fdef6fd5221f970390f9b61ab1f6a0c5129fbd77 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -44,6 +44,7 @@ RSpec.configure do |config|
 
   config.include Devise::Test::ControllerHelpers, type: :controller
   config.include Devise::Test::ControllerHelpers, type: :view
+  config.include Devise::Test::IntegrationHelpers, type: :feature
   config.include Warden::Test::Helpers, type: :request
   config.include LoginHelpers, type: :feature
   config.include SearchHelpers, type: :feature
diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb
index c34e76fa72fc5c1fd29c6aa76e4929a1d2fca6bc..3e5d6cf1364d0a85628732de4eebd893f33b63aa 100644
--- a/spec/support/capybara.rb
+++ b/spec/support/capybara.rb
@@ -35,4 +35,14 @@ RSpec.configure do |config|
     TestEnv.eager_load_driver_server
     $capybara_server_already_started = true
   end
+
+  config.after(:each, :js) do |example|
+    # capybara/rspec already calls Capybara.reset_sessions! in an `after` hook,
+    # but `block_and_wait_for_requests_complete` is called before it so by
+    # calling it explicitely here, we prevent any new requests from being fired
+    # See https://github.com/teamcapybara/capybara/blob/ffb41cfad620de1961bb49b1562a9fa9b28c0903/lib/capybara/rspec.rb#L20-L25
+    # We don't reset the session when the example failed, because we need capybara-screenshot to have access to it.
+    Capybara.reset_sessions! unless example.exception
+    block_and_wait_for_requests_complete
+  end
 end
diff --git a/spec/support/chat_slash_commands_shared_examples.rb b/spec/support/chat_slash_commands_shared_examples.rb
index 4dfa29849ee7c4fc0ff9a224a3be904a2f75b25a..978b0b9cc30b2df6cbebf108855e6eabb876a77c 100644
--- a/spec/support/chat_slash_commands_shared_examples.rb
+++ b/spec/support/chat_slash_commands_shared_examples.rb
@@ -87,7 +87,7 @@ RSpec.shared_examples 'chat slash commands service' do
         end
 
         it 'triggers the command' do
-          expect_any_instance_of(Gitlab::ChatCommands::Command).to receive(:execute)
+          expect_any_instance_of(Gitlab::SlashCommands::Command).to receive(:execute)
 
           subject.trigger(params)
         end
diff --git a/spec/support/controllers/githubish_import_controller_shared_examples.rb b/spec/support/controllers/githubish_import_controller_shared_examples.rb
index d6b40db09ce3b05e7aae92a4a1703fa45d72703a..a8d9566b4e496b54c258d9b442decd56b24b286a 100644
--- a/spec/support/controllers/githubish_import_controller_shared_examples.rb
+++ b/spec/support/controllers/githubish_import_controller_shared_examples.rb
@@ -14,8 +14,8 @@ shared_examples 'a GitHub-ish import controller: POST personal_access_token' do
   it "updates access token" do
     token = 'asdfasdf9876'
 
-    allow_any_instance_of(Gitlab::GithubImport::Client).
-      to receive(:user).and_return(true)
+    allow_any_instance_of(Gitlab::GithubImport::Client)
+      .to receive(:user).and_return(true)
 
     post :personal_access_token, personal_access_token: token
 
@@ -79,8 +79,8 @@ shared_examples 'a GitHub-ish import controller: GET status' do
   end
 
   it "handles an invalid access token" do
-    allow_any_instance_of(Gitlab::GithubImport::Client).
-      to receive(:repos).and_raise(Octokit::Unauthorized)
+    allow_any_instance_of(Gitlab::GithubImport::Client)
+      .to receive(:repos).and_raise(Octokit::Unauthorized)
 
     get :status
 
@@ -110,9 +110,9 @@ shared_examples 'a GitHub-ish import controller: POST create' do
   context "when the repository owner is the provider user" do
     context "when the provider user and GitLab user's usernames match" do
       it "takes the current user's namespace" do
-        expect(Gitlab::GithubImport::ProjectCreator).
-          to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider).
-            and_return(double(execute: true))
+        expect(Gitlab::GithubImport::ProjectCreator)
+          .to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider)
+            .and_return(double(execute: true))
 
         post :create, format: :js
       end
@@ -122,9 +122,9 @@ shared_examples 'a GitHub-ish import controller: POST create' do
       let(:provider_username) { "someone_else" }
 
       it "takes the current user's namespace" do
-        expect(Gitlab::GithubImport::ProjectCreator).
-          to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider).
-            and_return(double(execute: true))
+        expect(Gitlab::GithubImport::ProjectCreator)
+          .to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider)
+            .and_return(double(execute: true))
 
         post :create, format: :js
       end
@@ -144,9 +144,9 @@ shared_examples 'a GitHub-ish import controller: POST create' do
 
       context "when the namespace is owned by the GitLab user" do
         it "takes the existing namespace" do
-          expect(Gitlab::GithubImport::ProjectCreator).
-            to receive(:new).with(provider_repo, provider_repo.name, existing_namespace, user, access_params, type: provider).
-              and_return(double(execute: true))
+          expect(Gitlab::GithubImport::ProjectCreator)
+            .to receive(:new).with(provider_repo, provider_repo.name, existing_namespace, user, access_params, type: provider)
+              .and_return(double(execute: true))
 
           post :create, format: :js
         end
@@ -159,9 +159,9 @@ shared_examples 'a GitHub-ish import controller: POST create' do
         end
 
         it "creates a project using user's namespace" do
-          expect(Gitlab::GithubImport::ProjectCreator).
-            to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider).
-              and_return(double(execute: true))
+          expect(Gitlab::GithubImport::ProjectCreator)
+            .to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider)
+              .and_return(double(execute: true))
 
           post :create, format: :js
         end
@@ -171,16 +171,16 @@ shared_examples 'a GitHub-ish import controller: POST create' do
     context "when a namespace with the provider user's username doesn't exist" do
       context "when current user can create namespaces" do
         it "creates the namespace" do
-          expect(Gitlab::GithubImport::ProjectCreator).
-            to receive(:new).and_return(double(execute: true))
+          expect(Gitlab::GithubImport::ProjectCreator)
+            .to receive(:new).and_return(double(execute: true))
 
           expect { post :create, target_namespace: provider_repo.name, format: :js }.to change(Namespace, :count).by(1)
         end
 
         it "takes the new namespace" do
-          expect(Gitlab::GithubImport::ProjectCreator).
-            to receive(:new).with(provider_repo, provider_repo.name, an_instance_of(Group), user, access_params, type: provider).
-              and_return(double(execute: true))
+          expect(Gitlab::GithubImport::ProjectCreator)
+            .to receive(:new).with(provider_repo, provider_repo.name, an_instance_of(Group), user, access_params, type: provider)
+              .and_return(double(execute: true))
 
           post :create, target_namespace: provider_repo.name, format: :js
         end
@@ -192,16 +192,16 @@ shared_examples 'a GitHub-ish import controller: POST create' do
         end
 
         it "doesn't create the namespace" do
-          expect(Gitlab::GithubImport::ProjectCreator).
-            to receive(:new).and_return(double(execute: true))
+          expect(Gitlab::GithubImport::ProjectCreator)
+            .to receive(:new).and_return(double(execute: true))
 
           expect { post :create, format: :js }.not_to change(Namespace, :count)
         end
 
         it "takes the current user's namespace" do
-          expect(Gitlab::GithubImport::ProjectCreator).
-            to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider).
-              and_return(double(execute: true))
+          expect(Gitlab::GithubImport::ProjectCreator)
+            .to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider)
+              .and_return(double(execute: true))
 
           post :create, format: :js
         end
@@ -217,17 +217,17 @@ shared_examples 'a GitHub-ish import controller: POST create' do
       end
 
       it 'takes the selected namespace and name' do
-        expect(Gitlab::GithubImport::ProjectCreator).
-          to receive(:new).with(provider_repo, test_name, test_namespace, user, access_params, type: provider).
-            and_return(double(execute: true))
+        expect(Gitlab::GithubImport::ProjectCreator)
+          .to receive(:new).with(provider_repo, test_name, test_namespace, user, access_params, type: provider)
+            .and_return(double(execute: true))
 
         post :create, { target_namespace: test_namespace.name, new_name: test_name, format: :js }
       end
 
       it 'takes the selected name and default namespace' do
-        expect(Gitlab::GithubImport::ProjectCreator).
-          to receive(:new).with(provider_repo, test_name, user.namespace, user, access_params, type: provider).
-            and_return(double(execute: true))
+        expect(Gitlab::GithubImport::ProjectCreator)
+          .to receive(:new).with(provider_repo, test_name, user.namespace, user, access_params, type: provider)
+            .and_return(double(execute: true))
 
         post :create, { new_name: test_name, format: :js }
       end
@@ -243,9 +243,9 @@ shared_examples 'a GitHub-ish import controller: POST create' do
       end
 
       it 'takes the selected namespace and name' do
-        expect(Gitlab::GithubImport::ProjectCreator).
-          to receive(:new).with(provider_repo, test_name, nested_namespace, user, access_params, type: provider).
-            and_return(double(execute: true))
+        expect(Gitlab::GithubImport::ProjectCreator)
+          .to receive(:new).with(provider_repo, test_name, nested_namespace, user, access_params, type: provider)
+            .and_return(double(execute: true))
 
         post :create, { target_namespace: nested_namespace.full_path, new_name: test_name, format: :js }
       end
@@ -255,26 +255,26 @@ shared_examples 'a GitHub-ish import controller: POST create' do
       let(:test_name) { 'test_name' }
 
       it 'takes the selected namespace and name' do
-        expect(Gitlab::GithubImport::ProjectCreator).
-          to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider).
-            and_return(double(execute: true))
+        expect(Gitlab::GithubImport::ProjectCreator)
+          .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider)
+            .and_return(double(execute: true))
 
         post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js }
       end
 
       it 'creates the namespaces' do
-        allow(Gitlab::GithubImport::ProjectCreator).
-          to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider).
-            and_return(double(execute: true))
+        allow(Gitlab::GithubImport::ProjectCreator)
+          .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider)
+            .and_return(double(execute: true))
 
         expect { post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js } }
           .to change { Namespace.count }.by(2)
       end
 
       it 'new namespace has the right parent' do
-        allow(Gitlab::GithubImport::ProjectCreator).
-          to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider).
-            and_return(double(execute: true))
+        allow(Gitlab::GithubImport::ProjectCreator)
+          .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider)
+            .and_return(double(execute: true))
 
         post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js }
 
@@ -287,17 +287,17 @@ shared_examples 'a GitHub-ish import controller: POST create' do
       let!(:parent_namespace) { create(:group, name: 'foo', owner: user) }
 
       it 'takes the selected namespace and name' do
-        expect(Gitlab::GithubImport::ProjectCreator).
-          to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider).
-            and_return(double(execute: true))
+        expect(Gitlab::GithubImport::ProjectCreator)
+          .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider)
+            .and_return(double(execute: true))
 
         post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :js }
       end
 
       it 'creates the namespaces' do
-        allow(Gitlab::GithubImport::ProjectCreator).
-          to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider).
-            and_return(double(execute: true))
+        allow(Gitlab::GithubImport::ProjectCreator)
+          .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider)
+            .and_return(double(execute: true))
 
         expect { post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :js } }
           .to change { Namespace.count }.by(2)
diff --git a/spec/support/features/issuable_slash_commands_shared_examples.rb b/spec/support/features/issuable_slash_commands_shared_examples.rb
index fa82dc5e9f967f35b529a3ae8c31a9f5ffc8d42c..50869099bb76cc0c52b7658cd597c95f22aaff5a 100644
--- a/spec/support/features/issuable_slash_commands_shared_examples.rb
+++ b/spec/support/features/issuable_slash_commands_shared_examples.rb
@@ -1,8 +1,8 @@
 # Specifications for behavior common to all objects with executable attributes.
 # It takes a `issuable_type`, and expect an `issuable`.
 
-shared_examples 'issuable record that supports slash commands in its description and notes' do |issuable_type|
-  include SlashCommandsHelpers
+shared_examples 'issuable record that supports quick actions in its description and notes' do |issuable_type|
+  include QuickActionsHelpers
 
   let(:master) { create(:user) }
   let(:assignee) { create(:user, username: 'bob') }
@@ -17,7 +17,7 @@ shared_examples 'issuable record that supports slash commands in its description
     project.team << [master, :master]
     project.team << [assignee, :developer]
     project.team << [guest, :guest]
-    login_with(master)
+    gitlab_sign_in(master)
   end
 
   after do
@@ -105,8 +105,8 @@ shared_examples 'issuable record that supports slash commands in its description
 
       context "when current user cannot close #{issuable_type}" do
         before do
-          logout
-          login_with(guest)
+          gitlab_sign_out
+          gitlab_sign_in(guest)
           visit public_send("namespace_project_#{issuable_type}_path", project.namespace, project, issuable)
         end
 
@@ -140,8 +140,8 @@ shared_examples 'issuable record that supports slash commands in its description
 
       context "when current user cannot reopen #{issuable_type}" do
         before do
-          logout
-          login_with(guest)
+          gitlab_sign_out
+          gitlab_sign_in(guest)
           visit public_send("namespace_project_#{issuable_type}_path", project.namespace, project, issuable)
         end
 
@@ -170,8 +170,8 @@ shared_examples 'issuable record that supports slash commands in its description
 
       context "when current user cannot change title of #{issuable_type}" do
         before do
-          logout
-          login_with(guest)
+          gitlab_sign_out
+          gitlab_sign_in(guest)
           visit public_send("namespace_project_#{issuable_type}_path", project.namespace, project, issuable)
         end
 
@@ -260,7 +260,7 @@ shared_examples 'issuable record that supports slash commands in its description
   end
 
   describe "preview of note on #{issuable_type}" do
-    it 'removes slash commands from note and explains them' do
+    it 'removes quick actions from note and explains them' do
       visit public_send("namespace_project_#{issuable_type}_path", project.namespace, project, issuable)
 
       page.within('.js-main-target-form') do
diff --git a/spec/support/features/reportable_note_shared_examples.rb b/spec/support/features/reportable_note_shared_examples.rb
index 0d80c95e8267e10a8de7d1f7eaf59051a8e2c6ea..27e079c01dd1eeaca0c8c309c91d63bb5a216f44 100644
--- a/spec/support/features/reportable_note_shared_examples.rb
+++ b/spec/support/features/reportable_note_shared_examples.rb
@@ -13,9 +13,7 @@ shared_examples 'reportable note' do
 
   it 'dropdown has Edit, Report and Delete links' do
     dropdown = comment.find(more_actions_selector)
-
-    dropdown.click
-    dropdown.find('.dropdown-menu li', match: :first)
+    open_dropdown(dropdown)
 
     expect(dropdown).to have_button('Edit comment')
     expect(dropdown).to have_link('Report as abuse', href: abuse_report_path)
@@ -24,13 +22,16 @@ shared_examples 'reportable note' do
 
   it 'Report button links to a report page' do
     dropdown = comment.find(more_actions_selector)
-
-    dropdown.click
-    dropdown.find('.dropdown-menu li', match: :first)
+    open_dropdown(dropdown)
 
     dropdown.click_link('Report as abuse')
 
     expect(find('#user_name')['value']).to match(note.author.username)
     expect(find('#abuse_report_message')['value']).to match(noteable_note_url(note))
   end
+
+  def open_dropdown(dropdown)
+    dropdown.click
+    dropdown.find('.dropdown-menu li', match: :first)
+  end
 end
diff --git a/spec/support/filtered_search_helpers.rb b/spec/support/filtered_search_helpers.rb
index 37cc308e613d65048ae3ee4c31c6d149e83ba3e0..d21c4324d9eac75d718f34687d60bacd1fa83ce0 100644
--- a/spec/support/filtered_search_helpers.rb
+++ b/spec/support/filtered_search_helpers.rb
@@ -14,6 +14,9 @@ module FilteredSearchHelpers
     filtered_search.set(search)
 
     if submit
+      # Wait for the lazy author/assignee tokens that
+      # swap out the username with an avatar and name
+      wait_for_requests
       filtered_search.send_keys(:enter)
     end
   end
diff --git a/spec/support/login_helpers.rb b/spec/support/login_helpers.rb
index e6da852e7288aa09b0a4bd759254c66c8acc921c..879386b5437603978f482065861bf6b12bd02618 100644
--- a/spec/support/login_helpers.rb
+++ b/spec/support/login_helpers.rb
@@ -6,15 +6,15 @@ module LoginHelpers
   # Examples:
   #
   #   # Create a user automatically
-  #   login_as(:user)
+  #   gitlab_sign_in(:user)
   #
   #   # Create an admin automatically
-  #   login_as(:admin)
+  #   gitlab_sign_in(:admin)
   #
   #   # Provide an existing User record
   #   user = create(:user)
-  #   login_as(user)
-  def login_as(user_or_role)
+  #   gitlab_sign_in(user)
+  def gitlab_sign_in(user_or_role, **kwargs)
     @user =
       if user_or_role.is_a?(User)
         user_or_role
@@ -22,26 +22,44 @@ module LoginHelpers
         create(user_or_role)
       end
 
-    login_with(@user)
+    gitlab_sign_in_with(@user, **kwargs)
   end
 
-  # Internal: Login as the specified user
+  def gitlab_sign_in_via(provider, user, uid)
+    mock_auth_hash(provider, uid, user.email)
+    visit new_user_session_path
+    click_link provider
+  end
+
+  # Requires Javascript driver.
+  def gitlab_sign_out
+    find(".header-user-dropdown-toggle").click
+    click_link "Sign out"
+    # check the sign_in button
+    expect(page).to have_button('Sign in')
+  end
+
+  # Logout without JavaScript driver
+  def gitlab_sign_out_direct
+    page.driver.submit :delete, '/users/sign_out', {}
+  end
+
+  private
+
+  # Private: Login as the specified user
   #
   # user     - User instance to login with
   # remember - Whether or not to check "Remember me" (default: false)
-  def login_with(user, remember: false)
+  def gitlab_sign_in_with(user, remember: false)
     visit new_user_session_path
+
     fill_in "user_login", with: user.email
     fill_in "user_password", with: "12345678"
     check 'user_remember_me' if remember
+
     click_button "Sign in"
-    Thread.current[:current_user] = user
-  end
 
-  def login_via(provider, user, uid)
-    mock_auth_hash(provider, uid, user.email)
-    visit new_user_session_path
-    click_link provider
+    Thread.current[:current_user] = user
   end
 
   def mock_auth_hash(provider, uid, email)
@@ -71,17 +89,4 @@ module LoginHelpers
     })
     Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[:saml]
   end
-
-  # Requires Javascript driver.
-  def logout
-    find(".header-user-dropdown-toggle").click
-    click_link "Sign out"
-    # check the sign_in button
-    expect(page).to have_button('Sign in')
-  end
-
-  # Logout without JavaScript driver
-  def logout_direct
-    page.driver.submit :delete, '/users/sign_out', {}
-  end
 end
diff --git a/spec/support/mentionable_shared_examples.rb b/spec/support/mentionable_shared_examples.rb
index 87936bb485941ee0295659aa6f4234b0fe5c8571..3ac201f1fb195f80384b22ee1677597a5f709792 100644
--- a/spec/support/mentionable_shared_examples.rb
+++ b/spec/support/mentionable_shared_examples.rb
@@ -81,8 +81,8 @@ shared_examples 'a mentionable' do
                          ext_issue, ext_mr, ext_commit]
 
     mentioned_objects.each do |referenced|
-      expect(SystemNoteService).to receive(:cross_reference).
-        with(referenced, subject.local_reference, author)
+      expect(SystemNoteService).to receive(:cross_reference)
+        .with(referenced, subject.local_reference, author)
     end
 
     subject.create_cross_references!
@@ -127,15 +127,15 @@ shared_examples 'an editable mentionable' do
     # These three objects were already referenced, and should not receive new
     # notes
     [mentioned_issue, mentioned_commit, ext_issue].each do |oldref|
-      expect(SystemNoteService).not_to receive(:cross_reference).
-        with(oldref, any_args)
+      expect(SystemNoteService).not_to receive(:cross_reference)
+        .with(oldref, any_args)
     end
 
     # These two issues are new and should receive reference notes
     # In the case of MergeRequests remember that cannot mention commits included in the MergeRequest
     new_issues.each do |newref|
-      expect(SystemNoteService).to receive(:cross_reference).
-        with(newref, subject.local_reference, author)
+      expect(SystemNoteService).to receive(:cross_reference)
+        .with(newref, subject.local_reference, author)
     end
 
     set_mentionable_text.call(new_text)
diff --git a/spec/support/project_features_apply_to_issuables_shared_examples.rb b/spec/support/project_features_apply_to_issuables_shared_examples.rb
index f8b7d0527ba5f7dd459fc87b70fb621aa24aca89..81b51509e0b05e89cc914b48bfee588dc4ee1cd4 100644
--- a/spec/support/project_features_apply_to_issuables_shared_examples.rb
+++ b/spec/support/project_features_apply_to_issuables_shared_examples.rb
@@ -18,7 +18,7 @@ shared_examples 'project features apply to issuables' do |klass|
 
   before do
     _ = issuable
-    login_as(user) if user
+    gitlab_sign_in(user) if user
     visit path
   end
 
diff --git a/spec/support/slash_commands_helpers.rb b/spec/support/quick_actions_helpers.rb
similarity index 88%
rename from spec/support/slash_commands_helpers.rb
rename to spec/support/quick_actions_helpers.rb
index 4bfe481115fa2df6079972c058715a7fc91648c5..d2aaae7518f8c28d678ab17c2f383068c69aae27 100644
--- a/spec/support/slash_commands_helpers.rb
+++ b/spec/support/quick_actions_helpers.rb
@@ -1,4 +1,4 @@
-module SlashCommandsHelpers
+module QuickActionsHelpers
   def write_note(text)
     Sidekiq::Testing.fake! do
       page.within('.js-main-target-form') do
diff --git a/spec/support/reactive_caching_helpers.rb b/spec/support/reactive_caching_helpers.rb
index 98eb57f8b54b97352b852ad5310502c4aa5ba193..34124f02133328ad2e48b2025b5dd255b842b60c 100644
--- a/spec/support/reactive_caching_helpers.rb
+++ b/spec/support/reactive_caching_helpers.rb
@@ -35,8 +35,8 @@ module ReactiveCachingHelpers
   end
 
   def expect_reactive_cache_update_queued(subject)
-    expect(ReactiveCachingWorker).
-      to receive(:perform_in).
-      with(subject.class.reactive_cache_refresh_interval, subject.class, subject.id)
+    expect(ReactiveCachingWorker)
+      .to receive(:perform_in)
+      .with(subject.class.reactive_cache_refresh_interval, subject.class, subject.id)
   end
 end
diff --git a/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb b/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb
index 5e6f9f323a1dfb67a6839bc64414bfd7ba98a8eb..9399745f9003697761c4882d83c504623990073f 100644
--- a/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb
+++ b/spec/support/services/issuable_create_service_slash_commands_shared_examples.rb
@@ -1,7 +1,7 @@
 # Specifications for behavior common to all objects with executable attributes.
 # It can take a `default_params`.
 
-shared_examples 'new issuable record that supports slash commands' do
+shared_examples 'new issuable record that supports quick actions' do
   let!(:project) { create(:project, :repository) }
   let(:user) { create(:user).tap { |u| project.team << [u, :master] } }
   let(:assignee) { create(:user) }
diff --git a/spec/support/services_shared_context.rb b/spec/support/services_shared_context.rb
index 66c93890e31ef202211c7d1643f206f8e7567ddf..7457484a932fa4d3d8e6ff245af525ff55d97e02 100644
--- a/spec/support/services_shared_context.rb
+++ b/spec/support/services_shared_context.rb
@@ -6,9 +6,9 @@ Service.available_services_names.each do |service|
     let(:service_fields) { service_klass.new.fields }
     let(:service_attrs_list) { service_fields.inject([]) {|arr, hash| arr << hash[:name].to_sym } }
     let(:service_attrs_list_without_passwords) do
-      service_fields.
-        select { |field| field[:type] != 'password' }.
-        map { |field| field[:name].to_sym}
+      service_fields
+        .select { |field| field[:type] != 'password' }
+        .map { |field| field[:name].to_sym}
     end
     let(:service_attrs) do
       service_attrs_list.inject({}) do |hash, k|
diff --git a/spec/support/slack_mattermost_notifications_shared_examples.rb b/spec/support/slack_mattermost_notifications_shared_examples.rb
index a7deb038703a9823b9aca54cfbad8bcbfc0186b1..044c09d5fde8b3fcc1466d8af9ebf8522f320765 100644
--- a/spec/support/slack_mattermost_notifications_shared_examples.rb
+++ b/spec/support/slack_mattermost_notifications_shared_examples.rb
@@ -108,9 +108,9 @@ RSpec.shared_examples 'slack or mattermost notifications' do
     it 'uses the username as an option for slack when configured' do
       allow(chat_service).to receive(:username).and_return(username)
 
-      expect(Slack::Notifier).to receive(:new).
-       with(webhook_url, username: username).
-       and_return(
+      expect(Slack::Notifier).to receive(:new)
+       .with(webhook_url, username: username)
+       .and_return(
          double(:slack_service).as_null_object
        )
 
@@ -119,9 +119,9 @@ RSpec.shared_examples 'slack or mattermost notifications' do
 
     it 'uses the channel as an option when it is configured' do
       allow(chat_service).to receive(:channel).and_return(channel)
-      expect(Slack::Notifier).to receive(:new).
-        with(webhook_url, channel: channel).
-        and_return(
+      expect(Slack::Notifier).to receive(:new)
+        .with(webhook_url, channel: channel)
+        .and_return(
           double(:slack_service).as_null_object
         )
       chat_service.execute(push_sample_data)
@@ -131,9 +131,9 @@ RSpec.shared_examples 'slack or mattermost notifications' do
       it "uses the right channel for push event" do
         chat_service.update_attributes(push_channel: "random")
 
-        expect(Slack::Notifier).to receive(:new).
-         with(webhook_url, channel: "random").
-         and_return(
+        expect(Slack::Notifier).to receive(:new)
+         .with(webhook_url, channel: "random")
+         .and_return(
            double(:slack_service).as_null_object
          )
 
@@ -143,9 +143,9 @@ RSpec.shared_examples 'slack or mattermost notifications' do
       it "uses the right channel for merge request event" do
         chat_service.update_attributes(merge_request_channel: "random")
 
-        expect(Slack::Notifier).to receive(:new).
-         with(webhook_url, channel: "random").
-         and_return(
+        expect(Slack::Notifier).to receive(:new)
+         .with(webhook_url, channel: "random")
+         .and_return(
            double(:slack_service).as_null_object
          )
 
@@ -155,9 +155,9 @@ RSpec.shared_examples 'slack or mattermost notifications' do
       it "uses the right channel for issue event" do
         chat_service.update_attributes(issue_channel: "random")
 
-        expect(Slack::Notifier).to receive(:new).
-         with(webhook_url, channel: "random").
-         and_return(
+        expect(Slack::Notifier).to receive(:new)
+         .with(webhook_url, channel: "random")
+         .and_return(
            double(:slack_service).as_null_object
          )
 
@@ -167,9 +167,9 @@ RSpec.shared_examples 'slack or mattermost notifications' do
       it "uses the right channel for wiki event" do
         chat_service.update_attributes(wiki_page_channel: "random")
 
-        expect(Slack::Notifier).to receive(:new).
-         with(webhook_url, channel: "random").
-         and_return(
+        expect(Slack::Notifier).to receive(:new)
+         .with(webhook_url, channel: "random")
+         .and_return(
            double(:slack_service).as_null_object
          )
 
@@ -186,9 +186,9 @@ RSpec.shared_examples 'slack or mattermost notifications' do
 
           note_data = Gitlab::DataBuilder::Note.build(issue_note, user)
 
-          expect(Slack::Notifier).to receive(:new).
-           with(webhook_url, channel: "random").
-           and_return(
+          expect(Slack::Notifier).to receive(:new)
+           .with(webhook_url, channel: "random")
+           .and_return(
              double(:slack_service).as_null_object
            )
 
diff --git a/spec/support/stub_configuration.rb b/spec/support/stub_configuration.rb
index b39a23bd18a8f3bdd80ef73b0a23ca4374f77e57..48f454c71876b00eb9c9be967d877b383f163717 100644
--- a/spec/support/stub_configuration.rb
+++ b/spec/support/stub_configuration.rb
@@ -5,8 +5,8 @@ module StubConfiguration
     # Stubbing both of these because we're not yet consistent with how we access
     # current application settings
     allow_any_instance_of(ApplicationSetting).to receive_messages(messages)
-    allow(Gitlab::CurrentSettings.current_application_settings).
-      to receive_messages(messages)
+    allow(Gitlab::CurrentSettings.current_application_settings)
+      .to receive_messages(messages)
   end
 
   def stub_config_setting(messages)
diff --git a/spec/support/stub_gitlab_calls.rb b/spec/support/stub_gitlab_calls.rb
index ded2d5930597d2dedb0d95e8881604f0806b7b5e..78a2ff7374601f7d7864257a38d99e0e241af039 100644
--- a/spec/support/stub_gitlab_calls.rb
+++ b/spec/support/stub_gitlab_calls.rb
@@ -68,22 +68,22 @@ module StubGitlabCalls
   def stub_session
     f = File.read(Rails.root.join('spec/support/gitlab_stubs/session.json'))
 
-    stub_request(:post, "#{gitlab_url}api/v3/session.json").
-      with(body: "{\"email\":\"test@test.com\",\"password\":\"123456\"}",
-           headers: { 'Content-Type' => 'application/json' }).
-           to_return(status: 201, body: f, headers: { 'Content-Type' => 'application/json' })
+    stub_request(:post, "#{gitlab_url}api/v3/session.json")
+      .with(body: "{\"email\":\"test@test.com\",\"password\":\"123456\"}",
+            headers: { 'Content-Type' => 'application/json' })
+      .to_return(status: 201, body: f, headers: { 'Content-Type' => 'application/json' })
   end
 
   def stub_user
     f = File.read(Rails.root.join('spec/support/gitlab_stubs/user.json'))
 
-    stub_request(:get, "#{gitlab_url}api/v3/user?private_token=Wvjy2Krpb7y8xi93owUz").
-      with(headers: { 'Content-Type' => 'application/json' }).
-      to_return(status: 200, body: f, headers: { 'Content-Type' => 'application/json' })
+    stub_request(:get, "#{gitlab_url}api/v3/user?private_token=Wvjy2Krpb7y8xi93owUz")
+      .with(headers: { 'Content-Type' => 'application/json' })
+      .to_return(status: 200, body: f, headers: { 'Content-Type' => 'application/json' })
 
-    stub_request(:get, "#{gitlab_url}api/v3/user?access_token=some_token").
-      with(headers: { 'Content-Type' => 'application/json' }).
-      to_return(status: 200, body: f, headers: { 'Content-Type' => 'application/json' })
+    stub_request(:get, "#{gitlab_url}api/v3/user?access_token=some_token")
+      .with(headers: { 'Content-Type' => 'application/json' })
+      .to_return(status: 200, body: f, headers: { 'Content-Type' => 'application/json' })
   end
 
   def stub_project_8
@@ -99,21 +99,21 @@ module StubGitlabCalls
   def stub_projects
     f = File.read(Rails.root.join('spec/support/gitlab_stubs/projects.json'))
 
-    stub_request(:get, "#{gitlab_url}api/v3/projects.json?archived=false&ci_enabled_first=true&private_token=Wvjy2Krpb7y8xi93owUz").
-      with(headers: { 'Content-Type' => 'application/json' }).
-      to_return(status: 200, body: f, headers: { 'Content-Type' => 'application/json' })
+    stub_request(:get, "#{gitlab_url}api/v3/projects.json?archived=false&ci_enabled_first=true&private_token=Wvjy2Krpb7y8xi93owUz")
+      .with(headers: { 'Content-Type' => 'application/json' })
+      .to_return(status: 200, body: f, headers: { 'Content-Type' => 'application/json' })
   end
 
   def stub_projects_owned
-    stub_request(:get, "#{gitlab_url}api/v3/projects/owned.json?archived=false&ci_enabled_first=true&private_token=Wvjy2Krpb7y8xi93owUz").
-      with(headers: { 'Content-Type' => 'application/json' }).
-      to_return(status: 200, body: "", headers: {})
+    stub_request(:get, "#{gitlab_url}api/v3/projects/owned.json?archived=false&ci_enabled_first=true&private_token=Wvjy2Krpb7y8xi93owUz")
+      .with(headers: { 'Content-Type' => 'application/json' })
+      .to_return(status: 200, body: "", headers: {})
   end
 
   def stub_ci_enable
-    stub_request(:put, "#{gitlab_url}api/v3/projects/2/services/gitlab-ci.json?private_token=Wvjy2Krpb7y8xi93owUz").
-      with(headers: { 'Content-Type' => 'application/json' }).
-      to_return(status: 200, body: "", headers: {})
+    stub_request(:put, "#{gitlab_url}api/v3/projects/2/services/gitlab-ci.json?private_token=Wvjy2Krpb7y8xi93owUz")
+      .with(headers: { 'Content-Type' => 'application/json' })
+      .to_return(status: 200, body: "", headers: {})
   end
 
   def project_hash_array
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index 3f472e59c4973c493b7032206676eb97f813a51c..1c5267c290bef768f6480e2da7b5ab8cfc2b61bc 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -83,13 +83,13 @@ module TestEnv
   end
 
   def disable_mailer
-    allow_any_instance_of(NotificationService).to receive(:mailer).
-      and_return(double.as_null_object)
+    allow_any_instance_of(NotificationService).to receive(:mailer)
+      .and_return(double.as_null_object)
   end
 
   def enable_mailer
-    allow_any_instance_of(NotificationService).to receive(:mailer).
-      and_call_original
+    allow_any_instance_of(NotificationService).to receive(:mailer)
+      .and_call_original
   end
 
   def disable_pre_receive
diff --git a/spec/support/time_tracking_shared_examples.rb b/spec/support/time_tracking_shared_examples.rb
index b407b8097d27e3e83de17f6464164c81c47bc276..0fa74f911f65d940c651197a42fdf323b5145ea4 100644
--- a/spec/support/time_tracking_shared_examples.rb
+++ b/spec/support/time_tracking_shared_examples.rb
@@ -54,7 +54,7 @@ shared_examples 'issuable time tracker' do
   it 'shows the help state when icon is clicked' do
     page.within '.time-tracking-component-wrap' do
       find('.help-button').click
-      expect(page).to have_content 'Track time with slash commands'
+      expect(page).to have_content 'Track time with quick actions'
       expect(page).to have_content 'Learn more'
     end
   end
@@ -64,7 +64,7 @@ shared_examples 'issuable time tracker' do
       find('.help-button').click
       find('.close-help-button').click
 
-      expect(page).not_to have_content 'Track time with slash commands'
+      expect(page).not_to have_content 'Track time with quick actions'
       expect(page).not_to have_content 'Learn more'
     end
   end
@@ -78,8 +78,8 @@ shared_examples 'issuable time tracker' do
   end
 end
 
-def submit_time(slash_command)
-  fill_in 'note[note]', with: slash_command
+def submit_time(quick_action)
+  fill_in 'note[note]', with: quick_action
   find('.js-comment-submit-button').trigger('click')
   wait_for_requests
 end
diff --git a/spec/support/update_invalid_issuable.rb b/spec/support/update_invalid_issuable.rb
index 365c34448ac700be0c627661f32077ba479951ea..1490287681bccf2bf8dd7fa8c6766003648fe35b 100644
--- a/spec/support/update_invalid_issuable.rb
+++ b/spec/support/update_invalid_issuable.rb
@@ -21,8 +21,8 @@ shared_examples 'update invalid issuable' do |klass|
 
   context 'when updating causes conflicts' do
     before do
-      allow_any_instance_of(issuable.class).to receive(:save).
-        and_raise(ActiveRecord::StaleObjectError.new(issuable, :save))
+      allow_any_instance_of(issuable.class).to receive(:save)
+        .and_raise(ActiveRecord::StaleObjectError.new(issuable, :save))
     end
 
     it 'renders edit when format is html' do
diff --git a/spec/support/user_activities_helpers.rb b/spec/support/user_activities_helpers.rb
index f7ca9a31edd4e4e2b71d8dd96d04d6997a4ad352..44feb104644e8b15f0cef8b79e5ba22188e5f1d3 100644
--- a/spec/support/user_activities_helpers.rb
+++ b/spec/support/user_activities_helpers.rb
@@ -1,7 +1,7 @@
 module UserActivitiesHelpers
   def user_activity(user)
-    Gitlab::UserActivities.new.
-      find { |k, _| k == user.id.to_s }&.
+    Gitlab::UserActivities.new
+      .find { |k, _| k == user.id.to_s }&.
       second
   end
 end
diff --git a/spec/support/wait_for_requests.rb b/spec/support/wait_for_requests.rb
index 1cbe609c0e029437dc5da2a096d73ab187ca5a27..b5c3c0f55b8dcd784ed046e3545756097ebe72b9 100644
--- a/spec/support/wait_for_requests.rb
+++ b/spec/support/wait_for_requests.rb
@@ -53,9 +53,3 @@ module WaitForRequests
     Capybara.current_driver == Capybara.javascript_driver
   end
 end
-
-RSpec.configure do |config|
-  config.after(:each, :js) do
-    block_and_wait_for_requests_complete
-  end
-end
diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb
index 1e5f55a738a2bccf90b33df0c059c1c9831854d7..71580a788d09ed6ffa9c8ef7b53b17eefd72d94b 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -47,24 +47,24 @@ describe 'gitlab:app namespace rake task' do
         allow(Kernel).to receive(:system).and_return(true)
         allow(FileUtils).to receive(:cp_r).and_return(true)
         allow(FileUtils).to receive(:mv).and_return(true)
-        allow(Rake::Task["gitlab:shell:setup"]).
-          to receive(:invoke).and_return(true)
+        allow(Rake::Task["gitlab:shell:setup"])
+          .to receive(:invoke).and_return(true)
         ENV['force'] = 'yes'
       end
 
       let(:gitlab_version) { Gitlab::VERSION }
 
       it 'fails on mismatch' do
-        allow(YAML).to receive(:load_file).
-          and_return({ gitlab_version: "not #{gitlab_version}" })
+        allow(YAML).to receive(:load_file)
+          .and_return({ gitlab_version: "not #{gitlab_version}" })
 
-        expect { run_rake_task('gitlab:backup:restore') }.
-          to raise_error(SystemExit)
+        expect { run_rake_task('gitlab:backup:restore') }
+          .to raise_error(SystemExit)
       end
 
       it 'invokes restoration on match' do
-        allow(YAML).to receive(:load_file).
-          and_return({ gitlab_version: gitlab_version })
+        allow(YAML).to receive(:load_file)
+          .and_return({ gitlab_version: gitlab_version })
         expect(Rake::Task['gitlab:db:drop_tables']).to receive(:invoke)
         expect(Rake::Task['gitlab:backup:db:restore']).to receive(:invoke)
         expect(Rake::Task['gitlab:backup:repo:restore']).to receive(:invoke)
@@ -310,8 +310,8 @@ describe 'gitlab:app namespace rake task' do
     end
 
     it 'does not invoke repositories restore' do
-      allow(Rake::Task['gitlab:shell:setup']).
-        to receive(:invoke).and_return(true)
+      allow(Rake::Task['gitlab:shell:setup'])
+        .to receive(:invoke).and_return(true)
       allow($stdout).to receive :write
 
       expect(Rake::Task['gitlab:db:drop_tables']).to receive :invoke
diff --git a/spec/tasks/gitlab/gitaly_rake_spec.rb b/spec/tasks/gitlab/gitaly_rake_spec.rb
index cfa6c9ca8ce3401c1de36fac79d82096e94845fa..d42d2423f151344fc198cf8d4343c94cba7fde12 100644
--- a/spec/tasks/gitlab/gitaly_rake_spec.rb
+++ b/spec/tasks/gitlab/gitaly_rake_spec.rb
@@ -20,8 +20,8 @@ describe 'gitlab:gitaly namespace rake task' do
 
     context 'when an underlying Git command fail' do
       it 'aborts and display a help message' do
-        expect_any_instance_of(Object).
-          to receive(:checkout_or_clone_version).and_raise 'Git error'
+        expect_any_instance_of(Object)
+          .to receive(:checkout_or_clone_version).and_raise 'Git error'
 
         expect { run_rake_task('gitlab:gitaly:install', clone_path) }.to raise_error 'Git error'
       end
@@ -33,8 +33,8 @@ describe 'gitlab:gitaly namespace rake task' do
       end
 
       it 'calls checkout_or_clone_version with the right arguments' do
-        expect_any_instance_of(Object).
-          to receive(:checkout_or_clone_version).with(version: version, repo: repo, target_dir: clone_path)
+        expect_any_instance_of(Object)
+          .to receive(:checkout_or_clone_version).with(version: version, repo: repo, target_dir: clone_path)
 
         run_rake_task('gitlab:gitaly:install', clone_path)
       end
@@ -89,6 +89,7 @@ describe 'gitlab:gitaly namespace rake task' do
         }
       }
       allow(Gitlab.config.repositories).to receive(:storages).and_return(config)
+      allow(Rails.env).to receive(:test?).and_return(false)
 
       expected_output = ''
       Timecop.freeze do
@@ -105,8 +106,8 @@ describe 'gitlab:gitaly namespace rake task' do
         TOML
       end
 
-      expect { run_rake_task('gitlab:gitaly:storage_config')}.
-        to output(expected_output).to_stdout
+      expect { run_rake_task('gitlab:gitaly:storage_config')}
+        .to output(expected_output).to_stdout
 
       parsed_output = TOML.parse(expected_output)
       config.each do |name, params|
diff --git a/spec/tasks/gitlab/task_helpers_spec.rb b/spec/tasks/gitlab/task_helpers_spec.rb
index 3d9ba7cdc6f4a74e32d3741c4d86c04f54485c83..91cc684d0325670391c6aeedc7bc8c47c1edd132 100644
--- a/spec/tasks/gitlab/task_helpers_spec.rb
+++ b/spec/tasks/gitlab/task_helpers_spec.rb
@@ -60,8 +60,8 @@ describe Gitlab::TaskHelpers do
 
   describe '#clone_repo' do
     it 'clones the repo in the target dir' do
-      expect(subject).
-        to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} clone -- #{repo} #{clone_path}])
+      expect(subject)
+        .to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} clone -- #{repo} #{clone_path}])
 
       subject.clone_repo(repo, clone_path)
     end
@@ -69,10 +69,10 @@ describe Gitlab::TaskHelpers do
 
   describe '#checkout_version' do
     it 'clones the repo in the target dir' do
-      expect(subject).
-        to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} fetch --quiet])
-      expect(subject).
-        to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} checkout --quiet #{tag}])
+      expect(subject)
+        .to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} fetch --quiet])
+      expect(subject)
+        .to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} checkout --quiet #{tag}])
 
       subject.checkout_version(tag, clone_path)
     end
@@ -80,8 +80,8 @@ describe Gitlab::TaskHelpers do
 
   describe '#reset_to_version' do
     it 'resets --hard to the given version' do
-      expect(subject).
-        to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} reset --hard #{tag}])
+      expect(subject)
+        .to receive(:run_command!).with(%W[#{Gitlab.config.git.bin_path} -C #{clone_path} reset --hard #{tag}])
 
       subject.reset_to_version(tag, clone_path)
     end
diff --git a/spec/tasks/gitlab/workhorse_rake_spec.rb b/spec/tasks/gitlab/workhorse_rake_spec.rb
index 63d1cf2bbe59732bb985a71dfb1386b2eccd0229..1b68f3044a495df648978d4cdb19010effb1f1c0 100644
--- a/spec/tasks/gitlab/workhorse_rake_spec.rb
+++ b/spec/tasks/gitlab/workhorse_rake_spec.rb
@@ -20,8 +20,8 @@ describe 'gitlab:workhorse namespace rake task' do
 
     context 'when an underlying Git command fail' do
       it 'aborts and display a help message' do
-        expect_any_instance_of(Object).
-          to receive(:checkout_or_clone_version).and_raise 'Git error'
+        expect_any_instance_of(Object)
+          .to receive(:checkout_or_clone_version).and_raise 'Git error'
 
         expect { run_rake_task('gitlab:workhorse:install', clone_path) }.to raise_error 'Git error'
       end
@@ -33,8 +33,8 @@ describe 'gitlab:workhorse namespace rake task' do
       end
 
       it 'calls checkout_or_clone_version with the right arguments' do
-        expect_any_instance_of(Object).
-          to receive(:checkout_or_clone_version).with(version: version, repo: repo, target_dir: clone_path)
+        expect_any_instance_of(Object)
+          .to receive(:checkout_or_clone_version).with(version: version, repo: repo, target_dir: clone_path)
 
         run_rake_task('gitlab:workhorse:install', clone_path)
       end
diff --git a/spec/validators/dynamic_path_validator_spec.rb b/spec/validators/dynamic_path_validator_spec.rb
index 8dbf3eecd23aeb1af4c9b57bee9e42a01e46755a..8bd5306ff986f771890129b042d3e3c32277edce 100644
--- a/spec/validators/dynamic_path_validator_spec.rb
+++ b/spec/validators/dynamic_path_validator_spec.rb
@@ -84,5 +84,14 @@ describe DynamicPathValidator do
 
       expect(group.errors[:path]).to include('users is a reserved name')
     end
+
+    it 'updating to an invalid path is not allowed' do
+      project = create(:empty_project)
+      project.path = 'update'
+
+      validator.validate_each(project, :path, 'update')
+
+      expect(project.errors[:path]).to include('update is a reserved name')
+    end
   end
 end
diff --git a/spec/views/devise/shared/_signin_box.html.haml_spec.rb b/spec/views/devise/shared/_signin_box.html.haml_spec.rb
index 1397bfa5864e8991f3b24a7fb8548467cb3435a1..9adbb0476bed319a175b62557ecff1e39b5c26ae 100644
--- a/spec/views/devise/shared/_signin_box.html.haml_spec.rb
+++ b/spec/views/devise/shared/_signin_box.html.haml_spec.rb
@@ -31,7 +31,7 @@ describe 'devise/shared/_signin_box' do
   def enable_crowd
     allow(view).to receive(:form_based_providers).and_return([:crowd])
     allow(view).to receive(:crowd_enabled?).and_return(true)
-    allow(view).to receive(:omniauth_authorize_path).with(:user, :crowd).
-      and_return('/crowd')
+    allow(view).to receive(:omniauth_authorize_path).with(:user, :crowd)
+      .and_return('/crowd')
   end
 end
diff --git a/spec/views/profiles/show.html.haml_spec.rb b/spec/views/profiles/show.html.haml_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e89a8cb962659e23e4f0841e943626835e2ade4a
--- /dev/null
+++ b/spec/views/profiles/show.html.haml_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+
+describe 'profiles/show' do
+  let(:user) { create(:user) }
+
+  before do
+    assign(:user, user)
+    allow(controller).to receive(:current_user).and_return(user)
+  end
+
+  context 'when the profile page is opened' do
+    it 'displays the correct elements' do
+      render
+
+      expect(rendered).to have_field('user_name', user.name)
+      expect(rendered).to have_field('user_id', user.id)
+    end
+  end
+end
diff --git a/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb b/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e56c0f6be038deffd775e6d220e2db13827b864a
--- /dev/null
+++ b/spec/views/projects/notes/_more_actions_dropdown.html.haml_spec.rb
@@ -0,0 +1,41 @@
+require 'spec_helper'
+
+describe 'projects/notes/_more_actions_dropdown', :view do
+  let(:author_user) { create(:user) }
+  let(:not_author_user) { create(:user) }
+
+  let(:project) { create(:empty_project) }
+  let(:issue) { create(:issue, project: project) }
+  let!(:note) { create(:note_on_issue, author: author_user, noteable: issue, project: project) }
+
+  before do
+    assign(:project, project)
+  end
+
+  it 'shows Report as abuse button if not editable and not current users comment' do
+    render 'projects/notes/more_actions_dropdown', current_user: not_author_user, note_editable: false, note: note
+
+    expect(rendered).to have_link('Report as abuse')
+  end
+
+  it 'does not show the More actions button if not editable and current users comment' do
+    render 'projects/notes/more_actions_dropdown', current_user: author_user, note_editable: false, note: note
+
+    expect(rendered).not_to have_selector('.dropdown.more-actions')
+  end
+
+  it 'shows Report as abuse, Edit and Delete buttons if editable and not current users comment' do
+    render 'projects/notes/more_actions_dropdown', current_user: not_author_user, note_editable: true, note: note
+
+    expect(rendered).to have_link('Report as abuse')
+    expect(rendered).to have_button('Edit comment')
+    expect(rendered).to have_link('Delete comment')
+  end
+
+  it 'shows Edit and Delete buttons if editable and current users comment' do
+    render 'projects/notes/more_actions_dropdown', current_user: author_user, note_editable: true, note: note
+
+    expect(rendered).to have_button('Edit comment')
+    expect(rendered).to have_link('Delete comment')
+  end
+end
diff --git a/spec/views/shared/notes/_form.html.haml_spec.rb b/spec/views/shared/notes/_form.html.haml_spec.rb
index d7d0a5bf56a9b992e78ecff60b276e12842b9a4f..cae6bee2776d7cfe715a2196e800c928d819ecfc 100644
--- a/spec/views/shared/notes/_form.html.haml_spec.rb
+++ b/spec/views/shared/notes/_form.html.haml_spec.rb
@@ -20,8 +20,8 @@ describe 'shared/notes/_form' do
     context "with a note on #{noteable}" do
       let(:note) { build(:"note_on_#{noteable}", project: project) }
 
-      it 'says that markdown and slash commands are supported' do
-        expect(rendered).to have_content('Markdown and slash commands are supported')
+      it 'says that markdown and quick actions are supported' do
+        expect(rendered).to have_content('Markdown and quick actions are supported')
       end
     end
   end
@@ -29,7 +29,7 @@ describe 'shared/notes/_form' do
   context 'with a note on a commit' do
     let(:note) { build(:note_on_commit, project: project) }
 
-    it 'says that only markdown is supported, not slash commands' do
+    it 'says that only markdown is supported, not quick actions' do
       expect(rendered).to have_content('Markdown is supported')
     end
   end
diff --git a/spec/workers/background_migration_worker_spec.rb b/spec/workers/background_migration_worker_spec.rb
index 0d742ae9dc7e35942caf756a7f1f107eb460b169..85939429feb80623611e7ea9420fdd27042e60fe 100644
--- a/spec/workers/background_migration_worker_spec.rb
+++ b/spec/workers/background_migration_worker_spec.rb
@@ -3,9 +3,9 @@ require 'spec_helper'
 describe BackgroundMigrationWorker do
   describe '.perform' do
     it 'performs a background migration' do
-      expect(Gitlab::BackgroundMigration).
-        to receive(:perform).
-        with('Foo', [10, 20])
+      expect(Gitlab::BackgroundMigration)
+        .to receive(:perform)
+        .with('Foo', [10, 20])
 
       described_class.new.perform('Foo', [10, 20])
     end
diff --git a/spec/workers/delete_user_worker_spec.rb b/spec/workers/delete_user_worker_spec.rb
index 5912dd76262fbd9d954763d3068cd5c182214fc4..3659451500504cc0d6f10311c52a4d46bfabbc69 100644
--- a/spec/workers/delete_user_worker_spec.rb
+++ b/spec/workers/delete_user_worker_spec.rb
@@ -5,15 +5,15 @@ describe DeleteUserWorker do
   let!(:current_user) { create(:user) }
 
   it "calls the DeleteUserWorker with the params it was given" do
-    expect_any_instance_of(Users::DestroyService).to receive(:execute).
-                                                      with(user, {})
+    expect_any_instance_of(Users::DestroyService).to receive(:execute)
+                                                      .with(user, {})
 
     described_class.new.perform(current_user.id, user.id)
   end
 
   it "uses symbolized keys" do
-    expect_any_instance_of(Users::DestroyService).to receive(:execute).
-                                                      with(user, test: "test")
+    expect_any_instance_of(Users::DestroyService).to receive(:execute)
+                                                      .with(user, test: "test")
 
     described_class.new.perform(current_user.id, user.id, "test" => "test")
   end
diff --git a/spec/workers/every_sidekiq_worker_spec.rb b/spec/workers/every_sidekiq_worker_spec.rb
index fc9adf47c1e833ee529bea87e3666dff8d16a9ac..30908534eb3b6e1399086ec88b07d6bb9d5c5e37 100644
--- a/spec/workers/every_sidekiq_worker_spec.rb
+++ b/spec/workers/every_sidekiq_worker_spec.rb
@@ -5,8 +5,8 @@ describe 'Every Sidekiq worker' do
     root = Rails.root.join('app', 'workers')
     concerns = root.join('concerns').to_s
 
-    workers = Dir[root.join('**', '*.rb')].
-      reject { |path| path.start_with?(concerns) }
+    workers = Dir[root.join('**', '*.rb')]
+      .reject { |path| path.start_with?(concerns) }
 
     workers.map do |path|
       ns = Pathname.new(path).relative_path_from(root).to_s.gsub('.rb', '')
@@ -22,9 +22,9 @@ describe 'Every Sidekiq worker' do
   end
 
   it 'uses the cronjob queue when the worker runs as a cronjob' do
-    cron_workers = Settings.cron_jobs.
-      map { |job_name, options| options['job_class'].constantize }.
-      to_set
+    cron_workers = Settings.cron_jobs
+      .map { |job_name, options| options['job_class'].constantize }
+      .to_set
 
     workers.each do |worker|
       next unless cron_workers.include?(worker)
diff --git a/spec/workers/expire_pipeline_cache_worker_spec.rb b/spec/workers/expire_pipeline_cache_worker_spec.rb
index 28e5b706803215ced0fa5d8ffa996e1443f9c9f9..e4f78999489e4a71b6fe8bef5f37ddb7e77c1b65 100644
--- a/spec/workers/expire_pipeline_cache_worker_spec.rb
+++ b/spec/workers/expire_pipeline_cache_worker_spec.rb
@@ -37,8 +37,8 @@ describe ExpirePipelineCacheWorker do
     end
 
     it 'updates the cached status for a project' do
-      expect(Gitlab::Cache::Ci::ProjectPipelineStatus).to receive(:update_for_pipeline).
-                                                            with(pipeline)
+      expect(Gitlab::Cache::Ci::ProjectPipelineStatus).to receive(:update_for_pipeline)
+                                                            .with(pipeline)
 
       subject.perform(pipeline.id)
     end
diff --git a/spec/workers/git_garbage_collect_worker_spec.rb b/spec/workers/git_garbage_collect_worker_spec.rb
index f443bb2c9b46914a41c54f0e163e9e609e9d4814..309b3172da11fda574f5df301e9a7dbaa45c5310 100644
--- a/spec/workers/git_garbage_collect_worker_spec.rb
+++ b/spec/workers/git_garbage_collect_worker_spec.rb
@@ -11,8 +11,8 @@ describe GitGarbageCollectWorker do
   describe "#perform" do
     it "flushes ref caches when the task is 'gc'" do
       expect(subject).to receive(:command).with(:gc).and_return([:the, :command])
-      expect(Gitlab::Popen).to receive(:popen).
-        with([:the, :command], project.repository.path_to_repo).and_return(["", 0])
+      expect(Gitlab::Popen).to receive(:popen)
+        .with([:the, :command], project.repository.path_to_repo).and_return(["", 0])
 
       expect_any_instance_of(Repository).to receive(:after_create_branch).and_call_original
       expect_any_instance_of(Repository).to receive(:branch_names).and_call_original
diff --git a/spec/workers/new_note_worker_spec.rb b/spec/workers/new_note_worker_spec.rb
index 8fdbb35afd0d640b64283b0638c2d1cc7db85f29..575361c93d4ac99641926b2543d036b7189ccf99 100644
--- a/spec/workers/new_note_worker_spec.rb
+++ b/spec/workers/new_note_worker_spec.rb
@@ -24,8 +24,8 @@ describe NewNoteWorker do
     let(:unexistent_note_id) { 999 }
 
     it 'logs NewNoteWorker process skipping' do
-      expect(Rails.logger).to receive(:error).
-        with("NewNoteWorker: couldn't find note with ID=999, skipping job")
+      expect(Rails.logger).to receive(:error)
+        .with("NewNoteWorker: couldn't find note with ID=999, skipping job")
 
       described_class.new.perform(unexistent_note_id)
     end
diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb
index 3c93da63f2e04eadb17944531fc100b3cc465cfe..cc9bc29c6ccb5e37d5d9f28b5e0541efc0210707 100644
--- a/spec/workers/post_receive_spec.rb
+++ b/spec/workers/post_receive_spec.rb
@@ -32,7 +32,7 @@ describe PostReceive do
 
   context "with an absolute path as the project identifier" do
     it "searches the project by full path" do
-      expect(Project).to receive(:find_by_full_path).with(project.full_path).and_call_original
+      expect(Project).to receive(:find_by_full_path).with(project.full_path, follow_redirects: true).and_call_original
 
       described_class.new.perform(pwd(project), key_id, base64_changes)
     end
@@ -123,9 +123,9 @@ describe PostReceive do
     end
 
     it "does not run if the author is not in the project" do
-      allow_any_instance_of(Gitlab::GitPostReceive).
-        to receive(:identify_using_ssh_key).
-        and_return(nil)
+      allow_any_instance_of(Gitlab::GitPostReceive)
+        .to receive(:identify_using_ssh_key)
+        .and_return(nil)
 
       expect(project).not_to receive(:execute_hooks)
 
diff --git a/spec/workers/process_commit_worker_spec.rb b/spec/workers/process_commit_worker_spec.rb
index 4e036285e8c1afb60d9c2f57a6dd32e850dd238a..6ebc94bb5444d402c52cf2b51332109b79b86837 100644
--- a/spec/workers/process_commit_worker_spec.rb
+++ b/spec/workers/process_commit_worker_spec.rb
@@ -48,11 +48,11 @@ describe ProcessCommitWorker do
   describe '#process_commit_message' do
     context 'when pushing to the default branch' do
       it 'closes issues that should be closed per the commit message' do
-        allow(commit).to receive(:safe_message).
-          and_return("Closes #{issue.to_reference}")
+        allow(commit).to receive(:safe_message)
+          .and_return("Closes #{issue.to_reference}")
 
-        expect(worker).to receive(:close_issues).
-          with(project, user, user, commit, [issue])
+        expect(worker).to receive(:close_issues)
+          .with(project, user, user, commit, [issue])
 
         worker.process_commit_message(project, commit, user, user, true)
       end
@@ -60,8 +60,8 @@ describe ProcessCommitWorker do
 
     context 'when pushing to a non-default branch' do
       it 'does not close any issues' do
-        allow(commit).to receive(:safe_message).
-          and_return("Closes #{issue.to_reference}")
+        allow(commit).to receive(:safe_message)
+          .and_return("Closes #{issue.to_reference}")
 
         expect(worker).not_to receive(:close_issues)
 
@@ -102,8 +102,8 @@ describe ProcessCommitWorker do
 
   describe '#update_issue_metrics' do
     it 'updates any existing issue metrics' do
-      allow(commit).to receive(:safe_message).
-        and_return("Closes #{issue.to_reference}")
+      allow(commit).to receive(:safe_message)
+        .and_return("Closes #{issue.to_reference}")
 
       worker.update_issue_metrics(commit, user)
 
@@ -113,8 +113,8 @@ describe ProcessCommitWorker do
     end
 
     it "doesn't execute any queries with false conditions" do
-      allow(commit).to receive(:safe_message).
-        and_return("Lorem Ipsum")
+      allow(commit).to receive(:safe_message)
+        .and_return("Lorem Ipsum")
 
       expect { worker.update_issue_metrics(commit, user) }.not_to make_queries_matching(/WHERE (?:1=0|0=1)/)
     end
@@ -128,8 +128,8 @@ describe ProcessCommitWorker do
     end
 
     it 'parses date strings into Time instances' do
-      commit = worker.
-        build_commit(project, id: '123', authored_date: Time.now.to_s)
+      commit = worker
+        .build_commit(project, id: '123', authored_date: Time.now.to_s)
 
       expect(commit.authored_date).to be_an_instance_of(Time)
     end
diff --git a/spec/workers/project_cache_worker_spec.rb b/spec/workers/project_cache_worker_spec.rb
index a4ba5f7c9430f164de827202aaa92a56a2982a0b..6b1f2ff3227f93db1cc62b4677bf75601da4a9bb 100644
--- a/spec/workers/project_cache_worker_spec.rb
+++ b/spec/workers/project_cache_worker_spec.rb
@@ -7,8 +7,8 @@ describe ProjectCacheWorker do
 
   describe '#perform' do
     before do
-      allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).
-        and_return(true)
+      allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain)
+        .and_return(true)
     end
 
     context 'with a non-existing project' do
@@ -39,9 +39,9 @@ describe ProjectCacheWorker do
       end
 
       it 'refreshes the method caches' do
-        expect_any_instance_of(Repository).to receive(:refresh_method_caches).
-          with(%i(readme)).
-          and_call_original
+        expect_any_instance_of(Repository).to receive(:refresh_method_caches)
+          .with(%i(readme))
+          .and_call_original
 
         worker.perform(project.id, %w(readme))
       end
@@ -51,9 +51,9 @@ describe ProjectCacheWorker do
           allow(MarkupHelper).to receive(:gitlab_markdown?).and_return(false)
           allow(MarkupHelper).to receive(:plain?).and_return(true)
 
-          expect_any_instance_of(Repository).to receive(:refresh_method_caches).
-                                                  with(%i(readme)).
-                                                  and_call_original
+          expect_any_instance_of(Repository).to receive(:refresh_method_caches)
+                                                  .with(%i(readme))
+                                                  .and_call_original
           worker.perform(project.id, %w(readme))
         end
       end
@@ -63,9 +63,9 @@ describe ProjectCacheWorker do
   describe '#update_statistics' do
     context 'when a lease could not be obtained' do
       it 'does not update the repository size' do
-        allow(worker).to receive(:try_obtain_lease_for).
-          with(project.id, :update_statistics).
-          and_return(false)
+        allow(worker).to receive(:try_obtain_lease_for)
+          .with(project.id, :update_statistics)
+          .and_return(false)
 
         expect(statistics).not_to receive(:refresh!)
 
@@ -75,9 +75,9 @@ describe ProjectCacheWorker do
 
     context 'when a lease could be obtained' do
       it 'updates the project statistics' do
-        allow(worker).to receive(:try_obtain_lease_for).
-          with(project.id, :update_statistics).
-          and_return(true)
+        allow(worker).to receive(:try_obtain_lease_for)
+          .with(project.id, :update_statistics)
+          .and_return(true)
 
         expect(statistics).to receive(:refresh!)
           .with(only: %i(repository_size))
diff --git a/spec/workers/propagate_service_template_worker_spec.rb b/spec/workers/propagate_service_template_worker_spec.rb
index 7040d5ef81c9780f98f833d4bf098bdee97cfad0..b8b65ead9b36e4ab6871a97efb07ea9b84d601c8 100644
--- a/spec/workers/propagate_service_template_worker_spec.rb
+++ b/spec/workers/propagate_service_template_worker_spec.rb
@@ -15,8 +15,8 @@ describe PropagateServiceTemplateWorker do
   end
 
   before do
-    allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).
-      and_return(true)
+    allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain)
+      .and_return(true)
   end
 
   describe '#perform' do
diff --git a/spec/workers/repository_fork_worker_spec.rb b/spec/workers/repository_fork_worker_spec.rb
index 6ea5569b4385f41f9528f71887ef59115b16e446..d9e9409840f6deb23ec293ef4c039562ee1cf05a 100644
--- a/spec/workers/repository_fork_worker_spec.rb
+++ b/spec/workers/repository_fork_worker_spec.rb
@@ -35,11 +35,11 @@ describe RepositoryForkWorker do
         fork_project.namespace.full_path
       ).and_return(true)
 
-      expect_any_instance_of(Repository).to receive(:expire_emptiness_caches).
-        and_call_original
+      expect_any_instance_of(Repository).to receive(:expire_emptiness_caches)
+        .and_call_original
 
-      expect_any_instance_of(Repository).to receive(:expire_exists_cache).
-        and_call_original
+      expect_any_instance_of(Repository).to receive(:expire_exists_cache)
+        .and_call_original
 
       subject.perform(project.id, '/test/path', project.full_path,
                       fork_project.namespace.full_path)
diff --git a/spec/workers/repository_import_worker_spec.rb b/spec/workers/repository_import_worker_spec.rb
index 9c277c501f18800bf9ec3b27f87347df65bc7adf..6b30dabc80e0a33ff0b3868f1b95d4d656ef7f91 100644
--- a/spec/workers/repository_import_worker_spec.rb
+++ b/spec/workers/repository_import_worker_spec.rb
@@ -8,8 +8,8 @@ describe RepositoryImportWorker do
   describe '#perform' do
     context 'when the import was successful' do
       it 'imports a project' do
-        expect_any_instance_of(Projects::ImportService).to receive(:execute).
-          and_return({ status: :ok })
+        expect_any_instance_of(Projects::ImportService).to receive(:execute)
+          .and_return({ status: :ok })
 
         expect_any_instance_of(Repository).to receive(:expire_emptiness_caches)
         expect_any_instance_of(Project).to receive(:import_finish)