diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 195783454f9c2f157b455787033ed84966f7f7c5..34348247e91c696b127f2f36a7cc711bb3109eda 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -213,11 +213,24 @@ rake downtime_check: *exec
 rake ee_compat_check:
   <<: *exec
   only:
-    - branches
+    - branches@gitlab-org/gitlab-ce
+    - branches@gitlab/gitlabhq
   except:
     - master
     - tags
+    - /^[\d-]+-stable(-ee)?$/
   allow_failure: yes
+  cache:
+    key: "ruby231-ee_compat_check_repo"
+    paths:
+      - ee_compat_check/repo/
+      - vendor/ruby
+  artifacts:
+    name: "${CI_BUILD_NAME}_${CI_BUILD_REF_NAME}_${CI_BUILD_REF}"
+    when: on_failure
+    expire_in: 10d
+    paths:
+      - ee_compat_check/patches/*.patch
 
 rake db:migrate:reset:
   stage: test
diff --git a/lib/gitlab/ee_compat_check.rb b/lib/gitlab/ee_compat_check.rb
index b1a6d5fe0f6715989d25ee8b2b5a0af7d31eb48f..f4d1505ea918ac5fe25629c714981558818155e4 100644
--- a/lib/gitlab/ee_compat_check.rb
+++ b/lib/gitlab/ee_compat_check.rb
@@ -2,39 +2,38 @@
 module Gitlab
   # Checks if a set of migrations requires downtime or not.
   class EeCompatCheck
+    CE_REPO = 'https://gitlab.com/gitlab-org/gitlab-ce.git'.freeze
     EE_REPO = 'https://gitlab.com/gitlab-org/gitlab-ee.git'.freeze
+    CHECK_DIR = Rails.root.join('ee_compat_check')
+    MAX_FETCH_DEPTH = 500
+    IGNORED_FILES_REGEX = /(VERSION|CHANGELOG\.md:\d+)/.freeze
 
-    attr_reader :ce_branch, :check_dir, :ce_repo
+    attr_reader :repo_dir, :patches_dir, :ce_repo, :ce_branch
 
-    def initialize(branch:, check_dir:, ce_repo: nil)
+    def initialize(branch:, ce_repo: CE_REPO)
+      @repo_dir = CHECK_DIR.join('repo')
+      @patches_dir = CHECK_DIR.join('patches')
       @ce_branch = branch
-      @check_dir = check_dir
-      @ce_repo = ce_repo || 'https://gitlab.com/gitlab-org/gitlab-ce.git'
+      @ce_repo = ce_repo
     end
 
     def check
       ensure_ee_repo
-      delete_patches
+      ensure_patches_dir
 
       generate_patch(ce_branch, ce_patch_full_path)
 
-      Dir.chdir(check_dir) do
-        step("In the #{check_dir} directory")
-
-        step("Pulling latest master", %w[git pull --ff-only origin master])
+      Dir.chdir(repo_dir) do
+        step("In the #{repo_dir} directory")
 
         status = catch(:halt_check) do
           ce_branch_compat_check!
-
-          delete_ee_branch_locally
-
+          delete_ee_branch_locally!
           ee_branch_presence_check!
-
           ee_branch_compat_check!
         end
 
-        delete_ee_branch_locally
-        delete_patches
+        delete_ee_branch_locally!
 
         if status.nil?
           true
@@ -47,20 +46,43 @@ module Gitlab
     private
 
     def ensure_ee_repo
-      if Dir.exist?(check_dir)
-        step("#{check_dir} already exists")
+      if Dir.exist?(repo_dir)
+        step("#{repo_dir} already exists")
       else
-        cmd = %W[git clone --branch master --single-branch --depth 1 #{EE_REPO} #{check_dir}]
-        step("Cloning #{EE_REPO} into #{check_dir}", cmd)
+        cmd = %W[git clone --branch master --single-branch --depth 200 #{EE_REPO} #{repo_dir}]
+        step("Cloning #{EE_REPO} into #{repo_dir}", cmd)
       end
     end
 
-    def ce_branch_compat_check!
-      cmd = %W[git apply --check #{ce_patch_full_path}]
-      status = step("Checking if #{ce_patch_name} applies cleanly to EE/master", cmd)
+    def ensure_patches_dir
+      FileUtils.mkdir_p(patches_dir)
+    end
+
+    def generate_patch(branch, patch_path)
+      FileUtils.rm(patch_path, force: true)
+
+      depth = 0
+      loop do
+        depth += 50
+        cmd = %W[git fetch --depth #{depth} origin --prune +refs/heads/master:refs/remotes/origin/master]
+        Gitlab::Popen.popen(cmd)
+        _, status = Gitlab::Popen.popen(%w[git merge-base FETCH_HEAD HEAD])
+
+        raise "#{branch} is too far behind master, please rebase it!" if depth >= MAX_FETCH_DEPTH
+        break if status.zero?
+      end
 
-      if status.zero?
-        puts ce_applies_cleanly_msg(ce_branch)
+      step("Generating the patch against master in #{patch_path}")
+      output, status = Gitlab::Popen.popen(%w[git format-patch FETCH_HEAD --stdout])
+      throw(:halt_check, :ko) unless status.zero?
+
+      File.write(patch_path, output)
+      throw(:halt_check, :ko) unless File.exist?(patch_path)
+    end
+
+    def ce_branch_compat_check!
+      if check_patch(ce_patch_full_path).zero?
+        puts applies_cleanly_msg(ce_branch)
         throw(:halt_check)
       end
     end
@@ -80,10 +102,8 @@ module Gitlab
       step("Checking out origin/#{ee_branch}", %W[git checkout -b #{ee_branch} FETCH_HEAD])
 
       generate_patch(ee_branch, ee_patch_full_path)
-      cmd = %W[git apply --check #{ee_patch_full_path}]
-      status = step("Checking if #{ee_patch_name} applies cleanly to EE/master", cmd)
 
-      unless status.zero?
+      unless check_patch(ee_patch_full_path).zero?
         puts
         puts ee_branch_doesnt_apply_cleanly_msg
 
@@ -91,50 +111,49 @@ module Gitlab
       end
 
       puts
-      puts ee_applies_cleanly_msg
+      puts applies_cleanly_msg(ee_branch)
     end
 
-    def generate_patch(branch, filepath)
-      FileUtils.rm(filepath, force: true)
+    def check_patch(patch_path)
+      step("Checking out master", %w[git checkout master])
+      step("Reseting to latest master", %w[git reset --hard origin/master])
 
-      depth = 0
-      loop do
-        depth += 10
-        step("Fetching origin/master", %W[git fetch origin master --depth=#{depth}])
-        status = step("Finding merge base with master", %W[git merge-base FETCH_HEAD #{branch}])
-
-        break if status.zero? || depth > 500
-      end
+      step("Checking if #{patch_path} applies cleanly to EE/master")
+      output, status = Gitlab::Popen.popen(%W[git apply --check #{patch_path}])
 
-      raise "#{branch} is too far behind master, please rebase it!" if depth > 500
+      unless status.zero?
+        failed_files = output.lines.reduce([]) do |memo, line|
+          if line.start_with?('error: patch failed:')
+            file = line.sub(/\Aerror: patch failed: /, '')
+            memo << file unless file =~ IGNORED_FILES_REGEX
+          end
+          memo
+        end
 
-      step("Generating the patch against master")
-      output, status = Gitlab::Popen.popen(%w[git format-patch FETCH_HEAD --stdout])
-      throw(:halt_check, :ko) unless status.zero?
+        if failed_files.empty?
+          status = 0
+        else
+          puts "\nConflicting files:"
+          failed_files.each do |file|
+            puts "  - #{file}"
+          end
+        end
+      end
 
-      File.write(filepath, output)
-      throw(:halt_check, :ko) unless File.exist?(filepath)
+      status
     end
 
-    def delete_ee_branch_locally
+    def delete_ee_branch_locally!
       command(%w[git checkout master])
       step("Deleting the local #{ee_branch} branch", %W[git branch -D #{ee_branch}])
     end
 
-    def delete_patches
-      step("Deleting #{ce_patch_full_path}")
-      FileUtils.rm(ce_patch_full_path, force: true)
-
-      step("Deleting #{ee_patch_full_path}")
-      FileUtils.rm(ee_patch_full_path, force: true)
-    end
-
     def ce_patch_name
       @ce_patch_name ||= "#{ce_branch}.patch"
     end
 
     def ce_patch_full_path
-      @ce_patch_full_path ||= File.expand_path(ce_patch_name, check_dir)
+      @ce_patch_full_path ||= patches_dir.join(ce_patch_name)
     end
 
     def ee_branch
@@ -146,15 +165,18 @@ module Gitlab
     end
 
     def ee_patch_full_path
-      @ee_patch_full_path ||= File.expand_path(ee_patch_name, check_dir)
+      @ee_patch_full_path ||= patches_dir.join(ee_patch_name)
     end
 
     def step(desc, cmd = nil)
       puts "\n=> #{desc}\n"
 
       if cmd
+        start = Time.now
         puts "\n$ #{cmd.join(' ')}"
-        command(cmd)
+        status = command(cmd)
+        puts "\nFinished in #{Time.now - start} seconds"
+        status
       end
     end
 
@@ -165,12 +187,12 @@ module Gitlab
       status
     end
 
-    def ce_applies_cleanly_msg(ce_branch)
+    def applies_cleanly_msg(branch)
       <<-MSG.strip_heredoc
         =================================================================
         🎉 Congratulations!! 🎉
 
-        The #{ce_branch} branch applies cleanly to EE/master!
+        The #{branch} branch applies cleanly to EE/master!
 
         Much ❤️!!
         =================================================================\n
@@ -211,7 +233,7 @@ module Gitlab
 
           # In the EE repo
           $ git fetch origin
-          $ git checkout -b #{ee_branch} FETCH_HEAD
+          $ git checkout -b #{ee_branch} origin/master
           $ git fetch #{ce_repo} #{ce_branch}
           $ git cherry-pick SHA # Repeat for all the commits you want to pick
 
@@ -245,17 +267,5 @@ module Gitlab
         =================================================================\n
       MSG
     end
-
-    def ee_applies_cleanly_msg
-      <<-MSG.strip_heredoc
-        =================================================================
-        🎉 Congratulations!! 🎉
-
-        The #{ee_branch} branch applies cleanly to EE/master!
-
-        Much ❤️!!
-        =================================================================\n
-      MSG
-    end
   end
 end
diff --git a/lib/tasks/gitlab/dev.rake b/lib/tasks/gitlab/dev.rake
index 5ee99dfc810163f1c86226b9c9d9d87fe9c882c0..3117075b08b06ec7a0a38805cc944d23c3a5fd58 100644
--- a/lib/tasks/gitlab/dev.rake
+++ b/lib/tasks/gitlab/dev.rake
@@ -1,18 +1,22 @@
 namespace :gitlab do
   namespace :dev do
     desc 'Checks if the branch would apply cleanly to EE'
-    task ee_compat_check: :environment do
-      return if defined?(Gitlab::License)
-      return unless ENV['CI']
+    task :ee_compat_check, [:branch] => :environment do |_, args|
+      opts =
+        if ENV['CI']
+          {
+            branch: ENV['CI_BUILD_REF_NAME'],
+            ce_repo: ENV['CI_BUILD_REPO']
+          }
+        else
+          unless args[:branch]
+            puts "Must specify a branch as an argument".color(:red)
+            exit 1
+          end
+          args
+        end
 
-      success =
-        Gitlab::EeCompatCheck.new(
-          branch: ENV['CI_BUILD_REF_NAME'],
-          check_dir: File.expand_path('ee-compat-check', __dir__),
-          ce_repo: ENV['CI_BUILD_REPO']
-        ).check
-
-      if success
+      if Gitlab::EeCompatCheck.new(opts || {}).check
         exit 0
       else
         exit 1