diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index 10fc5d399b73f98a883f1d19166295d70b224e2f..4a105c8015419c3fb4ec85d359e937157142b34b 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -261,7 +261,7 @@ def set_pipeline_path
   end
 
   def latest_pipeline
-    @project.latest_pipeline_for_ref(params['ref'])
+    @project.latest_pipeline(params['ref'])
             &.present(current_user: current_user)
   end
 
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 6174fd139b3aba3be97f81f1b28eaeac2fc9be8f..ccf9f501799f9953c5235de156596776d785086d 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -364,7 +364,7 @@ def merge_pipeline
     # when it is fast-forward there is no merge commit, so we must fall back to
     # either the squash commit (if the MR was squashed) or the diff head commit.
     sha = merge_commit_sha || squash_commit_sha || diff_head_sha
-    target_project.pipeline_for(target_branch, sha)
+    target_project.latest_pipeline(target_branch, sha)
   end
 
   # Pattern used to extract `!123` merge request references from text
diff --git a/app/models/project.rb b/app/models/project.rb
index a40f3b35adde1a68ddb3ac2e8f93514724964fdb..6f0497d36467a453854823f2ec3d0e0e63d90a21 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -956,13 +956,12 @@ def latest_successful_build_for_ref!(job_name, ref = default_branch)
     latest_successful_build_for_ref(job_name, ref) || raise(ActiveRecord::RecordNotFound.new("Couldn't find job #{job_name}"))
   end
 
-  def latest_pipeline_for_ref(ref = default_branch)
+  def latest_pipeline(ref = default_branch, sha = nil)
     ref = ref.presence || default_branch
-    sha = commit(ref)&.sha
-
+    sha ||= commit(ref)&.sha
     return unless sha
 
-    ci_pipelines.newest_first(ref: ref, sha: sha).first
+    ci_pipelines.newest_first(ref: ref, sha: sha).take
   end
 
   def merge_base_commit(first_commit_id, second_commit_id)
@@ -1671,21 +1670,6 @@ def allowed_to_share_with_group?
     !namespace.share_with_group_lock
   end
 
-  def pipeline_for(ref, sha = nil, id = nil)
-    sha ||= commit(ref).try(:sha)
-    return unless sha
-
-    if id.present?
-      pipelines_for(ref, sha).find_by(id: id)
-    else
-      pipelines_for(ref, sha).take
-    end
-  end
-
-  def pipelines_for(ref, sha)
-    ci_pipelines.order(id: :desc).where(sha: sha, ref: ref)
-  end
-
   def latest_successful_pipeline_for_default_branch
     if defined?(@latest_successful_pipeline_for_default_branch)
       return @latest_successful_pipeline_for_default_branch
diff --git a/app/services/issues/related_branches_service.rb b/app/services/issues/related_branches_service.rb
index 46076218857861269e4a5a29de0022778081810a..98d8412102f866d446dfe494194f006939827d82 100644
--- a/app/services/issues/related_branches_service.rb
+++ b/app/services/issues/related_branches_service.rb
@@ -24,7 +24,7 @@ def pipeline_status(branch_name)
 
       return unless target
 
-      pipeline = project.pipeline_for(branch_name, target.sha)
+      pipeline = project.latest_pipeline(branch_name, target.sha)
       pipeline.detailed_status(current_user) if can?(current_user, :read_pipeline, pipeline)
     end
 
diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml
index 1d0ad6dcde6585748d8a033bc0998ed42669eeb6..c04687bd8468b133510945a6b0e26a68480700d7 100644
--- a/app/views/projects/buttons/_download.html.haml
+++ b/app/views/projects/buttons/_download.html.haml
@@ -17,7 +17,7 @@
         %section.border-top.pt-1.mt-1
           %h5.m-0.dropdown-bold-header= _('Download artifacts')
           - unless pipeline.latest?
-            %span.unclickable= ci_status_for_statuseable(project.pipeline_for(ref))
+            %span.unclickable= ci_status_for_statuseable(project.latest_pipeline(ref))
           %h6.m-0.dropdown-header= _('Previous Artifacts')
           %ul
             - pipeline.latest_builds_with_artifacts.each do |job|
diff --git a/ee/app/models/concerns/latest_pipeline_information.rb b/ee/app/models/concerns/latest_pipeline_information.rb
index b009478e33b933d27702ce1712bc82bccdecbb43..ea590576536b9cc19e4e634b502d02e44cf51cf8 100644
--- a/ee/app/models/concerns/latest_pipeline_information.rb
+++ b/ee/app/models/concerns/latest_pipeline_information.rb
@@ -29,7 +29,7 @@ def latest_security_builds
   end
 
   def latest_default_branch_pipeline
