diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
index 82be5eeb5ff..56cfd441fbc 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
@@ -45,7 +45,8 @@ export default {
       isMakingRequest: false,
       isMergingImmediately: false,
       commitMessage: this.mr.commitMessage,
-      squashBeforeMerge: this.mr.squash,
+      squashBeforeMerge: this.mr.squashIsSelected,
+      isSquashReadOnly: this.mr.squashIsReadonly,
       successSvg,
       warningSvg,
       squashCommitMessage: this.mr.squashCommitMessage,
@@ -106,7 +107,12 @@ export default {
       return this.isMergeButtonDisabled;
     },
     shouldShowSquashBeforeMerge() {
-      const { commitsCount, enableSquashBeforeMerge } = this.mr;
+      const { commitsCount, enableSquashBeforeMerge, squashIsReadonly, squashIsSelected } = this.mr;
+
+      if (squashIsReadonly && !squashIsSelected) {
+        return false;
+      }
+
       return enableSquashBeforeMerge && commitsCount > 1;
     },
     shouldShowMergeControls() {
@@ -344,7 +350,7 @@ export default {
                 v-if="shouldShowSquashBeforeMerge"
                 v-model="squashBeforeMerge"
                 :help-path="mr.squashBeforeMergeHelpPath"
-                :is-disabled="isMergeButtonDisabled"
+                :is-disabled="isSquashReadOnly"
               />
             </template>
             <template v-else>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue
index 5305894873f..14e0a9e18a3 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/squash_before_merge.vue
@@ -30,7 +30,7 @@ export default {
 
 <template>
   <div class="inline">
-    <label>
+    <label :class="{ 'gl-text-gray-600': isDisabled }" data-testid="squashLabel">
       <input
         :checked="value"
         :disabled="isDisabled"
diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
index d61e122d612..8bba40a593d 100644
--- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
+++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
@@ -22,7 +22,10 @@ export default class MergeRequestStore {
     const pipelineStatus = data.pipeline ? data.pipeline.details.status : null;
 
     this.squash = data.squash;
+    this.squashIsEnabledByDefault = data.squash_enabled_by_default;
+    this.squashIsReadonly = data.squash_readonly;
     this.enableSquashBeforeMerge = this.enableSquashBeforeMerge || true;
+    this.squashIsSelected = data.squash_readonly ? data.squash_on_merge : data.squash;
 
     this.iid = data.iid;
     this.title = data.title;
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index f27ac8b53e4..c5877012ea8 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -19,6 +19,9 @@ class Projects::PipelinesController < Projects::ApplicationController
   end
   before_action :ensure_pipeline, only: [:show]
 
+  # Will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/225596
+  before_action :redirect_for_legacy_scope_filter, only: [:index], if: -> { request.format.html? }
+
   around_action :allow_gitaly_ref_name_caching, only: [:index, :show]
 
   track_unique_visits :charts, target_id: 'p_analytics_pipelines'
@@ -34,9 +37,6 @@ class Projects::PipelinesController < Projects::ApplicationController
       .page(params[:page])
       .per(30)
 
-    @running_count = limited_pipelines_count(project, 'running')
-    @pending_count = limited_pipelines_count(project, 'pending')
-    @finished_count = limited_pipelines_count(project, 'finished')
     @pipelines_count = limited_pipelines_count(project)
 
     respond_to do |format|
@@ -47,10 +47,7 @@ class Projects::PipelinesController < Projects::ApplicationController
         render json: {
           pipelines: serialize_pipelines,
           count: {
-            all: @pipelines_count,
-            running: @running_count,
-            pending: @pending_count,
-            finished: @finished_count
+            all: @pipelines_count
           }
         }
       end
@@ -229,6 +226,12 @@ class Projects::PipelinesController < Projects::ApplicationController
     render_404 unless pipeline
   end
 
+  def redirect_for_legacy_scope_filter
+    return unless %w[running pending].include?(params[:scope])
+
+    redirect_to url_for(safe_params.except(:scope).merge(status: safe_params[:scope])), status: :moved_permanently
+  end
+
   # rubocop: disable CodeReuse/ActiveRecord
   def pipeline
     @pipeline ||= if params[:id].blank? && params[:latest]
diff --git a/changelogs/unreleased/lm-remove-counts-and-redirect.yml b/changelogs/unreleased/lm-remove-counts-and-redirect.yml
new file mode 100644
index 00000000000..c8698e6b8e5
--- /dev/null
+++ b/changelogs/unreleased/lm-remove-counts-and-redirect.yml
@@ -0,0 +1,5 @@
+---
+title: Remove count for pending/running/finished pipelines in tabs
+merge_request: 35693
+author:
+type: changed
diff --git a/doc/administration/gitaly/img/praefect_storage_v13_1.png b/doc/administration/gitaly/img/praefect_storage_v13_1.png
deleted file mode 100644
index 8e36ab82fa4..00000000000
Binary files a/doc/administration/gitaly/img/praefect_storage_v13_1.png and /dev/null differ
diff --git a/doc/administration/gitaly/praefect.md b/doc/administration/gitaly/praefect.md
index 13ec67068dd..fec6e040c9c 100644
--- a/doc/administration/gitaly/praefect.md
+++ b/doc/administration/gitaly/praefect.md
@@ -103,7 +103,7 @@ GitLab](https://about.gitlab.com/install/).
 
 You will need the IP/host address for each node.
 
-1. `LOAD_BALANCER_SERVER_ADDRESS`: the IP/hots address of the load balancer
+1. `LOAD_BALANCER_SERVER_ADDRESS`: the IP/host address of the load balancer
 1. `POSTGRESQL_SERVER_ADDRESS`: the IP/host address of the PostgreSQL server
 1. `PRAEFECT_HOST`: the IP/host address of the Praefect server
 1. `GITALY_HOST`: the IP/host address of each Gitaly server
@@ -281,9 +281,15 @@ application server, or a Gitaly node.
 1. Configure the **Praefect** cluster to connect to each Gitaly node in the
    cluster by editing `/etc/gitlab/gitlab.rb`.
 
-   In the example below we have configured one virtual storage (or shard) named
-   `storage-1`. This cluster has three Gitaly nodes `gitaly-1`, `gitaly-2`, and
-   `gitaly-3`, which will be replicas of each other.
+   The virtual storage's name must match the configured storage name in GitLab
+   configuration. In a later step, we configure the storage name as `default`
+   so we use `default` here as well. This cluster has three Gitaly nodes `gitaly-1`,
+   `gitaly-2`, and `gitaly-3`, which will be replicas of each other.
+
+   CAUTION: **CAUTION:** If you have data on an already existing storage called
+   `default`, you should configure the virtual storage with another name and
+   [migrate the data to the Praefect storage](#migrating-existing-repositories-to-praefect)
+   afterwards.
 
    Replace `PRAEFECT_INTERNAL_TOKEN` with a strong secret, which will be used by
    Praefect when communicating with Gitaly nodes in the cluster. This token is
@@ -302,7 +308,7 @@ application server, or a Gitaly node.
    # Name of storage hash must match storage name in git_data_dirs on GitLab
    # server ('praefect') and in git_data_dirs on Gitaly nodes ('gitaly-1')
    praefect['virtual_storages'] = {
-     'storage-1' => {
+     'default' => {
        'gitaly-1' => {
          'address' => 'tcp://GITALY_HOST:8075',
          'token'   => 'PRAEFECT_INTERNAL_TOKEN',
@@ -555,6 +561,16 @@ Particular attention should be shown to:
    external_url 'GITLAB_SERVER_URL'
    ```
 
+1. Disable the default Gitaly service running on the GitLab host. It won't be needed
+   as GitLab will connect to the configured cluster.
+
+   CAUTION: **CAUTION** If you have existing data stored on the default Gitaly storage,
+   you should [migrate the data your Praefect storage first](#migrating-existing-repositories-to-praefect).
+
+   ```ruby
+   gitaly['enable'] = false
+   ```
+
 1. Add the Praefect cluster as a storage location by editing
    `/etc/gitlab/gitlab.rb`.
 
@@ -562,28 +578,17 @@ Particular attention should be shown to:
 
    - `LOAD_BALANCER_SERVER_ADDRESS` with the IP address or hostname of the load
      balancer.
-   - `GITLAB_HOST` with the IP address or hostname of the GitLab server
    - `PRAEFECT_EXTERNAL_TOKEN` with the real secret
 
    ```ruby
    git_data_dirs({
      "default" => {
-       "gitaly_address" => "tcp://GITLAB_HOST:8075"
-     },
-     "storage-1" => {
        "gitaly_address" => "tcp://LOAD_BALANCER_SERVER_ADDRESS:2305",
        "gitaly_token" => 'PRAEFECT_EXTERNAL_TOKEN'
      }
    })
    ```
 
-1. Allow Gitaly to listen on a TCP port by editing
-   `/etc/gitlab/gitlab.rb`
-
-   ```ruby
-   gitaly['listen_addr'] = '0.0.0.0:8075'
-   ```
-
 1. Configure the `gitlab_shell['secret_token']` so that callbacks from Gitaly
    nodes during a `git push` are properly authenticated by editing
    `/etc/gitlab/gitlab.rb`:
@@ -632,14 +637,6 @@ Particular attention should be shown to:
    gitlab-ctl reconfigure
    ```
 
-1. To ensure that Gitaly [has updated its Prometheus listen
-   address](https://gitlab.com/gitlab-org/gitaly/-/issues/2734), [restart
-   Gitaly](../restart_gitlab.md#omnibus-gitlab-restart):
-
-   ```shell
-   gitlab-ctl restart gitaly
-   ```
-
 1. Verify each `gitlab-shell` on each Gitaly instance can reach GitLab. On each Gitaly instance run:
 
    ```shell
@@ -652,16 +649,11 @@ Particular attention should be shown to:
    gitlab-rake gitlab:gitaly:check
    ```
 
-1. Update the **Repository storage** settings from **Admin Area > Settings >
-   Repository > Repository storage** to make the newly configured Praefect
-   cluster the storage location for new Git repositories.
+1. Check in **Admin Area > Settings > Repository > Repository storage** that the Praefect storage
+   is configured to store new repositories. Following this guide, the `default` storage should have
+   weight 100 to store all new repositories.
 
-   - The default weight is 0.
-   - The Praefect weight is 100.
-
-   ![Update repository storage](img/praefect_storage_v13_1.png)
-
-1. Verify everything is still working by creating a new project. Check the
+1. Verify everything is working by creating a new project. Check the
    "Initialize repository with a README" box so that there is content in the
    repository that viewed. If the project is created, and you can see the
    README file, it works!
@@ -790,12 +782,15 @@ The Praefect `dataloss` sub-command helps identify lost writes by checking for u
 
 ```shell
 sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml dataloss [-virtual-storage <virtual-storage>]
+```
 
 If the virtual storage is not specified, every configured virtual storage is checked for data loss.
 
 ```shell
 sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml dataloss
+```
 
+```shell
 Virtual storage: default
   Current read-only primary: gitaly-2
   Previous write-enabled primary: gitaly-1
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 6d56ab9befe..c38ec95144e 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -1052,7 +1052,7 @@ POST /projects
 | `show_default_award_emojis` | boolean | no | Show default award emojis |
 | `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push |
 | `container_registry_enabled` | boolean | no | Enable container registry for this project |
-| `container_expiration_policy_attributes` | hash | no | Update the image expiration policy for this project. Accepts: `cadence` (string), `keep_n` (string), `older_than` (string), `name_regex` (string), `name_regex_delete` (string), `name_regex_keep` (string), `enabled` (boolean) |
+| `container_expiration_policy_attributes` | hash | no | Update the image cleanup policy for this project. Accepts: `cadence` (string), `keep_n` (string), `older_than` (string), `name_regex` (string), `name_regex_delete` (string), `name_regex_keep` (string), `enabled` (boolean) |
 | `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
 | `visibility` | string | no | See [project visibility level](#project-visibility-level) |
 | `import_url` | string | no | URL to import repository from |
@@ -1193,7 +1193,7 @@ PUT /projects/:id
 | `show_default_award_emojis` | boolean | no | Show default award emojis |
 | `resolve_outdated_diff_discussions` | boolean | no | Automatically resolve merge request diffs discussions on lines changed with a push |
 | `container_registry_enabled` | boolean | no | Enable container registry for this project |
-| `container_expiration_policy_attributes` | hash | no | Update the image expiration policy for this project. Accepts: `cadence` (string), `keep_n` (string), `older_than` (string), `name_regex` (string), `name_regex_delete` (string), `name_regex_keep` (string), `enabled` (boolean) |
+| `container_expiration_policy_attributes` | hash | no | Update the image cleanup policy for this project. Accepts: `cadence` (string), `keep_n` (string), `older_than` (string), `name_regex` (string), `name_regex_delete` (string), `name_regex_keep` (string), `enabled` (boolean) |
 | `shared_runners_enabled` | boolean | no | Enable shared runners for this project |
 | `visibility` | string | no | See [project visibility level](#project-visibility-level) |
 | `import_url` | string | no | URL to import repository from |
diff --git a/doc/development/i18n/proofreader.md b/doc/development/i18n/proofreader.md
index 8c1f62330f4..935a171f34c 100644
--- a/doc/development/i18n/proofreader.md
+++ b/doc/development/i18n/proofreader.md
@@ -105,6 +105,7 @@ are very appreciative of the work done by translators and proofreaders!
   - Proofreaders needed.
 - Turkish
   - Ali Demirtaş - [GitLab](https://gitlab.com/alidemirtas), [CrowdIn](https://crowdin.com/profile/alidemirtas)
+  - Rıfat Ünalmış (Rifat Unalmis) - [GitLab](https://gitlab.com/runalmis), [CrowdIn](https://crowdin.com/profile/runalmis)
 - Ukrainian
   - Volodymyr Sobotovych - [GitLab](https://gitlab.com/wheleph), [CrowdIn](https://crowdin.com/profile/wheleph)
   - Andrew Vityuk - [GitLab](https://gitlab.com/3_1_3_u), [CrowdIn](https://crowdin.com/profile/andruwa13)
diff --git a/doc/user/packages/container_registry/img/expiration_policy_app_v13_0.png b/doc/user/packages/container_registry/img/expiration_policy_app_v13_0.png
deleted file mode 100644
index 93c9e00a8f5..00000000000
Binary files a/doc/user/packages/container_registry/img/expiration_policy_app_v13_0.png and /dev/null differ
diff --git a/doc/user/packages/container_registry/index.md b/doc/user/packages/container_registry/index.md
index ee455eeda8e..bf35db8ea2c 100644
--- a/doc/user/packages/container_registry/index.md
+++ b/doc/user/packages/container_registry/index.md
@@ -71,7 +71,7 @@ This view will:
 - Allow you to [delete](#delete-images-from-within-gitlab) one or more image repository.
 - Allow you to navigate to the image repository details page.
 - Show a **Quick start** dropdown with the most common commands to log in, build and push
-- Optionally, a banner will be visible if the [expiration policy](#expiration-policy) is enabled for this project.
+- Optionally, a banner will be visible if the [cleanup policy](#cleanup-policy) is enabled for this project.
 
 ### Control Container Registry for your group
 
@@ -486,29 +486,33 @@ You can download the latest `reg` release from
 the code example by changing the `REG_SHA256` and `REG_VERSION` variables
 defined in the `delete_image` job.
 
-### Delete images using an expiration policy
+### Delete images by using a cleanup policy
 
-You can create a per-project [expiration policy](#expiration-policy) to ensure
-older tags and images are regularly removed from the Container Registry.
+You can create a per-project [cleanup policy](#cleanup-policy) to ensure older tags and images are regularly removed from the
+Container Registry.
 
-## Expiration policy
+## Cleanup policy
 
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/15398) in GitLab 12.8.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/15398) in GitLab 12.8.
+> - [Renamed](https://gitlab.com/gitlab-org/gitlab/-/issues/218737) from "expiration policy" to "cleanup policy" in GitLab 13.2.
 
 For a specific project, if you want to remove tags you no longer need,
-you can create an expiration policy. When the policy is applied, tags matching the regex pattern are removed.
+you can create a cleanup policy. When the policy is applied, tags matching the regex pattern are removed.
 The underlying layers and images remain.
 
 To delete the underlying layers and images no longer associated with any tags, Instance Administrators can use
 [garbage collection](../../../administration/packages/container_registry.md#removing-unused-layers-not-referenced-by-manifests) with the `-m` switch.
 
 NOTE: **Note:**
-For GitLab.com, expiration policies are not available for projects created before GitLab 12.8.
-For self-managed instances, expiration policies may be enabled by an admin in the
+For GitLab.com, cleanup policies are not available for projects created
+before this feature was deployed to production (February 2020).
+Support for pre-existing projects on GitLab.com
+[is planned](https://gitlab.com/gitlab-org/gitlab/-/issues/196124).
+For self-managed instances, cleanup policies may be enabled by an admin in the
 [CI/CD Package Registry settings](./../../admin_area/settings/index.md#cicd).
 Note the inherent [risks involved](./index.md#use-with-external-container-registries).
 
-The expiration policy algorithm starts by collecting all the tags for a given repository in a list,
+The cleanup policy algorithm starts by collecting all the tags for a given repository in a list,
 then goes through a process of excluding tags from it until only the ones to be deleted remain:
 
 1. Collect all the tags for a given repository in a list.
@@ -517,43 +521,41 @@ then goes through a process of excluding tags from it until only the ones to be
 1. Excludes any tags that do not have a manifest (not part of the options).
 1. Orders the remaining tags by `created_date`.
 1. Excludes from the list the N tags based on the `keep_n` value (Number of tags to retain).
-1. Excludes from the list the tags more recent than the `older_than` value (Expiration interval).
+1. Excludes from the list the tags more recent than the `older_than` value (Cleanup interval).
 1. Excludes from the list any tags matching the `name_regex_keep` value (Images to preserve).
 1. Finally, the remaining tags in the list are deleted from the Container Registry.
 
-### Managing project expiration policy through the UI
+### Managing project cleanup policy through the UI
 
-To manage project expiration policy, navigate to **{settings}** **Settings > CI/CD > Container Registry tag expiration policy**.
-
-![Expiration Policy App](img/expiration_policy_app_v13_0.png)
+To manage project cleanup policy, navigate to **{settings}** **Settings > CI/CD > Container Registry tag cleanup policy**.
 
 The UI allows you to configure the following:
 
-- **Expiration policy:** enable or disable the expiration policy.
-- **Expiration interval:** how long tags are exempt from being deleted.
-- **Expiration schedule:** how often the cron job checking the tags should run.
+- **Cleanup policy:** enable or disable the cleanup policy.
+- **Cleanup interval:** how long tags are exempt from being deleted.
+- **Cleanup schedule:** how often the cron job checking the tags should run.
 - **Number of tags to retain:** how many tags to _always_ keep for each image.
-- **Docker tags with names matching this regex pattern will expire:** the regex used to determine what tags should be expired. To qualify all tags for expiration, use the default value of `.*`.
+- **Docker tags with names matching this regex pattern will expire:** the regex used to determine what tags should be cleaned up. To qualify all tags for cleanup, use the default value of `.*`.
 - **Docker tags with names matching this regex pattern will be preserved:** the regex used to determine what tags should be preserved. To preserve all tags, use the default value of `.*`.
 
-#### Troubleshooting expiration policies
+#### Troubleshooting cleanup policies
 
 If you see the following message:
 
-"Something went wrong while updating the expiration policy."
+"Something went wrong while updating the cleanup policy."
 
 Check the regex patterns to ensure they are valid.
 
 You can use [Rubular](https://rubular.com/) to check your regex.
 View some common [regex pattern examples](#regex-pattern-examples).
 
-### Managing project expiration policy through the API
+### Managing project cleanup policy through the API
 
-You can set, update, and disable the expiration policies using the GitLab API.
+You can set, update, and disable the cleanup policies using the GitLab API.
 
 Examples:
 
-- Select all tags, keep at least 1 tag per image, expire any tag older than 14 days, run once a month, preserve any images with the name `master` and the policy is enabled:
+- Select all tags, keep at least 1 tag per image, clean up any tag older than 14 days, run once a month, preserve any images with the name `master` and the policy is enabled:
 
   ```shell
   curl --request PUT --header 'Content-Type: application/json;charset=UTF-8' --header "PRIVATE-TOKEN: <your_access_token>" --data-binary '{"container_expiration_policy_attributes":{"cadence":"1month","enabled":true,"keep_n":1,"older_than":"14d","name_regex":"","name_regex_delete":".*","name_regex_keep":".*-master"}}' 'https://gitlab.example.com/api/v4/projects/2'
@@ -564,15 +566,15 @@ See the API documentation for further details: [Edit project](../../../api/proje
 ### Use with external container registries
 
 When using an [external container registry](./../../../administration/packages/container_registry.md#use-an-external-container-registry-with-gitlab-as-an-auth-endpoint),
-running an expiration policy on a project may have some performance risks. If a project is going to run
+running a cleanup policy on a project may have some performance risks. If a project is going to run
 a policy that will remove large quantities of tags (in the thousands), the GitLab background jobs that
-run the policy may get backed up or fail completely. It is recommended you only enable container expiration
+run the policy may get backed up or fail completely. It is recommended you only enable container cleanup
 policies for projects that were created before GitLab 12.8 if you are confident the amount of tags
 being cleaned up will be minimal.
 
 ### Regex pattern examples
 
-Expiration policies use regex patterns to determine which tags should be preserved or removed, both in the UI and the API.
+Cleanup policies use regex patterns to determine which tags should be preserved or removed, both in the UI and the API.
 
 Here are examples of regex patterns you may want to use:
 
@@ -616,7 +618,7 @@ once you have pushed images, because the images are signed, and the
 signature includes the repository name. To move or rename a repository with a
 Container Registry, you will have to delete all existing images.
 - Prior to GitLab 12.10, any tags that use the same image ID as the `latest` tag
-will not be deleted by the expiration policy.
+will not be deleted by the cleanup policy.
 
 ## Troubleshooting the GitLab Container Registry
 
diff --git a/lib/gitlab/background_migration/cleanup_concurrent_schema_change.rb b/lib/gitlab/background_migration/cleanup_concurrent_schema_change.rb
index 54f77f184d5..91b50c1a493 100644
--- a/lib/gitlab/background_migration/cleanup_concurrent_schema_change.rb
+++ b/lib/gitlab/background_migration/cleanup_concurrent_schema_change.rb
@@ -2,7 +2,7 @@
 
 module Gitlab
   module BackgroundMigration
-    # Base class for cleaning up concurrent schema changes.
+    # Base class for background migration for rename/type changes.
     class CleanupConcurrentSchemaChange
       include Database::MigrationHelpers
 
@@ -10,7 +10,7 @@ module Gitlab
       # old_column - The name of the old (to drop) column.
       # new_column - The name of the new column.
       def perform(table, old_column, new_column)
-        return unless column_exists?(table, new_column)
+        return unless column_exists?(table, new_column) && column_exists?(table, old_column)
 
         rows_to_migrate = define_model_for(table)
           .where(new_column => nil)
@@ -28,6 +28,10 @@ module Gitlab
         end
       end
 
+      def cleanup_concurrent_schema_change(_table, _old_column, _new_column)
+        raise NotImplementedError
+      end
+
       # These methods are necessary so we can re-use the migration helpers in
       # this class.
       def connection
diff --git a/qa/qa/specs/features/api/3_create/repository/changing_repository_storage_spec.rb b/qa/qa/specs/features/api/3_create/repository/changing_repository_storage_spec.rb
index d5ab6a3544d..4f00a16d673 100644
--- a/qa/qa/specs/features/api/3_create/repository/changing_repository_storage_spec.rb
+++ b/qa/qa/specs/features/api/3_create/repository/changing_repository_storage_spec.rb
@@ -2,7 +2,7 @@
 
 module QA
   context 'Create' do
-    describe 'Changing Gitaly repository storage', :orchestrated, :requires_admin do
+    describe 'Changing Gitaly repository storage', :requires_admin do
       shared_examples 'repository storage move' do
         it 'confirms a `finished` status after moving project repository storage' do
           expect(project).to have_file('README.md')
@@ -24,7 +24,7 @@ module QA
         end
       end
 
-      context 'when moving from one Gitaly storage to another', :repository_storage do
+      context 'when moving from one Gitaly storage to another', :orchestrated, :repository_storage do
         let(:project) do
           Resource::Project.fabricate_via_api! do |project|
             project.name = 'repo-storage-move-status'
@@ -36,6 +36,8 @@ module QA
         it_behaves_like 'repository storage move'
       end
 
+      # Note: This test doesn't have the :orchestrated tag because it runs in the Test::Integration::Praefect
+      # scenario with other tests that aren't considered orchestrated.
       context 'when moving from Gitaly to Gitaly Cluster', :requires_praefect do
         let(:project) do
           Resource::Project.fabricate_via_api! do |project|
diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb
index 6c75e766fbd..872f0e97b09 100644
--- a/spec/controllers/projects/pipelines_controller_spec.rb
+++ b/spec/controllers/projects/pipelines_controller_spec.rb
@@ -37,9 +37,6 @@ RSpec.describe Projects::PipelinesController do
         expect(json_response).to include('pipelines')
         expect(json_response['pipelines'].count).to eq 6
         expect(json_response['count']['all']).to eq '6'
-        expect(json_response['count']['running']).to eq '2'
-        expect(json_response['count']['pending']).to eq '1'
-        expect(json_response['count']['finished']).to eq '3'
 
         json_response.dig('pipelines', 0, 'details', 'stages').tap do |stages|
           expect(stages.count).to eq 3
@@ -122,13 +119,15 @@ RSpec.describe Projects::PipelinesController do
       end
     end
 
-    context 'filter by scope' do
-      it 'returns matched pipelines' do
-        get_pipelines_index_json(scope: 'running')
+    context 'when user tries to access legacy scope via URL' do
+      it 'redirects to all pipelines with that status instead' do
+        get_pipelines_index_html(scope: 'running')
 
-        check_pipeline_response(returned: 2, all: 6, running: 2, pending: 1, finished: 3)
+        expect(response).to redirect_to(project_pipelines_path(project, status: 'running', format: :html))
       end
+    end
 
+    context 'filter by scope' do
       context 'scope is branches or tags' do
         before do
           create(:ci_pipeline, :failed, project: project, ref: 'v1.0.0', tag: true)
@@ -140,7 +139,7 @@ RSpec.describe Projects::PipelinesController do
           it 'returns matched pipelines' do
             get_pipelines_index_json(scope: 'branches')
 
-            check_pipeline_response(returned: 2, all: 9, running: 2, pending: 1, finished: 6)
+            check_pipeline_response(returned: 2, all: 9)
           end
         end
 
@@ -148,7 +147,7 @@ RSpec.describe Projects::PipelinesController do
           it 'returns matched pipelines' do
             get_pipelines_index_json(scope: 'tags')
 
-            check_pipeline_response(returned: 1, all: 9, running: 2, pending: 1, finished: 6)
+            check_pipeline_response(returned: 1, all: 9)
           end
         end
       end
@@ -161,7 +160,7 @@ RSpec.describe Projects::PipelinesController do
         it 'returns matched pipelines' do
           get_pipelines_index_json(username: user.username)
 
-          check_pipeline_response(returned: 1, all: 1, running: 1, pending: 0, finished: 0)
+          check_pipeline_response(returned: 1, all: 1)
         end
       end
 
@@ -169,7 +168,7 @@ RSpec.describe Projects::PipelinesController do
         it 'returns empty' do
           get_pipelines_index_json(username: 'invalid-username')
 
-          check_pipeline_response(returned: 0, all: 0, running: 0, pending: 0, finished: 0)
+          check_pipeline_response(returned: 0, all: 0)
         end
       end
     end
@@ -181,7 +180,7 @@ RSpec.describe Projects::PipelinesController do
         it 'returns matched pipelines' do
           get_pipelines_index_json(ref: 'branch-1')
 
-          check_pipeline_response(returned: 1, all: 1, running: 1, pending: 0, finished: 0)
+          check_pipeline_response(returned: 1, all: 1)
         end
       end
 
@@ -189,7 +188,7 @@ RSpec.describe Projects::PipelinesController do
         it 'returns empty list' do
           get_pipelines_index_json(ref: 'invalid-ref')
 
-          check_pipeline_response(returned: 0, all: 0, running: 0, pending: 0, finished: 0)
+          check_pipeline_response(returned: 0, all: 0)
         end
       end
     end
@@ -199,15 +198,7 @@ RSpec.describe Projects::PipelinesController do
         it 'returns matched pipelines' do
           get_pipelines_index_json(status: 'success')
 
-          check_pipeline_response(returned: 1, all: 1, running: 0, pending: 0, finished: 1)
-        end
-
-        context 'when filter by unrelated scope' do
-          it 'returns empty list' do
-            get_pipelines_index_json(status: 'success', scope: 'running')
-
-            check_pipeline_response(returned: 0, all: 1, running: 0, pending: 0, finished: 1)
-          end
+          check_pipeline_response(returned: 1, all: 1)
         end
       end
 
@@ -215,7 +206,7 @@ RSpec.describe Projects::PipelinesController do
         it 'returns empty list' do
           get_pipelines_index_json(status: 'manual')
 
-          check_pipeline_response(returned: 0, all: 0, running: 0, pending: 0, finished: 0)
+          check_pipeline_response(returned: 0, all: 0)
         end
       end
 
@@ -223,11 +214,19 @@ RSpec.describe Projects::PipelinesController do
         it 'returns all list' do
           get_pipelines_index_json(status: 'invalid-status')
 
-          check_pipeline_response(returned: 6, all: 6, running: 2, pending: 1, finished: 3)
+          check_pipeline_response(returned: 6, all: 6)
         end
       end
     end
 
+    def get_pipelines_index_html(params = {})
+      get :index, params: {
+                    namespace_id: project.namespace,
+                    project_id: project
+                  }.merge(params),
+                  format: :html
+    end
+
     def get_pipelines_index_json(params = {})
       get :index, params: {
                     namespace_id: project.namespace,
@@ -284,15 +283,12 @@ RSpec.describe Projects::PipelinesController do
       )
     end
 
-    def check_pipeline_response(returned:, all:, running:, pending:, finished:)
+    def check_pipeline_response(returned:, all:)
       aggregate_failures do
         expect(response).to match_response_schema('pipeline')
 
         expect(json_response['pipelines'].count).to eq returned
         expect(json_response['count']['all'].to_i).to eq all
-        expect(json_response['count']['running'].to_i).to eq running
-        expect(json_response['count']['pending'].to_i).to eq pending
-        expect(json_response['count']['finished'].to_i).to eq finished
       end
     end
   end
diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
index 1f0d6a7378c..5eb24315ca6 100644
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
+++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
@@ -34,6 +34,9 @@ const createTestMr = customConfig => {
     ciStatus: null,
     sha: '12345678',
     squash: false,
+    squashIsEnabledByDefault: false,
+    squashIsReadonly: false,
+    squashIsSelected: false,
     commitMessage,
     squashCommitMessage,
     commitMessageWithDescription,
@@ -694,6 +697,37 @@ describe('ReadyToMerge', () => {
 
         expect(findCheckboxElement().exists()).toBeFalsy();
       });
+
+      describe('squash options', () => {
+        it.each`
+          squashState           | state           | prop            | expectation
+          ${'squashIsReadonly'} | ${'enabled'}    | ${'isDisabled'} | ${false}
+          ${'squashIsSelected'} | ${'selected'}   | ${'value'}      | ${false}
+          ${'squashIsSelected'} | ${'unselected'} | ${'value'}      | ${false}
+        `(
+          'is $state when squashIsReadonly returns $expectation ',
+          ({ squashState, prop, expectation }) => {
+            createLocalComponent({
+              mr: { commitsCount: 2, enableSquashBeforeMerge: true, [squashState]: expectation },
+            });
+
+            expect(findCheckboxElement().props(prop)).toBe(expectation);
+          },
+        );
+
+        it('is not rendered for "Do not allow" option', () => {
+          createLocalComponent({
+            mr: {
+              commitsCount: 2,
+              enableSquashBeforeMerge: true,
+              squashIsReadonly: true,
+              squashIsSelected: false,
+            },
+          });
+
+          expect(findCheckboxElement().exists()).toBe(false);
+        });
+      });
     });
 
     describe('commits count collapsible header', () => {
@@ -709,7 +743,7 @@ describe('ReadyToMerge', () => {
             mr: {
               ffOnlyEnabled: true,
               enableSquashBeforeMerge: true,
-              squash: true,
+              squashIsSelected: true,
               commitsCount: 2,
             },
           });
@@ -803,7 +837,7 @@ describe('ReadyToMerge', () => {
           createLocalComponent({
             mr: {
               ffOnlyEnabled: true,
-              squash: true,
+              squashIsSelected: true,
               enableSquashBeforeMerge: true,
               commitsCount: 2,
             },
@@ -824,7 +858,7 @@ describe('ReadyToMerge', () => {
         createLocalComponent({
           mr: {
             commitsCount: 2,
-            squash: true,
+            squashIsSelected: true,
             enableSquashBeforeMerge: true,
           },
         });
@@ -854,7 +888,7 @@ describe('ReadyToMerge', () => {
         createLocalComponent({
           mr: {
             commitsCount: 2,
-            squash: true,
+            squashIsSelected: true,
             enableSquashBeforeMerge: true,
           },
         });
@@ -872,7 +906,7 @@ describe('ReadyToMerge', () => {
 
       it('should  be rendered if squash is enabled and there is more than 1 commit', () => {
         createLocalComponent({
-          mr: { enableSquashBeforeMerge: true, squash: true, commitsCount: 2 },
+          mr: { enableSquashBeforeMerge: true, squashIsSelected: true, commitsCount: 2 },
         });
 
         expect(findCommitDropdownElement().exists()).toBeTruthy();
diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_squash_before_merge_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_squash_before_merge_spec.js
index b70d580ed04..5f5043285b8 100644
--- a/spec/frontend/vue_mr_widget/components/states/mr_widget_squash_before_merge_spec.js
+++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_squash_before_merge_spec.js
@@ -63,6 +63,27 @@ describe('Squash before merge component', () => {
     });
   });
 
+  describe('label', () => {
+    const findLabel = () => wrapper.find('[data-testid="squashLabel"]');
+
+    describe.each`
+      isDisabled | expectation
+      ${true}    | ${'grays out text if it is true'}
+      ${false}   | ${'does not gray out text if it is false'}
+    `('isDisabled prop', ({ isDisabled, expectation }) => {
+      beforeEach(() => {
+        createComponent({
+          value: false,
+          isDisabled,
+        });
+      });
+
+      it(expectation, () => {
+        expect(findLabel().classes('gl-text-gray-600')).toBe(isDisabled);
+      });
+    });
+  });
+
   describe('about link', () => {
     it('is not rendered if no help path is passed', () => {
       createComponent({
diff --git a/spec/lib/gitlab/background_migration/cleanup_concurrent_schema_change_spec.rb b/spec/lib/gitlab/background_migration/cleanup_concurrent_schema_change_spec.rb
new file mode 100644
index 00000000000..2931b5e6dd3
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/cleanup_concurrent_schema_change_spec.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::CleanupConcurrentSchemaChange do
+  describe '#perform' do
+    it 'new column does not exist' do
+      expect(subject).to receive(:column_exists?).with(:issues, :closed_at_timestamp).and_return(false)
+      expect(subject).not_to receive(:column_exists?).with(:issues, :closed_at)
+      expect(subject).not_to receive(:define_model_for)
+
+      expect(subject.perform(:issues, :closed_at, :closed_at_timestamp)).to be_nil
+    end
+
+    it 'old column does not exist' do
+      expect(subject).to receive(:column_exists?).with(:issues, :closed_at_timestamp).and_return(true)
+      expect(subject).to receive(:column_exists?).with(:issues, :closed_at).and_return(false)
+      expect(subject).not_to receive(:define_model_for)
+
+      expect(subject.perform(:issues, :closed_at, :closed_at_timestamp)).to be_nil
+    end
+
+    it 'has both old and new columns' do
+      expect(subject).to receive(:column_exists?).twice.and_return(true)
+
+      expect { subject.perform('issues', :closed_at, :created_at) }.to raise_error(NotImplementedError)
+    end
+  end
+end
diff --git a/spec/requests/api/import_bitbucket_server_spec.rb b/spec/requests/api/import_bitbucket_server_spec.rb
index be5afdfab61..5828dab3080 100644
--- a/spec/requests/api/import_bitbucket_server_spec.rb
+++ b/spec/requests/api/import_bitbucket_server_spec.rb
@@ -198,6 +198,10 @@ RSpec.describe API::ImportBitbucketServer do
         end
       end
 
+      after do
+        Grape::Endpoint.before_each nil
+      end
+
       it 'raises a connection error' do
         post api("/import/bitbucket_server", user), params: {
           bitbucket_server_url: base_uri,
diff --git a/spec/requests/api/import_github_spec.rb b/spec/requests/api/import_github_spec.rb
index a14c60c0687..f026314f7a8 100644
--- a/spec/requests/api/import_github_spec.rb
+++ b/spec/requests/api/import_github_spec.rb
@@ -26,6 +26,10 @@ RSpec.describe API::ImportGithub do
       end
     end
 
+    after do
+      Grape::Endpoint.before_each nil
+    end
+
     it 'rejects requests when Github Importer is disabled' do
       stub_application_setting(import_sources: nil)