-    strong_memoize(:pipeline) { latest_pipeline_for_ref }
+    strong_memoize(:pipeline) { latest_pipeline }
   end
 
   def auto_devops_source?
diff --git a/ee/app/presenters/projects/security/configuration_presenter.rb b/ee/app/presenters/projects/security/configuration_presenter.rb
index 36724b57d0a49e408bab7a36b60dd54825c86b38..97b3837246cc30e435d1e03bbcd2946ebb64cce1 100644
--- a/ee/app/presenters/projects/security/configuration_presenter.rb
+++ b/ee/app/presenters/projects/security/configuration_presenter.rb
@@ -90,7 +90,7 @@ def can_enable_auto_devops?
       end
 
       def gitlab_ci_present?
-        latest_pipeline_for_ref.try(:config_path) == Gitlab::FileDetector::PATTERNS[:gitlab_ci]
+        latest_pipeline.try(:config_path) == Gitlab::FileDetector::PATTERNS[:gitlab_ci]
       end
 
       def features
diff --git a/ee/app/services/ci/subscribe_bridge_service.rb b/ee/app/services/ci/subscribe_bridge_service.rb
index dc756623c7158cc5ee6c8f25f61cabecbd4247d4..a0b0ed46a9aaeb741b9dcff318c715a4d81c9b18 100644
--- a/ee/app/services/ci/subscribe_bridge_service.rb
+++ b/ee/app/services/ci/subscribe_bridge_service.rb
@@ -35,7 +35,7 @@ def upstream_project
 
     def upstream_pipeline
       strong_memoize(:upstream_pipeline) do
-        upstream_project.pipeline_for(upstream_project.default_branch)
+        upstream_project.latest_pipeline(upstream_project.default_branch)
       end
     end
   end
diff --git a/ee/spec/presenters/projects/security/configuration_presenter_spec.rb b/ee/spec/presenters/projects/security/configuration_presenter_spec.rb
index a9b324bd438818b68013cd35601f0ed2439ade89..8ddcd7570366bab97b9a54a08b6d2b018fc6c104 100644
--- a/ee/spec/presenters/projects/security/configuration_presenter_spec.rb
+++ b/ee/spec/presenters/projects/security/configuration_presenter_spec.rb
@@ -190,7 +190,7 @@
         end
 
         it 'expects the gitlab_ci_presence to be false if the file is absent' do
-          allow_any_instance_of(described_class).to receive(:latest_pipeline_for_ref).and_return(nil)
+          allow_any_instance_of(described_class).to receive(:latest_pipeline).and_return(nil)
           expect(subject[:gitlab_ci_present]).to eq(false)
         end
       end
diff --git a/lib/api/ci/pipelines.rb b/lib/api/ci/pipelines.rb
index a010e0dd7615d52e72b857a3a9651cb1afebc36d..55089045e809d1f7a2e984276688141df8d61dc8 100644
--- a/lib/api/ci/pipelines.rb
+++ b/lib/api/ci/pipelines.rb
@@ -178,7 +178,7 @@ def pipeline
 
         def latest_pipeline
           strong_memoize(:latest_pipeline) do
-            user_project.latest_pipeline_for_ref(params[:ref])
+            user_project.latest_pipeline(params[:ref])
           end
         end
       end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index efa2353259cdf83ecc15ff9c0e2b45ef93c1098d..89772a651c2e484f18d2b560e660591e3ff821c0 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1269,60 +1269,6 @@
     end
   end
 
-  describe '#pipeline_for' do
-    let(:project) { create(:project, :repository) }
-
-    shared_examples 'giving the correct pipeline' do
-      it { is_expected.to eq(pipeline) }
-
-      context 'return latest' do
-        let!(:pipeline2) { create_pipeline(project) }
-
-        it { is_expected.to eq(pipeline2) }
-      end
-    end
-
-    context 'with a matching pipeline' do
-      let!(:pipeline) { create_pipeline(project) }
-
-      context 'with explicit sha' do
-        subject { project.pipeline_for('master', pipeline.sha) }
-
-        it_behaves_like 'giving the correct pipeline'
-
-        context 'with supplied id' do
-          let!(:other_pipeline) { create_pipeline(project) }
-
-          subject { project.pipeline_for('master', pipeline.sha, other_pipeline.id) }
-
-          it { is_expected.to eq(other_pipeline) }
-        end
-      end
-
-      context 'with implicit sha' do
-        subject { project.pipeline_for('master') }
-
-        it_behaves_like 'giving the correct pipeline'
-      end
-    end
-
-    context 'when there is no matching pipeline' do
-      subject { project.pipeline_for('master') }
-
-      it { is_expected.to be_nil }
-    end
-  end
-
-  describe '#pipelines_for' do
-    let(:project) { create(:project, :repository) }
-    let!(:pipeline) { create_pipeline(project) }
-    let!(:other_pipeline) { create_pipeline(project) }
-
-    subject { project.pipelines_for(project.default_branch, project.commit.sha) }
-
-    it { is_expected.to contain_exactly(pipeline, other_pipeline) }
-  end
-
   describe '#builds_enabled' do
     let(:project) { create(:project) }
 
@@ -2369,42 +2315,90 @@
     end
   end
 
-  describe '#latest_pipeline_for_ref' do
+  describe '#latest_pipeline' do
     let(:project) { create(:project, :repository) }
     let(:second_branch) { project.repository.branches[2] }
 
     let!(:pipeline_for_default_branch) do
-      create(:ci_empty_pipeline, project: project, sha: project.commit.id,
-                                 ref: project.default_branch)
+      create(:ci_pipeline, project: project, sha: project.commit.id,
+                           ref: project.default_branch)
     end
 
     let!(:pipeline_for_second_branch) do
-      create(:ci_empty_pipeline, project: project, sha: second_branch.target,
-                                 ref: second_branch.name)
+      create(:ci_pipeline, project: project, sha: second_branch.target,
+                           ref: second_branch.name)
     end
 
-    before do
-      create(:ci_empty_pipeline, project: project, sha: project.commit.parent.id,
-                                 ref: project.default_branch)
+    let!(:other_pipeline_for_default_branch) do
+      create(:ci_pipeline, project: project, sha: project.commit.parent.id,
+                           ref: project.default_branch)
     end
 
     context 'default repository branch' do
-      subject { project.latest_pipeline_for_ref(project.default_branch) }
+      context 'when explicitly provided' do
+        subject { project.latest_pipeline(project.default_branch) }
+
+        it { is_expected.to eq(pipeline_for_default_branch) }
+      end
+
+      context 'when not provided' do
+        subject { project.latest_pipeline }
+
+        it { is_expected.to eq(pipeline_for_default_branch) }
+      end
 
-      it { is_expected.to eq(pipeline_for_default_branch) }
+      context 'with provided sha' do
+        subject { project.latest_pipeline(project.default_branch, project.commit.parent.id) }
+
+        it { is_expected.to eq(other_pipeline_for_default_branch) }
+      end
     end
 
     context 'provided ref' do
-      subject { project.latest_pipeline_for_ref(second_branch.name) }
+      subject { project.latest_pipeline(second_branch.name) }
 
       it { is_expected.to eq(pipeline_for_second_branch) }
+
+      context 'with provided sha' do
+        let!(:latest_pipeline_for_ref) do
+          create(:ci_pipeline, project: project, sha: pipeline_for_second_branch.sha,
+                               ref: pipeline_for_second_branch.ref)
+        end
+
+        subject { project.latest_pipeline(second_branch.name, second_branch.target) }
+
+        it { is_expected.to eq(latest_pipeline_for_ref) }
+      end
     end
 
     context 'bad ref' do
-      subject { project.latest_pipeline_for_ref(SecureRandom.uuid) }
+      before do
+        # ensure we don't skip the filter by ref by mistakenly return this pipeline
+        create(:ci_pipeline, project: project)
+      end
+
+      subject { project.latest_pipeline(SecureRandom.uuid) }
 
       it { is_expected.to be_nil }
     end
+
+    context 'on deleted ref' do
+      let(:branch) { project.repository.branches.last }
+
+      let!(:pipeline_on_deleted_ref) do
+        create(:ci_pipeline, project: project, sha: branch.target, ref: branch.name)
+      end
+
+      before do
+        project.repository.rm_branch(project.owner, branch.name)
+      end
+
+      subject { project.latest_pipeline(branch.name) }
+
+      it 'always returns nil despite a pipeline exists' do
+        expect(subject).to be_nil
+      end
+    end
   end
 
   describe '#latest_successful_build_for_sha' do
diff --git a/spec/services/issues/related_branches_service_spec.rb b/spec/services/issues/related_branches_service_spec.rb
index d79132d98db5a506fd00d35a3707c60b17fd89d9..1780023803a63d752449bd9954ca80b70159eb13 100644
--- a/spec/services/issues/related_branches_service_spec.rb
+++ b/spec/services/issues/related_branches_service_spec.rb
@@ -57,7 +57,7 @@ def make_branch
           unreadable_branch_name => unreadable_pipeline
         }.each do |name, pipeline|
           allow(repo).to receive(:find_branch).with(name).and_return(make_branch)
-          allow(project).to receive(:pipeline_for).with(name, sha).and_return(pipeline)
+          allow(project).to receive(:latest_pipeline).with(name, sha).and_return(pipeline)
         end
 
         allow(repo).to receive(:find_branch).with(missing_branch).and_return(nil)