diff --git a/app/controllers/ci/builds_controller.rb b/app/controllers/ci/builds_controller.rb
index 80ee866679266013bc5098fe9e9dba0e82632ed6..bf87f81439a24b4d121d82fb737550e43159c0d8 100644
--- a/app/controllers/ci/builds_controller.rb
+++ b/app/controllers/ci/builds_controller.rb
@@ -18,7 +18,7 @@ module Ci
 
         if commit
           # Redirect to commit page
-          redirect_to ci_project_ref_commit_path(@project, @build.commit.ref, @build.commit.sha)
+          redirect_to ci_project_commit_path(@project, @build.commit)
           return
         end
       end
diff --git a/app/controllers/ci/commits_controller.rb b/app/controllers/ci/commits_controller.rb
index 7a0a500fbe6f4f5455e3076350b16cec1d175cab..887e92f84cfa3c5ce884a235a579c988f1a4bb08 100644
--- a/app/controllers/ci/commits_controller.rb
+++ b/app/controllers/ci/commits_controller.rb
@@ -13,7 +13,7 @@ module Ci
     end
 
     def status
-      commit = Ci::Project.find(params[:project_id]).commits.find_by_sha_and_ref!(params[:id], params[:ref_id])
+      commit = Ci::Project.find(params[:project_id]).commits.find_by_sha!(params[:id])
       render json: commit.to_json(only: [:id, :sha], methods: [:status, :coverage])
     rescue ActiveRecord::RecordNotFound
       render json: { status: "not_found" }
@@ -22,7 +22,7 @@ module Ci
     def cancel
       commit.builds.running_or_pending.each(&:cancel)
 
-      redirect_to ci_project_ref_commits_path(project, commit.ref, commit.sha)
+      redirect_to ci_project_commits_path(project, commit.sha)
     end
 
     private
@@ -32,7 +32,7 @@ module Ci
     end
 
     def commit
-      @commit ||= Ci::Project.find(params[:project_id]).commits.find_by_sha_and_ref!(params[:id], params[:ref_id])
+      @commit ||= Ci::Project.find(params[:project_id]).commits.find_by_sha!(params[:id])
     end
   end
 end
diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb
index e8788955eba72b812f83a946d5bf5b771fce5edc..20e6c2c2ba7736b23b637032a2f5ee435d66abcf 100644
--- a/app/controllers/ci/projects_controller.rb
+++ b/app/controllers/ci/projects_controller.rb
@@ -19,7 +19,8 @@ module Ci
       @ref = params[:ref]
 
       @commits = @project.commits.reverse_order
-      @commits = @commits.where(ref: @ref) if @ref
+      # TODO: this is broken
+      # @commits = @commits.where(ref: @ref) if @ref
       @commits = @commits.page(params[:page]).per(20)
     end
 
diff --git a/app/helpers/builds_helper.rb b/app/helpers/builds_helper.rb
index b6658e52d092c2ae4a6c628e065bd7dafbd69af5..626f4e2f4c0edae34f0839b9c7df24194281ecb2 100644
--- a/app/helpers/builds_helper.rb
+++ b/app/helpers/builds_helper.rb
@@ -3,10 +3,6 @@ module BuildsHelper
     gitlab_ref_link build.project, build.ref
   end
 
-  def build_compare_link build
-    gitlab_compare_link build.project, build.commit.short_before_sha, build.short_sha
-  end
-
   def build_commit_link build
     gitlab_commit_link build.project, build.short_sha
   end
diff --git a/app/helpers/ci/commits_helper.rb b/app/helpers/ci/commits_helper.rb
index 9069aed5b4d3f044d270521115a76784a4e45097..a0df4c3d72d35d599387cb908a1a48c284cb1dab 100644
--- a/app/helpers/ci/commits_helper.rb
+++ b/app/helpers/ci/commits_helper.rb
@@ -1,7 +1,7 @@
 module Ci
   module CommitsHelper
     def ci_commit_path(commit)
-      ci_project_ref_commits_path(commit.project, commit.ref, commit.sha)
+      ci_project_commits_path(commit.project, commit)
     end
 
     def commit_link(commit)
diff --git a/app/helpers/ci/gitlab_helper.rb b/app/helpers/ci/gitlab_helper.rb
index 13e4d0fd9c3b984056ce1a636d33f0dac684b976..baddbc806f2d386d31ec6e85605ba7af0136a2c4 100644
--- a/app/helpers/ci/gitlab_helper.rb
+++ b/app/helpers/ci/gitlab_helper.rb
@@ -26,7 +26,7 @@ module Ci
     def yaml_web_editor_link(project)
       commits = project.commits
 
-      if commits.any? && commits.last.push_data[:ci_yaml_file]
+      if commits.any? && commits.last.ci_yaml_file
         "#{project.gitlab_url}/edit/master/.gitlab-ci.yml"
       else
         "#{project.gitlab_url}/new/master"
diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb
index 3a88ed7107ee509ab519ae453939690c2db63277..794bdc2530ea2f1c11af7edf7137db2d90a28ae8 100644
--- a/app/helpers/ci_status_helper.rb
+++ b/app/helpers/ci_status_helper.rb
@@ -1,6 +1,6 @@
 module CiStatusHelper
   def ci_status_path(ci_commit)
-    ci_project_ref_commits_path(ci_commit.project, ci_commit.ref, ci_commit)
+    ci_project_commits_path(ci_commit.project, ci_commit)
   end
 
   def ci_status_icon(ci_commit)
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 645ad68e1b3d1183cba31fcbd9a615f36d2375a5..f35224916ed20e728593d92c1acc30d382796bb0 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -32,12 +32,14 @@ module Ci
     belongs_to :commit, class_name: 'Ci::Commit'
     belongs_to :runner, class_name: 'Ci::Runner'
     belongs_to :trigger_request, class_name: 'Ci::TriggerRequest'
+    belongs_to :user
 
     serialize :options
 
     validates :commit, presence: true
     validates :status, presence: true
     validates :coverage, numericality: true, allow_blank: true
+    validates_presence_of :ref
 
     scope :running, ->() { where(status: "running") }
     scope :pending, ->() { where(status: "pending") }
@@ -45,6 +47,10 @@ module Ci
     scope :failed, ->() { where(status: "failed")  }
     scope :unstarted, ->() { where(runner_id: nil) }
     scope :running_or_pending, ->() { where(status:[:running, :pending]) }
+    scope :latest, ->() { where(id: unscope(:select).select('max(id)').group(:name)).order(stage_idx: :asc) }
+    scope :ignore_failures, ->() { where(allow_failure: false) }
+    scope :for_ref, ->(ref) { where(ref: ref) }
+    scope :similar, ->(build) { where(ref: build.ref, tag: build.tag, trigger_request_id: build.trigger_request_id) }
 
     acts_as_taggable
 
@@ -75,6 +81,8 @@ module Ci
 
       def retry(build)
         new_build = Ci::Build.new(status: :pending)
+        new_build.ref = build.ref
+        new_build.tag = build.tag
         new_build.options = build.options
         new_build.commands = build.commands
         new_build.tag_list = build.tag_list
@@ -82,6 +90,7 @@ module Ci
         new_build.name = build.name
         new_build.allow_failure = build.allow_failure
         new_build.stage = build.stage
+        new_build.stage_idx = build.stage_idx
         new_build.trigger_request = build.trigger_request
         new_build.save
         new_build
@@ -117,8 +126,8 @@ module Ci
           Ci::WebHookService.new.build_end(build)
         end
 
-        if build.commit.success?
-          build.commit.create_next_builds(build.trigger_request)
+        if build.commit.should_create_next_builds?(build)
+          build.commit.create_next_builds(build.ref, build.tag, build.user, build.trigger_request)
         end
 
         project.execute_services(build)
@@ -135,9 +144,13 @@ module Ci
       state :canceled, value: 'canceled'
     end
 
-    delegate :sha, :short_sha, :before_sha, :ref, :project,
+    delegate :sha, :short_sha, :project,
       to: :commit, prefix: false
 
+    def before_sha
+      Gitlab::Git::BLANK_SHA
+    end
+
     def trace_html
       html = Ci::Ansi2html::convert(trace) if trace.present?
       html || ''
@@ -187,6 +200,16 @@ module Ci
       project.name
     end
 
+    def project_recipients
+      recipients = project.email_recipients.split(' ')
+
+      if project.email_add_pusher? && user.present? && user.notification_email.present?
+        recipients << user.notification_email
+      end
+
+      recipients.uniq
+    end
+
     def repo_url
       project.repo_url_with_auth
     end
diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb
index 6d048779cde78099b8a420e5e57acb51d18c5d26..46370034f9a30933988b20c0f441051a4b6dcd80 100644
--- a/app/models/ci/commit.rb
+++ b/app/models/ci/commit.rb
@@ -23,9 +23,7 @@ module Ci
     has_many :builds, dependent: :destroy, class_name: 'Ci::Build'
     has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest'
 
-    serialize :push_data
-
-    validates_presence_of :ref, :sha, :before_sha, :push_data
+    validates_presence_of :sha
     validate :valid_commit_sha
 
     def self.truncate_sha(sha)
@@ -60,28 +58,16 @@ module Ci
       end
     end
 
-    def new_branch?
-      before_sha == Ci::Git::BLANK_SHA
-    end
-
-    def compare?
-      !new_branch?
-    end
-
     def git_author_name
-      commit_data[:author][:name] if commit_data && commit_data[:author]
+      commit_data.author_name if commit_data
     end
 
     def git_author_email
-      commit_data[:author][:email] if commit_data && commit_data[:author]
+      commit_data.author_email if commit_data
     end
 
     def git_commit_message
-      commit_data[:message] if commit_data && commit_data[:message]
-    end
-
-    def short_before_sha
-      Ci::Commit.truncate_sha(before_sha)
+      commit_data.message if commit_data
     end
 
     def short_sha
@@ -89,84 +75,51 @@ module Ci
     end
 
     def commit_data
-      push_data[:commits].find do |commit|
-        commit[:id] == sha
-      end
+      @commit ||= gl_project.commit(sha)
     rescue
       nil
     end
 
-    def project_recipients
-      recipients = project.email_recipients.split(' ')
-
-      if project.email_add_pusher? && push_data[:user_email].present?
-        recipients << push_data[:user_email]
-      end
-
-      recipients.uniq
-    end
-
     def stage
-      return unless config_processor
-      stages = builds_without_retry.select(&:active?).map(&:stage)
-      config_processor.stages.find { |stage| stages.include? stage }
+      running_or_pending = builds_without_retry.running_or_pending
+      running_or_pending.limit(1).pluck(:stage).first
     end
 
-    def create_builds_for_stage(stage, trigger_request)
+    def create_builds(ref, tag, user, trigger_request = nil)
       return if skip_ci? && trigger_request.blank?
       return unless config_processor
-
-      builds_attrs = config_processor.builds_for_stage_and_ref(stage, ref, tag)
-      builds_attrs.map do |build_attrs|
-        builds.create!({
-                         name: build_attrs[:name],
-                         commands: build_attrs[:script],
-                         tag_list: build_attrs[:tags],
-                         options: build_attrs[:options],
-                         allow_failure: build_attrs[:allow_failure],
-                         stage: build_attrs[:stage],
-                         trigger_request: trigger_request,
-                       })
+      config_processor.stages.any? do |stage|
+        CreateBuildsService.new.execute(self, stage, ref, tag, user, trigger_request).present?
       end
     end
 
-    def create_next_builds(trigger_request)
+    def create_next_builds(ref, tag, user, trigger_request)
       return if skip_ci? && trigger_request.blank?
       return unless config_processor
 
-      stages = builds.where(trigger_request: trigger_request).group_by(&:stage)
+      stages = builds.where(ref: ref, tag: tag, trigger_request: trigger_request).group_by(&:stage)
 
       config_processor.stages.any? do |stage|
-        !stages.include?(stage) && create_builds_for_stage(stage, trigger_request).present?
+        unless stages.include?(stage)
+          CreateBuildsService.new.execute(self, stage, ref, tag, user, trigger_request).present?
+        end
       end
     end
 
-    def create_builds(trigger_request = nil)
-      return if skip_ci? && trigger_request.blank?
-      return unless config_processor
+    def refs
+      builds.group(:ref).pluck(:ref)
+    end
 
-      config_processor.stages.any? do |stage|
-        create_builds_for_stage(stage, trigger_request).present?
-      end
+    def last_ref
+      builds.latest.first.try(:ref)
     end
 
     def builds_without_retry
-      @builds_without_retry ||=
-        begin
-          grouped_builds = builds.group_by(&:name)
-          grouped_builds.map do |name, builds|
-            builds.sort_by(&:id).last
-          end
-        end
+      builds.latest
     end
 
-    def builds_without_retry_sorted
-      return builds_without_retry unless config_processor
-
-      stages = config_processor.stages
-      builds_without_retry.sort_by do |build|
-        [stages.index(build.stage) || -1, build.name || ""]
-      end
+    def builds_without_retry_for_ref(ref)
+      builds.for_ref(ref).latest
     end
 
     def retried_builds
@@ -225,6 +178,10 @@ module Ci
       @duration ||= builds_without_retry.select(&:duration).sum(&:duration).to_i
     end
 
+    def duration_for_ref(ref)
+      builds_without_retry_for_ref(ref).select(&:duration).sum(&:duration).to_i
+    end
+
     def finished_at
       @finished_at ||= builds.order('finished_at DESC').first.try(:finished_at)
     end
@@ -238,12 +195,12 @@ module Ci
       end
     end
 
-    def matrix?
-      builds_without_retry.size > 1
+    def matrix_for_ref?(ref)
+      builds_without_retry_for_ref(ref).pluck(:id).size > 1
     end
 
     def config_processor
-      @config_processor ||= Ci::GitlabCiYamlProcessor.new(push_data[:ci_yaml_file])
+      @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file)
     rescue Ci::GitlabCiYamlProcessor::ValidationError => e
       save_yaml_error(e.message)
       nil
@@ -253,16 +210,31 @@ module Ci
       nil
     end
 
+    def ci_yaml_file
+      gl_project.repository.blob_at(sha, '.gitlab-ci.yml')
+    rescue
+      nil
+    end
+
     def skip_ci?
       return false if builds.any?
-      commits = push_data[:commits]
-      commits.present? && commits.last[:message] =~ /(\[ci skip\])/
+      git_commit_message =~ /(\[ci skip\])/ if git_commit_message
     end
 
     def update_committed!
       update!(committed_at: DateTime.now)
     end
 
+    def should_create_next_builds?(build)
+      # don't create other builds if this one is retried
+      other_builds = builds.similar(build).latest
+      return false unless other_builds.include?(build)
+
+      other_builds.all? do |build|
+        build.success? || build.ignored?
+      end
+    end
+
     private
 
     def save_yaml_error(error)
diff --git a/app/models/ci/project_status.rb b/app/models/ci/project_status.rb
index 6d5cafe81a2f1a44a04f88274c73c7207206b5f5..b66f1212f239da967a16480ac2fe9b5d428edbc9 100644
--- a/app/models/ci/project_status.rb
+++ b/app/models/ci/project_status.rb
@@ -28,18 +28,6 @@ module Ci
       status
     end
 
-    # only check for toggling build status within same ref.
-    def last_commit_changed_status?
-      ref = last_commit.ref
-      last_commits = commits.where(ref: ref).last(2)
-
-      if last_commits.size < 2
-        false
-      else
-        last_commits[0].status != last_commits[1].status
-      end
-    end
-
     def last_commit_for_ref(ref)
       commits.where(ref: ref).last
     end
diff --git a/app/models/project.rb b/app/models/project.rb
index b90a82da9f231059a78f7e8a2f4dab54d05f816c..bb47b9abb03907a356cc86ad3d199bb0e364e510 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -744,7 +744,11 @@ class Project < ActiveRecord::Base
   end
 
   def ci_commit(sha)
-    gitlab_ci_project.commits.find_by(sha: sha) if gitlab_ci?
+    ci_commits.find_by(sha: sha)
+  end
+
+  def ensure_ci_commit(sha)
+    ci_commit(sha) || ci_commits.create(sha: sha)
   end
 
   def ensure_gitlab_ci_project
diff --git a/app/models/project_services/ci/hip_chat_message.rb b/app/models/project_services/ci/hip_chat_message.rb
index 25c72033eacafd948e84dbb77ccfdc0306fa24eb..0bf448d47f27f27c37c8b17ddab8dc2e8498f8cd 100644
--- a/app/models/project_services/ci/hip_chat_message.rb
+++ b/app/models/project_services/ci/hip_chat_message.rb
@@ -11,14 +11,7 @@ module Ci
     def to_s
       lines = Array.new
       lines.push("<a href=\"#{ci_project_url(project)}\">#{project.name}</a> - ")
-      
-      if commit.matrix?
-        lines.push("<a href=\"#{ci_project_ref_commits_url(project, commit.ref, commit.sha)}\">Commit ##{commit.id}</a></br>")
-      else
-        first_build = commit.builds_without_retry.first
-        lines.push("<a href=\"#{ci_project_build_url(project, first_build)}\">Build '#{first_build.name}' ##{first_build.id}</a></br>")
-      end
-      
+      lines.push("<a href=\"#{ci_project_commits_url(project, commit.sha)}\">Commit ##{commit.id}</a></br>")
       lines.push("#{commit.short_sha} #{commit.git_author_name} - #{commit.git_commit_message}</br>")
       lines.push("#{humanized_status(commit_status)} in #{commit.duration} second(s).")
       lines.join('')
diff --git a/app/models/project_services/ci/mail_service.rb b/app/models/project_services/ci/mail_service.rb
index 1bd2f33612b5cb196618c2a909e3e0b1c1ae080a..11a2743f9693a6b41ed3aed7bb33e19e2c8201d0 100644
--- a/app/models/project_services/ci/mail_service.rb
+++ b/app/models/project_services/ci/mail_service.rb
@@ -61,7 +61,7 @@ module Ci
     end
 
     def execute(build)
-      build.commit.project_recipients.each do |recipient|
+      build.project_recipients.each do |recipient|
         case build.status.to_sym
         when :success
           mailer.build_success_email(build.id, recipient)
diff --git a/app/models/project_services/ci/slack_message.rb b/app/models/project_services/ci/slack_message.rb
index 757b1961143f846f8bedc453ac8dad58814caf1f..a89c01517b73e5b2be4d2962c46457627a62674b 100644
--- a/app/models/project_services/ci/slack_message.rb
+++ b/app/models/project_services/ci/slack_message.rb
@@ -23,15 +23,13 @@ module Ci
     def attachments
       fields = []
 
-      if commit.matrix?
-        commit.builds_without_retry.each do |build|
-          next if build.allow_failure?
-          next unless build.failed?
-          fields << {
-            title: build.name,
-            value: "Build <#{ci_project_build_url(project, build)}|\##{build.id}> failed in #{build.duration.to_i} second(s)."
-          }
-        end
+      commit.builds_without_retry.each do |build|
+        next if build.allow_failure?
+        next unless build.failed?
+        fields << {
+          title: build.name,
+          value: "Build <#{ci_project_build_url(project, build)}|\##{build.id}> failed in #{build.duration.to_i} second(s)."
+        }
       end
 
       [{
@@ -47,12 +45,7 @@ module Ci
 
     def attachment_message
       out = "<#{ci_project_url(project)}|#{project_name}>: "
-      if commit.matrix?
-        out << "Commit <#{ci_project_ref_commits_url(project, commit.ref, commit.sha)}|\##{commit.id}> "
-      else
-        build = commit.builds_without_retry.first
-        out << "Build <#{ci_project_build_url(project, build)}|\##{build.id}> "
-      end
+      out << "Commit <#{ci_project_commits_url(project, commit.sha)}|\##{commit.id}> "
       out << "(<#{commit_sha_link}|#{commit.short_sha}>) "
       out << "of <#{commit_ref_link}|#{commit.ref}> "
       out << "by #{commit.git_author_name} " if commit.git_author_name
diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb
index 6d2cf79b691ce0baed3351bb5bd765597911b677..b63a75cf3afb1a0d61f25bba43d7c197dba20320 100644
--- a/app/models/project_services/gitlab_ci_service.rb
+++ b/app/models/project_services/gitlab_ci_service.rb
@@ -40,19 +40,10 @@ class GitlabCiService < CiService
   def execute(data)
     return unless supported_events.include?(data[:object_kind])
 
-    sha = data[:checkout_sha]
-
-    if sha.present?
-      file = ci_yaml_file(sha)
-
-      if file && file.data
-        data.merge!(ci_yaml_file: file.data)
-      end
-    end
-
-    ci_project = Ci::Project.find_by(gitlab_id: project.id)
+    ci_project = project.gitlab_ci_project
     if ci_project
-      Ci::CreateCommitService.new.execute(ci_project, data)
+      current_user = User.find_by(id: data[:user_id])
+      Ci::CreateCommitService.new.execute(ci_project, current_user, data)
     end
   end
 
@@ -63,7 +54,7 @@ class GitlabCiService < CiService
   end
 
   def get_ci_commit(sha, ref)
-    Ci::Project.find(project.gitlab_ci_project).commits.find_by_sha_and_ref!(sha, ref)
+    Ci::Project.find(project.gitlab_ci_project).commits.find_by_sha!(sha)
   end
 
   def commit_status(sha, ref)
@@ -80,7 +71,7 @@ class GitlabCiService < CiService
 
   def build_page(sha, ref)
     if project.gitlab_ci_project.present?
-      ci_project_ref_commits_url(project.gitlab_ci_project, ref, sha)
+      ci_project_commits_url(project.gitlab_ci_project, sha)
     end
   end
 
@@ -99,14 +90,4 @@ class GitlabCiService < CiService
   def fields
     []
   end
-
-  private
-
-  def ci_yaml_file(sha)
-    repository.blob_at(sha, '.gitlab-ci.yml')
-  end
-
-  def repository
-    project.repository
-  end
 end
diff --git a/app/models/user.rb b/app/models/user.rb
index 1069f8e36640212372f9237ea8d1918b0cf767ff..c7e3992b6a166fece09544fd9a3934c57f702503 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -130,6 +130,7 @@ class User < ActiveRecord::Base
   has_many :assigned_merge_requests,  dependent: :destroy, foreign_key: :assignee_id, class_name: "MergeRequest"
   has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy
   has_one  :abuse_report,             dependent: :destroy
+  has_many :ci_builds,                dependent: :nullify, class_name: 'Ci::Build'
 
 
   #
diff --git a/app/services/ci/create_builds_service.rb b/app/services/ci/create_builds_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c420f3268fd322444d173c0abb26882800ba573e
--- /dev/null
+++ b/app/services/ci/create_builds_service.rb
@@ -0,0 +1,27 @@
+module Ci
+  class CreateBuildsService
+    def execute(commit, stage, ref, tag, user, trigger_request)
+      builds_attrs = commit.config_processor.builds_for_stage_and_ref(stage, ref, tag)
+
+      builds_attrs.map do |build_attrs|
+        # don't create the same build twice
+        unless commit.builds.find_by(ref: ref, tag: tag, trigger_request: trigger_request, name: build_attrs[:name])
+          build_attrs.slice!(:name,
+                             :commands,
+                             :tag_list,
+                             :options,
+                             :allow_failure,
+                             :stage,
+                             :stage_idx)
+
+          build_attrs.merge!(ref: ref,
+                             tag: tag,
+                             trigger_request: trigger_request,
+                             user: user)
+
+          commit.builds.create!(build_attrs)
+        end
+      end
+    end
+  end
+end
diff --git a/app/services/ci/create_commit_service.rb b/app/services/ci/create_commit_service.rb
index 0a1abf89a95b14786bd03e10d6715fdf0f9de65a..fc1ae5774d561b6c96b8cd3f4f3bceaec2bfd997 100644
--- a/app/services/ci/create_commit_service.rb
+++ b/app/services/ci/create_commit_service.rb
@@ -1,7 +1,6 @@
 module Ci
   class CreateCommitService
-    def execute(project, params)
-      before_sha = params[:before]
+    def execute(project, user, params)
       sha = params[:checkout_sha] || params[:after]
       origin_ref = params[:ref]
       
@@ -16,33 +15,10 @@ module Ci
         return false
       end
 
-      commit = project.commits.find_by_sha_and_ref(sha, ref)
-
-      # Create commit if not exists yet
-      unless commit
-        data = {
-          ref: ref,
-          sha: sha,
-          tag: origin_ref.start_with?('refs/tags/'),
-          before_sha: before_sha,
-          push_data: {
-            before: before_sha,
-            after: sha,
-            ref: ref,
-            user_name: params[:user_name],
-            user_email: params[:user_email],
-            repository: params[:repository],
-            commits: params[:commits],
-            total_commits_count: params[:total_commits_count],
-            ci_yaml_file: params[:ci_yaml_file]
-          }
-        }
-
-        commit = project.commits.create(data)
-      end
-
+      tag = origin_ref.start_with?('refs/tags/')
+      commit = project.gl_project.ensure_ci_commit(sha)
       commit.update_committed!
-      commit.create_builds unless commit.builds.any?
+      commit.create_builds(ref, tag, user)
 
       commit
     end
diff --git a/app/services/ci/create_trigger_request_service.rb b/app/services/ci/create_trigger_request_service.rb
index 9bad09f2f54b35fd4ff1d6b3684621040d4f2e7b..4b86cb0a1f54edb4a6a5f7e235c0c2e728cece39 100644
--- a/app/services/ci/create_trigger_request_service.rb
+++ b/app/services/ci/create_trigger_request_service.rb
@@ -1,15 +1,20 @@
 module Ci
   class CreateTriggerRequestService
     def execute(project, trigger, ref, variables = nil)
-      commit = project.commits.where(ref: ref).last
+      commit = project.gl_project.commit(ref)
       return unless commit
 
+      # check if ref is tag
+      tag = project.gl_project.repository.find_tag(ref).present?
+
+      ci_commit = project.gl_project.ensure_ci_commit(commit.sha)
+
       trigger_request = trigger.trigger_requests.create!(
-        commit: commit,
-        variables: variables
+        variables: variables,
+        commit: ci_commit,
       )
 
-      if commit.create_builds(trigger_request)
+      if ci_commit.create_builds(ref, tag, nil, trigger_request)
         trigger_request
       end
     end
diff --git a/app/services/ci/web_hook_service.rb b/app/services/ci/web_hook_service.rb
index 87984b20fa16c747bb27fffd0e1b1a8c80ece883..92e6df442b4f3f9ee929077a254c81684ed3571c 100644
--- a/app/services/ci/web_hook_service.rb
+++ b/app/services/ci/web_hook_service.rb
@@ -27,9 +27,8 @@ module Ci
         project_name: project.name,
         gitlab_url: project.gitlab_url,
         ref: build.ref,
-        sha: build.sha,
         before_sha: build.before_sha,
-        push_data: build.commit.push_data
+        sha: build.sha,
       })
     end
   end
diff --git a/app/views/ci/builds/_build.html.haml b/app/views/ci/builds/_build.html.haml
index 515b862e992c11f95d7ea07a2e71691234d95fa1..8ccc0dff2fbf49ae110a42f250ae3d2530ad13cb 100644
--- a/app/views/ci/builds/_build.html.haml
+++ b/app/views/ci/builds/_build.html.haml
@@ -6,6 +6,10 @@
     = link_to ci_project_build_path(build.project, build) do
       %strong Build ##{build.id}
 
+  - if defined?(ref)
+    %td
+      = build.ref
+
   %td
     = build.stage
 
diff --git a/app/views/ci/builds/show.html.haml b/app/views/ci/builds/show.html.haml
index 839dbf5c55444f7772dea4340c118db4e4f1e95f..c42d11bf05deaa85d25ca212126f5815ef0f84e8 100644
--- a/app/views/ci/builds/show.html.haml
+++ b/app/views/ci/builds/show.html.haml
@@ -1,7 +1,7 @@
 #up-build-trace
-- if @commit.matrix?
+- if @commit.matrix_for_ref?(@build.ref)
   %ul.center-top-menu
-    - @commit.builds_without_retry_sorted.each do |build|
+    - @commit.builds_without_retry_for_ref(build.ref).each do |build|
       %li{class: ('active' if build == @build) }
         = link_to ci_project_build_url(@project, build) do
           = ci_icon_for_status(build.status)
@@ -12,7 +12,7 @@
               = build.id
 
 
-    - unless @commit.builds_without_retry.include?(@build)
+    - unless @commit.builds_without_retry_for_ref(@build.ref).include?(@build)
       %li.active
         %a
           Build ##{@build.id}
@@ -122,11 +122,6 @@
         Commit
         .pull-right
           %small #{build_commit_link @build}
-
-      - if @build.commit.compare?
-        %p
-          %span.attr-name Compare:
-          #{build_compare_link @build}
       %p
         %span.attr-name Branch:
         #{build_ref_link @build}
diff --git a/app/views/ci/commits/_commit.html.haml b/app/views/ci/commits/_commit.html.haml
index 1eacfca944fce44254928c5d5a87b5fa558ad83e..f8a1fa508517c35dec3896f31030d2a0e9e2180c 100644
--- a/app/views/ci/commits/_commit.html.haml
+++ b/app/views/ci/commits/_commit.html.haml
@@ -7,7 +7,7 @@
 
 
   %td.build-link
-    = link_to ci_project_ref_commits_path(commit.project, commit.ref, commit.sha) do
+    = link_to ci_project_commits_path(commit.project, commit.sha) do
       %strong #{commit.short_sha}
 
   %td.build-message
@@ -16,7 +16,7 @@
   %td.build-branch
     - unless @ref
       %span
-        = link_to truncate(commit.ref, length: 25), ci_project_path(@project, ref: commit.ref)
+        = link_to truncate(commit.last_ref, length: 25), ci_project_path(@project, ref: commit.last_ref)
 
   %td.duration
     - if commit.duration > 0
diff --git a/app/views/ci/commits/show.html.haml b/app/views/ci/commits/show.html.haml
index 8f38aa84676a6c39b0ef05aa2418de2d19958648..7217671fe95351b33cef489d2d58e13644c7113a 100644
--- a/app/views/ci/commits/show.html.haml
+++ b/app/views/ci/commits/show.html.haml
@@ -9,22 +9,14 @@
   .gray-content-block.second-block
     .row
       .col-sm-6
-        - if @commit.compare?
-          %p
-            %span.attr-name Compare:
-            #{gitlab_compare_link(@project, @commit.short_before_sha, @commit.short_sha)}
-        - else
-          %p
-            %span.attr-name Commit:
-            #{gitlab_commit_link(@project, @commit.sha)}
-
-        %p
-          %span.attr-name Branch:
-          #{gitlab_ref_link(@project, @commit.ref)}
+      %p
+        %span.attr-name Commit:
+        #{gitlab_commit_link(@project, @commit.sha)}
       .col-sm-6
-        %p
-          %span.attr-name Author:
-          #{@commit.git_author_name} (#{@commit.git_author_email})
+        - if @commit.git_author_name || @commit.git_author_email
+          %p
+            %span.attr-name Author:
+            #{@commit.git_author_name} (#{@commit.git_author_email})
         - if @commit.created_at
           %p
             %span.attr-name Created at:
@@ -33,7 +25,7 @@
 - if current_user && can?(current_user, :manage_builds, gl_project)
   .pull-right
     - if @commit.builds.running_or_pending.any?
-      = link_to "Cancel", cancel_ci_project_ref_commits_path(@project, @commit.ref, @commit.sha), class: 'btn btn-sm btn-danger'
+      = link_to "Cancel", cancel_ci_project_commits_path(@project, @commit), class: 'btn btn-sm btn-danger'
 
 
 - if @commit.yaml_errors.present?
@@ -43,30 +35,31 @@
       - @commit.yaml_errors.split(",").each do |error|
         %li= error
 
-- unless @commit.push_data[:ci_yaml_file]
+- unless @commit.ci_yaml_file
   .bs-callout.bs-callout-warning
     \.gitlab-ci.yml not found in this commit
 
-%h3
-  Builds
-  - if @commit.duration > 0
-    %small.pull-right
-      %i.fa.fa-time
-      #{time_interval_in_words @commit.duration}
+- @commit.refs.each do |ref|
+  %h3
+    Builds for #{ref}
+    - if @commit.duration_for_ref(ref) > 0
+      %small.pull-right
+        %i.fa.fa-time
+        #{time_interval_in_words @commit.duration_for_ref(ref)}
 
-%table.table.builds
-  %thead
-    %tr
-      %th Status
-      %th Build ID
-      %th Stage
-      %th Name
-      %th Duration
-      %th Finished at
-      - if @project.coverage_enabled?
-        %th Coverage
-      %th
-  = render @commit.builds_without_retry_sorted, controls: true
+  %table.table.builds
+    %thead
+      %tr
+        %th Status
+        %th Build ID
+        %th Stage
+        %th Name
+        %th Duration
+        %th Finished at
+        - if @project.coverage_enabled?
+          %th Coverage
+        %th
+    = render @commit.builds_without_retry.for_ref(ref), controls: true
 
 - if @commit.retried_builds.any?
   %h3
@@ -77,6 +70,7 @@
       %tr
         %th Status
         %th Build ID
+        %th Ref
         %th Stage
         %th Name
         %th Duration
@@ -84,4 +78,4 @@
         - if @project.coverage_enabled?
           %th Coverage
         %th
-    = render @commit.retried_builds
+    = render @commit.retried_builds, ref: true
diff --git a/app/views/ci/notify/build_fail_email.html.haml b/app/views/ci/notify/build_fail_email.html.haml
index d818e8b6756aaca8c8100ce8752d48c053c76b00..4ebdfa1b6c088d884f62efef3dcc7422facb4478 100644
--- a/app/views/ci/notify/build_fail_email.html.haml
+++ b/app/views/ci/notify/build_fail_email.html.haml
@@ -11,7 +11,7 @@
 %p
   Author: #{@build.commit.git_author_name}
 %p
-  Branch: #{@build.commit.ref}
+  Branch: #{@build.ref}
 %p
   Message: #{@build.commit.git_commit_message}
 
diff --git a/app/views/ci/notify/build_fail_email.text.erb b/app/views/ci/notify/build_fail_email.text.erb
index 1add215a1c8ab5c0ffda424632ebd2ec6f48ed0c..177827f9a3c1c2aa16874f6a840c598aa6c1a685 100644
--- a/app/views/ci/notify/build_fail_email.text.erb
+++ b/app/views/ci/notify/build_fail_email.text.erb
@@ -3,7 +3,7 @@ Build failed for <%= @project.name %>
 Status:   <%= @build.status %>
 Commit:   <%= @build.commit.short_sha %>
 Author:   <%= @build.commit.git_author_name %>
-Branch:   <%= @build.commit.ref %>
+Branch:   <%= @build.ref %>
 Message:  <%= @build.commit.git_commit_message %>
 
 Url:      <%= ci_project_build_url(@build.project, @build) %>
diff --git a/app/views/ci/notify/build_success_email.html.haml b/app/views/ci/notify/build_success_email.html.haml
index a20dcaee24e4f0d7f437e047d0188907741bdc01..7cc43300e883e4a45986a526db88af6b9c1a6922 100644
--- a/app/views/ci/notify/build_success_email.html.haml
+++ b/app/views/ci/notify/build_success_email.html.haml
@@ -12,7 +12,7 @@
 %p
   Author: #{@build.commit.git_author_name}
 %p
-  Branch: #{@build.commit.ref}
+  Branch: #{@build.ref}
 %p
   Message: #{@build.commit.git_commit_message}
 
diff --git a/app/views/ci/notify/build_success_email.text.erb b/app/views/ci/notify/build_success_email.text.erb
index 7ebd17e7270495bc56b06f3d2c2c99692dfc3af0..4d55c39b0fb7c6e5e5c3481949cf85de57125ec0 100644
--- a/app/views/ci/notify/build_success_email.text.erb
+++ b/app/views/ci/notify/build_success_email.text.erb
@@ -3,7 +3,7 @@ Build successful for <%= @project.name %>
 Status:   <%= @build.status %>
 Commit:   <%= @build.commit.short_sha %>
 Author:   <%= @build.commit.git_author_name %>
-Branch:   <%= @build.commit.ref %>
+Branch:   <%= @build.ref %>
 Message:  <%= @build.commit.git_commit_message %>
 
 Url:      <%= ci_project_build_url(@build.project, @build) %>
diff --git a/config/routes.rb b/config/routes.rb
index 7bda7ade817307cbf810daec5d89787fc1b55e13..a21889631ed7a2d09b62ab9d4fdcda9b1a35ab64 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -30,12 +30,10 @@ Gitlab::Application.routes.draw do
 
       resource :charts, only: [:show]
 
-      resources :refs, constraints: { ref_id: /.*/ }, only: [] do
-        resources :commits, only: [:show] do
-          member do
-            get :status
-            get :cancel
-          end
+      resources :commits, only: [:show] do
+        member do
+          get :status
+          get :cancel
         end
       end
 
diff --git a/db/migrate/20151002112914_add_stage_idx_to_builds.rb b/db/migrate/20151002112914_add_stage_idx_to_builds.rb
new file mode 100644
index 0000000000000000000000000000000000000000..68a745ffef49d6c1839964560013d6d274ff9434
--- /dev/null
+++ b/db/migrate/20151002112914_add_stage_idx_to_builds.rb
@@ -0,0 +1,5 @@
+class AddStageIdxToBuilds < ActiveRecord::Migration
+  def change
+    add_column :ci_builds, :stage_idx, :integer
+  end
+end
diff --git a/db/migrate/20151002121400_add_index_for_builds.rb b/db/migrate/20151002121400_add_index_for_builds.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4ffc1363910dab6f9e4443d06d2b1146d5f18136
--- /dev/null
+++ b/db/migrate/20151002121400_add_index_for_builds.rb
@@ -0,0 +1,5 @@
+class AddIndexForBuilds < ActiveRecord::Migration
+  def up
+    add_index :ci_builds, [:commit_id, :stage_idx, :created_at]
+  end
+end
diff --git a/db/migrate/20151002122929_add_ref_and_tag_to_builds.rb b/db/migrate/20151002122929_add_ref_and_tag_to_builds.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e3d2ac1cea5cc43e34d52e07a1a055b5dbc20693
--- /dev/null
+++ b/db/migrate/20151002122929_add_ref_and_tag_to_builds.rb
@@ -0,0 +1,6 @@
+class AddRefAndTagToBuilds < ActiveRecord::Migration
+  def change
+    add_column :ci_builds, :tag, :boolean
+    add_column :ci_builds, :ref, :string
+  end
+end
diff --git a/db/migrate/20151002122943_migrate_ref_and_tag_to_build.rb b/db/migrate/20151002122943_migrate_ref_and_tag_to_build.rb
new file mode 100644
index 0000000000000000000000000000000000000000..01d7b3f6773822dcbae7116c76d0c3ff90acceb6
--- /dev/null
+++ b/db/migrate/20151002122943_migrate_ref_and_tag_to_build.rb
@@ -0,0 +1,6 @@
+class MigrateRefAndTagToBuild < ActiveRecord::Migration
+  def change
+    execute('UPDATE ci_builds SET ref=(SELECT ref FROM ci_commits WHERE ci_commits.id = ci_builds.commit_id) WHERE ref IS NULL')
+    execute('UPDATE ci_builds SET tag=(SELECT tag FROM ci_commits WHERE ci_commits.id = ci_builds.commit_id) WHERE tag IS NULL')
+  end
+end
diff --git a/db/migrate/20151005075649_add_user_id_to_build.rb b/db/migrate/20151005075649_add_user_id_to_build.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0f4b92b8b79f3d587772627bf6cfea01c7125518
--- /dev/null
+++ b/db/migrate/20151005075649_add_user_id_to_build.rb
@@ -0,0 +1,5 @@
+class AddUserIdToBuild < ActiveRecord::Migration
+  def change
+    add_column :ci_builds, :user_id, :integer
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 72609da93f14f05490b821504bb97bedab8b3dd2..0e8d54fb267b027e6af0f0cf091e7605602e465a 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: 20150930095736) do
+ActiveRecord::Schema.define(version: 20151005075649) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -100,8 +100,13 @@ ActiveRecord::Schema.define(version: 20150930095736) do
     t.boolean  "allow_failure",      default: false, null: false
     t.string   "stage"
     t.integer  "trigger_request_id"
+    t.integer  "stage_idx"
+    t.boolean  "tag"
+    t.string   "ref"
+    t.integer  "user_id"
   end
 
+  add_index "ci_builds", ["commit_id", "stage_idx", "created_at"], name: "index_ci_builds_on_commit_id_and_stage_idx_and_created_at", using: :btree
   add_index "ci_builds", ["commit_id"], name: "index_ci_builds_on_commit_id", using: :btree
   add_index "ci_builds", ["project_id", "commit_id"], name: "index_ci_builds_on_project_id_and_commit_id", using: :btree
   add_index "ci_builds", ["project_id"], name: "index_ci_builds_on_project_id", using: :btree
diff --git a/lib/ci/api/commits.rb b/lib/ci/api/commits.rb
index bac463a59096e1797c5ba627cbe1406a6be5b47c..a60769d83059f431585697a4137c2d721febb227 100644
--- a/lib/ci/api/commits.rb
+++ b/lib/ci/api/commits.rb
@@ -51,7 +51,7 @@ module Ci
           required_attributes! [:project_id, :data, :project_token]
           project = Ci::Project.find(params[:project_id])
           authenticate_project_token!(project)
-          commit = Ci::CreateCommitService.new.execute(project, params[:data])
+          commit = Ci::CreateCommitService.new.execute(project, current_user, params[:data])
 
           if commit.persisted?
             present commit, with: Entities::CommitWithBuilds
diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb
index e625e790df8f06175a5f7c42ebef95e80bb8dae4..c47951bc5d190e108e89bfc8b95e4afb851cbf00 100644
--- a/lib/ci/gitlab_ci_yaml_processor.rb
+++ b/lib/ci/gitlab_ci_yaml_processor.rb
@@ -85,9 +85,10 @@ module Ci
 
     def build_job(name, job)
       {
+        stage_idx: stages.index(job[:stage]),
         stage: job[:stage],
-        script: "#{@before_script.join("\n")}\n#{normalize_script(job[:script])}",
-        tags: job[:tags] || [],
+        commands: "#{@before_script.join("\n")}\n#{normalize_script(job[:script])}",
+        tag_list: job[:tags] || [],
         name: name,
         only: job[:only],
         except: job[:except],
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index 99da5a187769d86792457f154c7523bdcd2ff58d..21b582afba4648b177e0699ff21248a81b67ba6b 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -27,6 +27,8 @@
 
 FactoryGirl.define do
   factory :ci_build, class: Ci::Build do
+    ref 'master'
+    tag false
     started_at 'Di 29. Okt 09:51:28 CET 2013'
     finished_at 'Di 29. Okt 09:53:28 CET 2013'
     commands 'ls -a'
@@ -43,5 +45,9 @@ FactoryGirl.define do
       started_at nil
       finished_at nil
     end
+
+    factory :ci_build_tag do
+      tag true
+    end
   end
 end
diff --git a/spec/factories/ci/commits.rb b/spec/factories/ci/commits.rb
index 9c7a0e9cbe028097e590ae70178db1116bcef4fe..79e000b7ccb1c164e519f93d70904303a3ff3c51 100644
--- a/spec/factories/ci/commits.rb
+++ b/spec/factories/ci/commits.rb
@@ -17,60 +17,32 @@
 
 # Read about factories at https://github.com/thoughtbot/factory_girl
 FactoryGirl.define do
-  factory :ci_commit, class: Ci::Commit do
-    ref 'master'
-    before_sha '76de212e80737a608d939f648d959671fb0a0142'
+  factory :ci_empty_commit, class: Ci::Commit do
     sha '97de212e80737a608d939f648d959671fb0a0142'
-    push_data do
-      {
-        ref: 'refs/heads/master',
-        before: '76de212e80737a608d939f648d959671fb0a0142',
-        after: '97de212e80737a608d939f648d959671fb0a0142',
-        user_name: 'Git User',
-        user_email: 'git@example.com',
-        repository: {
-          name: 'test-data',
-          url: 'ssh://git@gitlab.com/test/test-data.git',
-          description: '',
-          homepage: 'http://gitlab.com/test/test-data'
-        },
-        commits: [
-          {
-            id: '97de212e80737a608d939f648d959671fb0a0142',
-            message: 'Test commit message',
-            timestamp: '2014-09-23T13:12:25+02:00',
-            url: 'https://gitlab.com/test/test-data/commit/97de212e80737a608d939f648d959671fb0a0142',
-            author: {
-              name: 'Git User',
-              email: 'git@user.com'
-            }
-          }
-        ],
-        total_commits_count: 1,
-        ci_yaml_file: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
-      }
-    end
 
     gl_project factory: :empty_project
 
     factory :ci_commit_without_jobs do
-      after(:create) do |commit, evaluator|
-        commit.push_data[:ci_yaml_file] = YAML.dump({})
-        commit.save
+      after(:build) do |commit|
+        allow(commit).to receive(:ci_yaml_file) { YAML.dump({}) }
       end
     end
 
     factory :ci_commit_with_one_job do
-      after(:create) do |commit, evaluator|
-        commit.push_data[:ci_yaml_file] = YAML.dump({ rspec: { script: "ls" } })
-        commit.save
+      after(:build) do |commit|
+        allow(commit).to receive(:ci_yaml_file) { YAML.dump({ rspec: { script: "ls" } }) }
       end
     end
 
     factory :ci_commit_with_two_jobs do
-      after(:create) do |commit, evaluator|
-        commit.push_data[:ci_yaml_file] = YAML.dump({ rspec: { script: "ls" }, spinach: { script: "ls" } })
-        commit.save
+      after(:build) do |commit|
+        allow(commit).to receive(:ci_yaml_file) { YAML.dump({ rspec: { script: "ls" }, spinach: { script: "ls" } }) }
+      end
+    end
+
+    factory :ci_commit do
+      after(:build) do |commit|
+        allow(commit).to receive(:ci_yaml_file) { File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml')) }
       end
     end
   end
diff --git a/spec/features/ci/commits_spec.rb b/spec/features/ci/commits_spec.rb
index 657a9dabe9e19b10665282aac13d63b79b9e3d39..b4236e1e589626bdf4cf95da32abcaff359c6627 100644
--- a/spec/features/ci/commits_spec.rb
+++ b/spec/features/ci/commits_spec.rb
@@ -11,6 +11,10 @@ describe "Commits" do
       @commit.project.gl_project.team << [@user, :master]
     end
 
+    before do
+      stub_ci_commit_to_return_yaml_file
+    end
+
     describe "GET /:project/commits/:sha" do
       before do
         visit ci_commit_path(@commit)
@@ -38,8 +42,7 @@ describe "Commits" do
       end
 
       it "shows warning" do
-        @commit.push_data[:ci_yaml_file] = nil
-        @commit.save
+        stub_ci_commit_yaml_file(nil)
 
         visit ci_commit_path(@commit)
 
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index 49482ac2b12c2fb5e8a7d7e24c17996af589e761..aba957da4881694b6795736bfc163b1b682b6786 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -17,11 +17,12 @@ module Ci
         expect(config_processor.builds_for_stage_and_ref(type, "master").size).to eq(1)
         expect(config_processor.builds_for_stage_and_ref(type, "master").first).to eq({
           stage: "test",
+          stage_idx: 1,
           except: nil,
           name: :rspec,
           only: nil,
-          script: "pwd\nrspec",
-          tags: [],
+          commands: "pwd\nrspec",
+          tag_list: [],
           options: {},
           allow_failure: false
         })
@@ -115,10 +116,11 @@ module Ci
         expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
           except: nil,
           stage: "test",
+          stage_idx: 1,
           name: :rspec,
           only: nil,
-          script: "pwd\nrspec",
-          tags: [],
+          commands: "pwd\nrspec",
+          tag_list: [],
           options: {
             image: "ruby:2.1",
             services: ["mysql"]
@@ -141,10 +143,11 @@ module Ci
         expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({
           except: nil,
           stage: "test",
+          stage_idx: 1,
           name: :rspec,
           only: nil,
-          script: "pwd\nrspec",
-          tags: [],
+          commands: "pwd\nrspec",
+          tag_list: [],
           options: {
             image: "ruby:2.5",
             services: ["postgresql"]
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index ca070a14975516f85bda039bc3cf2034bb15057d..da56f6e31ae11673498d682be19e8903c8881735 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -30,9 +30,12 @@ describe Ci::Build do
   let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project }
   let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
   let(:build) { FactoryGirl.create :ci_build, commit: commit }
+  subject { build }
 
   it { is_expected.to belong_to(:commit) }
+  it { is_expected.to belong_to(:user) }
   it { is_expected.to validate_presence_of :status }
+  it { is_expected.to validate_presence_of :ref }
 
   it { is_expected.to respond_to :success? }
   it { is_expected.to respond_to :failed? }
@@ -236,12 +239,6 @@ describe Ci::Build do
     it { is_expected.to eq(options) }
   end
 
-  describe :ref do
-    subject { build.ref }
-
-    it { is_expected.to eq(commit.ref) }
-  end
-
   describe :sha do
     subject { build.sha }
 
@@ -254,12 +251,6 @@ describe Ci::Build do
     it { is_expected.to eq(commit.short_sha) }
   end
 
-  describe :before_sha do
-    subject { build.before_sha }
-
-    it { is_expected.to eq(commit.before_sha) }
-  end
-
   describe :allow_git_fetch do
     subject { build.allow_git_fetch }
 
@@ -359,4 +350,38 @@ describe Ci::Build do
       end
     end
   end
+
+  describe :project_recipients do
+    let(:pusher_email) { 'pusher@gitlab.test' }
+    let(:user) { User.new(notification_email: pusher_email) }
+    subject { build.project_recipients }
+
+    before do
+      build.update_attributes(user: user)
+    end
+
+    it 'should return pusher_email as only recipient when no additional recipients are given' do
+      project.update_attributes(email_add_pusher: true,
+                                email_recipients: '')
+      is_expected.to eq([pusher_email])
+    end
+
+    it 'should return pusher_email and additional recipients' do
+      project.update_attributes(email_add_pusher: true,
+                                email_recipients: 'rec1 rec2')
+      is_expected.to eq(['rec1', 'rec2', pusher_email])
+    end
+
+    it 'should return recipients' do
+      project.update_attributes(email_add_pusher: false,
+                                email_recipients: 'rec1 rec2')
+      is_expected.to eq(['rec1', 'rec2'])
+    end
+
+    it 'should return unique recipients only' do
+      project.update_attributes(email_add_pusher: true,
+                                email_recipients: "rec1 rec1 #{pusher_email}")
+      is_expected.to eq(['rec1', pusher_email])
+    end
+  end
 end
diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb
index c04bbcbadc7ff43e1f0233bad7fd496c6ecf117b..acff1ddf0fcf274a1fef5112d3a2275d1dfb0f54 100644
--- a/spec/models/ci/commit_spec.rb
+++ b/spec/models/ci/commit_spec.rb
@@ -21,15 +21,10 @@ describe Ci::Commit do
   let(:project) { FactoryGirl.create :ci_project }
   let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project }
   let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
-  let(:commit_with_project) { FactoryGirl.create :ci_commit, gl_project: gl_project }
-  let(:config_processor) { Ci::GitlabCiYamlProcessor.new(gitlab_ci_yaml) }
 
   it { is_expected.to belong_to(:gl_project) }
   it { is_expected.to have_many(:builds) }
-  it { is_expected.to validate_presence_of :before_sha }
   it { is_expected.to validate_presence_of :sha }
-  it { is_expected.to validate_presence_of :ref }
-  it { is_expected.to validate_presence_of :push_data }
 
   it { is_expected.to respond_to :git_author_name }
   it { is_expected.to respond_to :git_author_email }
@@ -59,53 +54,6 @@ describe Ci::Commit do
     end
   end
 
-  describe :project_recipients do
-
-    context 'always sending notification' do
-      it 'should return commit_pusher_email as only recipient when no additional recipients are given' do
-        project = FactoryGirl.create :ci_project,
-          email_add_pusher: true,
-          email_recipients: ''
-        gl_project = FactoryGirl.create :empty_project, gitlab_ci_project: project
-        commit =  FactoryGirl.create :ci_commit, gl_project: gl_project
-        expected = 'commit_pusher_email'
-        allow(commit).to receive(:push_data) { { user_email: expected } }
-        expect(commit.project_recipients).to eq([expected])
-      end
-
-      it 'should return commit_pusher_email and additional recipients' do
-        project = FactoryGirl.create :ci_project,
-          email_add_pusher: true,
-          email_recipients: 'rec1 rec2'
-        gl_project = FactoryGirl.create :empty_project, gitlab_ci_project: project
-        commit = FactoryGirl.create :ci_commit, gl_project: gl_project
-        expected = 'commit_pusher_email'
-        allow(commit).to receive(:push_data) { { user_email: expected } }
-        expect(commit.project_recipients).to eq(['rec1', 'rec2', expected])
-      end
-
-      it 'should return recipients' do
-        project = FactoryGirl.create :ci_project,
-          email_add_pusher: false,
-          email_recipients: 'rec1 rec2'
-        gl_project = FactoryGirl.create :empty_project, gitlab_ci_project: project
-        commit = FactoryGirl.create :ci_commit, gl_project: gl_project
-        expect(commit.project_recipients).to eq(['rec1', 'rec2'])
-      end
-
-      it 'should return unique recipients only' do
-        project = FactoryGirl.create :ci_project,
-          email_add_pusher: true,
-          email_recipients: 'rec1 rec1 rec2'
-        gl_project = FactoryGirl.create :empty_project, gitlab_ci_project: project
-        commit = FactoryGirl.create :ci_commit, gl_project: gl_project
-        expected = 'rec2'
-        allow(commit).to receive(:push_data) { { user_email: expected } }
-        expect(commit.project_recipients).to eq(['rec1', 'rec2'])
-      end
-    end
-  end
-
   describe :valid_commit_sha do
     context 'commit.sha can not start with 00000000' do
       before do
@@ -117,63 +65,95 @@ describe Ci::Commit do
     end
   end
 
-  describe :compare? do
-    subject { commit_with_project.compare? }
-
-    context 'if commit.before_sha are not nil' do
-      it { is_expected.to be_truthy }
-    end
-  end
-
   describe :short_sha do
-    subject { commit.short_before_sha }
+    subject { commit.short_sha }
 
     it 'has 8 items' do
       expect(subject.size).to eq(8)
     end
-    it { expect(commit.before_sha).to start_with(subject) }
+    it { expect(commit.sha).to start_with(subject) }
   end
 
-  describe :short_sha do
-    subject { commit.short_sha }
+  describe :stage do
+    subject { commit.stage }
 
-    it 'has 8 items' do
-      expect(subject.size).to eq(8)
+    before do
+      @second = FactoryGirl.create :ci_build, commit: commit, name: 'deploy', stage: 'deploy', stage_idx: 1, status: :pending
+      @first = FactoryGirl.create :ci_build, commit: commit, name: 'test', stage: 'test', stage_idx: 0, status: :pending
+    end
+
+    it 'returns first running stage' do
+      is_expected.to eq('test')
+    end
+
+    context 'first build succeeded' do
+      before do
+        @first.update_attributes(status: :success)
+      end
+
+      it 'returns last running stage' do
+        is_expected.to eq('deploy')
+      end
+    end
+
+    context 'all builds succeeded' do
+      before do
+        @first.update_attributes(status: :success)
+        @second.update_attributes(status: :success)
+      end
+
+      it 'returns nil' do
+        is_expected.to be_nil
+      end
     end
-    it { expect(commit.sha).to start_with(subject) }
   end
 
   describe :create_next_builds do
-    before do
-      allow(commit).to receive(:config_processor).and_return(config_processor)
+  end
+
+  describe :create_builds do
+    let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
+
+    def create_builds(trigger_request = nil)
+      commit.create_builds('master', false, nil, trigger_request)
+    end
+
+    def create_next_builds(trigger_request = nil)
+      commit.create_next_builds('master', false, nil, trigger_request)
     end
 
-    it "creates builds for next type" do
-      expect(commit.create_builds).to be_truthy
+    it 'creates builds' do
+      expect(create_builds).to be_truthy
       commit.builds.reload
       expect(commit.builds.size).to eq(2)
 
-      expect(commit.create_next_builds(nil)).to be_truthy
+      expect(create_next_builds).to be_truthy
       commit.builds.reload
       expect(commit.builds.size).to eq(4)
 
-      expect(commit.create_next_builds(nil)).to be_truthy
+      expect(create_next_builds).to be_truthy
       commit.builds.reload
       expect(commit.builds.size).to eq(5)
 
-      expect(commit.create_next_builds(nil)).to be_falsey
+      expect(create_next_builds).to be_falsey
     end
-  end
 
-  describe :create_builds do
-    before do
-      allow(commit).to receive(:config_processor).and_return(config_processor)
-    end
+    context 'for different ref' do
+      def create_develop_builds
+        commit.create_builds('develop', false, nil, nil)
+      end
 
-    it 'creates builds' do
-      expect(commit.create_builds).to be_truthy
-      commit.builds.reload
-      expect(commit.builds.size).to eq(2)
+      it 'creates builds' do
+        expect(create_builds).to be_truthy
+        commit.builds.reload
+        expect(commit.builds.size).to eq(2)
+
+        expect(create_develop_builds).to be_truthy
+        commit.builds.reload
+        expect(commit.builds.size).to eq(4)
+        expect(commit.refs.size).to eq(2)
+        expect(commit.builds.pluck(:name).uniq.size).to eq(2)
+      end
     end
 
     context 'for build triggers' do
@@ -181,40 +161,39 @@ describe Ci::Commit do
       let(:trigger_request) { FactoryGirl.create :ci_trigger_request, commit: commit, trigger: trigger }
 
       it 'creates builds' do
-        expect(commit.create_builds(trigger_request)).to be_truthy
+        expect(create_builds(trigger_request)).to be_truthy
         commit.builds.reload
         expect(commit.builds.size).to eq(2)
       end
 
       it 'rebuilds commit' do
-        expect(commit.create_builds).to be_truthy
+        expect(create_builds).to be_truthy
         commit.builds.reload
         expect(commit.builds.size).to eq(2)
 
-        expect(commit.create_builds(trigger_request)).to be_truthy
+        expect(create_builds(trigger_request)).to be_truthy
         commit.builds.reload
         expect(commit.builds.size).to eq(4)
       end
 
       it 'creates next builds' do
-        expect(commit.create_builds(trigger_request)).to be_truthy
+        expect(create_builds(trigger_request)).to be_truthy
         commit.builds.reload
         expect(commit.builds.size).to eq(2)
 
-        expect(commit.create_next_builds(trigger_request)).to be_truthy
+        expect(create_next_builds(trigger_request)).to be_truthy
         commit.builds.reload
         expect(commit.builds.size).to eq(4)
       end
 
       context 'for [ci skip]' do
         before do
-          commit.push_data[:commits][0][:message] = 'skip this commit [ci skip]'
-          commit.save
+          allow(commit).to receive(:git_commit_message) { 'message [ci skip]' }
         end
 
         it 'rebuilds commit' do
           expect(commit.status).to eq('skipped')
-          expect(commit.create_builds(trigger_request)).to be_truthy
+          expect(create_builds(trigger_request)).to be_truthy
           commit.builds.reload
           expect(commit.builds.size).to eq(2)
           expect(commit.status).to eq('pending')
@@ -270,4 +249,59 @@ describe Ci::Commit do
       expect(commit.coverage).to be_nil
     end
   end
+
+  describe :should_create_next_builds? do
+    before do
+      @build1 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'master', tag: false, status: :success
+      @build2 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'develop', tag: false, status: :failed
+      @build3 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'master', tag: true, status: :failed
+      @build4 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: :success
+    end
+
+    context 'for success' do
+      it 'to create if all succeeded' do
+        expect(commit.should_create_next_builds?(@build4)).to be_truthy
+      end
+    end
+
+    context 'for failed' do
+      before do
+        @build4.update_attributes(status: :failed)
+      end
+
+      it 'to not create' do
+        expect(commit.should_create_next_builds?(@build4)).to be_falsey
+      end
+
+      context 'and ignore failures for current' do
+        before do
+          @build4.update_attributes(allow_failure: true)
+        end
+
+        it 'to create' do
+          expect(commit.should_create_next_builds?(@build4)).to be_truthy
+        end
+      end
+    end
+
+    context 'for running' do
+      before do
+        @build4.update_attributes(status: :running)
+      end
+
+      it 'to not create' do
+        expect(commit.should_create_next_builds?(@build4)).to be_falsey
+      end
+    end
+
+    context 'for retried' do
+      before do
+        @build5 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: :failed
+      end
+
+      it 'to not create' do
+        expect(commit.should_create_next_builds?(@build4)).to be_falsey
+      end
+    end
+  end
 end
diff --git a/spec/models/ci/project_services/hip_chat_message_spec.rb b/spec/models/ci/project_services/hip_chat_message_spec.rb
index 1903c036924104b013dbb9b33ffc8dad243defea..e23d6ae2c2844ae7e090011910096f32ba5aa44b 100644
--- a/spec/models/ci/project_services/hip_chat_message_spec.rb
+++ b/spec/models/ci/project_services/hip_chat_message_spec.rb
@@ -3,70 +3,37 @@ require 'spec_helper'
 describe Ci::HipChatMessage do
   subject { Ci::HipChatMessage.new(build) }
 
-  context "One build" do
-    let(:commit) { FactoryGirl.create(:ci_commit_with_one_job) }
+  let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) }
 
-    let(:build) do
-      commit.create_builds
-      commit.builds.first
-    end
-
-    context 'when build succeeds' do
-      it 'returns a successful message' do
-        build.update(status: "success")
-
-        expect( subject.status_color ).to eq 'green'
-        expect( subject.notify? ).to be_falsey
-        expect( subject.to_s ).to match(/Build '[^']+' #\d+/)
-        expect( subject.to_s ).to match(/Successful in \d+ second\(s\)\./)
-      end
-    end
-
-    context 'when build fails' do
-      it 'returns a failure message' do
-        build.update(status: "failed")
-
-        expect( subject.status_color ).to eq 'red'
-        expect( subject.notify? ).to be_truthy
-        expect( subject.to_s ).to match(/Build '[^']+' #\d+/)
-        expect( subject.to_s ).to match(/Failed in \d+ second\(s\)\./)
-      end
-    end
+  let(:build) do
+    commit.builds.first
   end
 
-  context "Several builds" do
-    let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) }
-
-    let(:build) do
-      commit.builds.first
-    end
-
-    context 'when all matrix builds succeed' do
-      it 'returns a successful message' do
-        commit.create_builds
-        commit.builds.update_all(status: "success")
-        commit.reload
+  context 'when all matrix builds succeed' do
+    it 'returns a successful message' do
+      commit.create_builds('master', false, nil)
+      commit.builds.update_all(status: "success")
+      commit.reload
 
-        expect( subject.status_color ).to eq 'green'
-        expect( subject.notify? ).to be_falsey
-        expect( subject.to_s ).to match(/Commit #\d+/)
-        expect( subject.to_s ).to match(/Successful in \d+ second\(s\)\./)
-      end
+      expect(subject.status_color).to eq 'green'
+      expect(subject.notify?).to be_falsey
+      expect(subject.to_s).to match(/Commit #\d+/)
+      expect(subject.to_s).to match(/Successful in \d+ second\(s\)\./)
     end
+  end
 
-    context 'when at least one matrix build fails' do
-      it 'returns a failure message' do
-        commit.create_builds
-        first_build = commit.builds.first
-        second_build = commit.builds.last
-        first_build.update(status: "success")
-        second_build.update(status: "failed")
-
-        expect( subject.status_color ).to eq 'red'
-        expect( subject.notify? ).to be_truthy
-        expect( subject.to_s ).to match(/Commit #\d+/)
-        expect( subject.to_s ).to match(/Failed in \d+ second\(s\)\./)
-      end
+  context 'when at least one matrix build fails' do
+    it 'returns a failure message' do
+      commit.create_builds('master', false, nil)
+      first_build = commit.builds.first
+      second_build = commit.builds.last
+      first_build.update(status: "success")
+      second_build.update(status: "failed")
+
+      expect(subject.status_color).to eq 'red'
+      expect(subject.notify?).to be_truthy
+      expect(subject.to_s).to match(/Commit #\d+/)
+      expect(subject.to_s).to match(/Failed in \d+ second\(s\)\./)
     end
   end
 end
diff --git a/spec/models/ci/mail_service_spec.rb b/spec/models/ci/project_services/mail_service_spec.rb
similarity index 95%
rename from spec/models/ci/mail_service_spec.rb
rename to spec/models/ci/project_services/mail_service_spec.rb
index 0d9f85959bafbfad1d0a6c944b9d39159cfb4e8e..04e870dce7f5d36778f61afb7736fc9825153789 100644
--- a/spec/models/ci/mail_service_spec.rb
+++ b/spec/models/ci/project_services/mail_service_spec.rb
@@ -29,12 +29,13 @@ describe Ci::MailService do
 
   describe 'Sends email for' do
     let(:mail)   { Ci::MailService.new }
+    let(:user)   { User.new(notification_email: 'git@example.com')}
 
     describe 'failed build' do
       let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true) }
       let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
       let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
-      let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit) }
+      let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit, user: user) }
 
       before do
         allow(mail).to receive_messages(
@@ -57,7 +58,7 @@ describe Ci::MailService do
       let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true, email_only_broken_builds: false) }
       let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
       let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
-      let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) }
+      let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) }
 
       before do
         allow(mail).to receive_messages(
@@ -85,7 +86,7 @@ describe Ci::MailService do
       end
       let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
       let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
-      let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) }
+      let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) }
 
       before do
         allow(mail).to receive_messages(
@@ -114,7 +115,7 @@ describe Ci::MailService do
       end
       let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
       let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
-      let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) }
+      let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) }
 
       before do
         allow(mail).to receive_messages(
@@ -143,7 +144,7 @@ describe Ci::MailService do
       end
       let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
       let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
-      let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit) }
+      let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) }
 
       before do
         allow(mail).to receive_messages(
@@ -166,7 +167,7 @@ describe Ci::MailService do
       end
       let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
       let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
-      let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit) }
+      let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit, user: user) }
 
       before do
         allow(mail).to receive_messages(
diff --git a/spec/models/ci/project_services/slack_message_spec.rb b/spec/models/ci/project_services/slack_message_spec.rb
index 7b541802d7d7693b0aa547b128e1242c57acf435..8adda6c86ccc84950f35290142b0e59dae010e43 100644
--- a/spec/models/ci/project_services/slack_message_spec.rb
+++ b/spec/models/ci/project_services/slack_message_spec.rb
@@ -3,80 +3,41 @@ require 'spec_helper'
 describe Ci::SlackMessage do
   subject { Ci::SlackMessage.new(commit) }
 
-  context "One build" do
-    let(:commit) { FactoryGirl.create(:ci_commit_with_one_job) }
+  let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) }
 
-    let(:build) do
-      commit.create_builds
-      commit.builds.first
-    end
-
-    context 'when build succeeded' do
-      let(:color) { 'good' }
+  context 'when all matrix builds succeeded' do
+    let(:color) { 'good' }
 
-      it 'returns a message with succeeded build' do
-        build.update(status: "success")
+    it 'returns a message with success' do
+      commit.create_builds('master', false, nil)
+      commit.builds.update_all(status: "success")
+      commit.reload
 
-        expect(subject.color).to eq(color)
-        expect(subject.fallback).to include('Build')
-        expect(subject.fallback).to include("\##{build.id}")
-        expect(subject.fallback).to include('succeeded')
-        expect(subject.attachments.first[:fields]).to be_empty
-      end
-    end
-
-    context 'when build failed' do
-      let(:color) { 'danger' }
-
-      it 'returns a message with failed build' do
-        build.update(status: "failed")
-
-        expect(subject.color).to eq(color)
-        expect(subject.fallback).to include('Build')
-        expect(subject.fallback).to include("\##{build.id}")
-        expect(subject.fallback).to include('failed')
-        expect(subject.attachments.first[:fields]).to be_empty
-      end
+      expect(subject.color).to eq(color)
+      expect(subject.fallback).to include('Commit')
+      expect(subject.fallback).to include("\##{commit.id}")
+      expect(subject.fallback).to include('succeeded')
+      expect(subject.attachments.first[:fields]).to be_empty
     end
   end
 
-  context "Several builds" do
-    let(:commit) { FactoryGirl.create(:ci_commit_with_two_jobs) }
-
-    context 'when all matrix builds succeeded' do
-      let(:color) { 'good' }
-
-      it 'returns a message with success' do
-        commit.create_builds
-        commit.builds.update_all(status: "success")
-        commit.reload
-
-        expect(subject.color).to eq(color)
-        expect(subject.fallback).to include('Commit')
-        expect(subject.fallback).to include("\##{commit.id}")
-        expect(subject.fallback).to include('succeeded')
-        expect(subject.attachments.first[:fields]).to be_empty
-      end
-    end
-
-    context 'when one of matrix builds failed' do
-      let(:color) { 'danger' }
-
-      it 'returns a message with information about failed build' do
-        commit.create_builds
-        first_build = commit.builds.first
-        second_build = commit.builds.last
-        first_build.update(status: "success")
-        second_build.update(status: "failed")
-
-        expect(subject.color).to eq(color)
-        expect(subject.fallback).to include('Commit')
-        expect(subject.fallback).to include("\##{commit.id}")
-        expect(subject.fallback).to include('failed')
-        expect(subject.attachments.first[:fields].size).to eq(1)
-        expect(subject.attachments.first[:fields].first[:title]).to eq(second_build.name)
-        expect(subject.attachments.first[:fields].first[:value]).to include("\##{second_build.id}")
-      end
+  context 'when one of matrix builds failed' do
+    let(:color) { 'danger' }
+
+    it 'returns a message with information about failed build' do
+      commit.create_builds('master', false, nil)
+      first_build = commit.builds.first
+      second_build = commit.builds.last
+      first_build.update(status: "success")
+      second_build.update(status: "failed")
+
+      expect(subject.color).to eq(color)
+      expect(subject.fallback).to include('Commit')
+      expect(subject.fallback).to include("\##{commit.id}")
+      expect(subject.fallback).to include('failed')
+      expect(subject.attachments.first[:fields].size).to eq(1)
+      expect(subject.attachments.first[:fields].first[:title]).to eq(second_build.name)
+      expect(subject.attachments.first[:fields].first[:value]).to include("\##{second_build.id}")
     end
   end
 end
diff --git a/spec/models/project_services/gitlab_ci_service_spec.rb b/spec/models/project_services/gitlab_ci_service_spec.rb
index 683a403a9071c56048f724410334d8e890da6bd7..c0b8a144c3add3279e20367f181a29cab2847599 100644
--- a/spec/models/project_services/gitlab_ci_service_spec.rb
+++ b/spec/models/project_services/gitlab_ci_service_spec.rb
@@ -39,8 +39,8 @@ describe GitlabCiService do
     end
 
     describe :build_page do
-      it { expect(@service.build_page("2ab7834c", 'master')).to eq("http://localhost/ci/projects/#{@ci_project.id}/refs/master/commits/2ab7834c")}
-      it { expect(@service.build_page("issue#2", 'master')).to eq("http://localhost/ci/projects/#{@ci_project.id}/refs/master/commits/issue%232")}
+      it { expect(@service.build_page("2ab7834c", 'master')).to eq("http://localhost/ci/projects/#{@ci_project.id}/commits/2ab7834c")}
+      it { expect(@service.build_page("issue#2", 'master')).to eq("http://localhost/ci/projects/#{@ci_project.id}/commits/issue%232")}
     end
 
     describe "execute" do
@@ -48,8 +48,8 @@ describe GitlabCiService do
       let(:project) { create(:project, name: 'project') }
       let(:push_sample_data) { Gitlab::PushDataBuilder.build_sample(project, user) }
 
-      it "calls ci_yaml_file" do
-        expect(@service).to receive(:ci_yaml_file).with(push_sample_data[:checkout_sha])
+      it "calls CreateCommitService" do
+        expect_any_instance_of(Ci::CreateCommitService).to receive(:execute).with(@ci_project, user, push_sample_data)
 
         @service.execute(push_sample_data)
       end
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index bad250fbf484b86d7c490d02b9fc644b122865a7..54c1d0199f6a88b57026a882ee6b42df3259f166 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -7,6 +7,10 @@ describe Ci::API::API do
   let(:project) { FactoryGirl.create(:ci_project) }
   let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
 
+  before do
+    stub_ci_commit_to_return_yaml_file
+  end
+
   describe "Builds API for runners" do
     let(:shared_runner) { FactoryGirl.create(:ci_runner, token: "SharedRunner") }
     let(:shared_project) { FactoryGirl.create(:ci_project, name: "SharedProject") }
@@ -19,7 +23,7 @@ describe Ci::API::API do
     describe "POST /builds/register" do
       it "should start a build" do
         commit = FactoryGirl.create(:ci_commit, gl_project: gl_project)
-        commit.create_builds
+        commit.create_builds('master', false, nil)
         build = commit.builds.first
 
         post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
@@ -55,7 +59,7 @@ describe Ci::API::API do
 
       it "returns options" do
         commit = FactoryGirl.create(:ci_commit, gl_project: gl_project)
-        commit.create_builds
+        commit.create_builds('master', false, nil)
 
         post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
 
@@ -65,7 +69,7 @@ describe Ci::API::API do
 
       it "returns variables" do
         commit = FactoryGirl.create(:ci_commit, gl_project: gl_project)
-        commit.create_builds
+        commit.create_builds('master', false, nil)
         project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value")
 
         post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
@@ -82,7 +86,7 @@ describe Ci::API::API do
         commit = FactoryGirl.create(:ci_commit, gl_project: gl_project)
 
         trigger_request = FactoryGirl.create(:ci_trigger_request_with_variables, commit: commit, trigger: trigger)
-        commit.create_builds(trigger_request)
+        commit.create_builds('master', false, nil, trigger_request)
         project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value")
 
         post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
diff --git a/spec/requests/ci/api/commits_spec.rb b/spec/requests/ci/api/commits_spec.rb
index a41c321a30043f5001efe4effd31fb2dbb51484d..6049135fd104f799c8757e81824dfc185a7c5495 100644
--- a/spec/requests/ci/api/commits_spec.rb
+++ b/spec/requests/ci/api/commits_spec.rb
@@ -44,8 +44,7 @@ describe Ci::API::API, 'Commits' do
               "email" => "jordi@softcatala.org",
             }
           }
-        ],
-        ci_yaml_file: gitlab_ci_yaml
+        ]
       }
     end
 
diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb
index bbe98e7dacdb947a0c4c09bf211ce153ba6aed65..93617fc4b3f2dc9eee0b9656a3d53f1aab77b7e3 100644
--- a/spec/requests/ci/api/triggers_spec.rb
+++ b/spec/requests/ci/api/triggers_spec.rb
@@ -5,8 +5,8 @@ describe Ci::API::API do
 
   describe 'POST /projects/:project_id/refs/:ref/trigger' do
     let!(:trigger_token) { 'secure token' }
-    let!(:project) { FactoryGirl.create(:ci_project) }
-    let!(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
+    let!(:gl_project) { FactoryGirl.create(:project) }
+    let!(:project) { FactoryGirl.create(:ci_project, gl_project: gl_project) }
     let!(:project2) { FactoryGirl.create(:ci_project) }
     let!(:trigger) { FactoryGirl.create(:ci_trigger, project: project, token: trigger_token) }
     let(:options) do
@@ -15,6 +15,10 @@ describe Ci::API::API do
       }
     end
 
+    before do
+      stub_ci_commit_to_return_yaml_file
+    end
+
     context 'Handles errors' do
       it 'should return bad request if token is missing' do
         post ci_api("/projects/#{project.id}/refs/master/trigger")
@@ -33,15 +37,13 @@ describe Ci::API::API do
     end
 
     context 'Have a commit' do
-      before do
-        @commit = FactoryGirl.create(:ci_commit, gl_project: gl_project)
-      end
+      let(:commit) { project.commits.last }
 
       it 'should create builds' do
         post ci_api("/projects/#{project.id}/refs/master/trigger"), options
         expect(response.status).to eq(201)
-        @commit.builds.reload
-        expect(@commit.builds.size).to eq(2)
+        commit.builds.reload
+        expect(commit.builds.size).to eq(2)
       end
 
       it 'should return bad request with no builds created if there\'s no commit for that ref' do
@@ -70,8 +72,8 @@ describe Ci::API::API do
         it 'create trigger request with variables' do
           post ci_api("/projects/#{project.id}/refs/master/trigger"), options.merge(variables: variables)
           expect(response.status).to eq(201)
-          @commit.builds.reload
-          expect(@commit.builds.first.trigger_request.variables).to eq(variables)
+          commit.builds.reload
+          expect(commit.builds.first.trigger_request.variables).to eq(variables)
         end
       end
     end
diff --git a/spec/requests/ci/commits_spec.rb b/spec/requests/ci/commits_spec.rb
index 3ab8c915dfd9b739e532ab92539d9c666392e1d6..f43a3982d718ec2759e1700e79cebf74b9c22178 100644
--- a/spec/requests/ci/commits_spec.rb
+++ b/spec/requests/ci/commits_spec.rb
@@ -7,7 +7,7 @@ describe "Commits" do
 
   describe "GET /:project/refs/:ref_name/commits/:id/status.json" do
     before do
-      get status_ci_project_ref_commits_path(@commit.project, @commit.ref, @commit.sha), format: :json
+      get status_ci_project_commits_path(@commit.project, @commit.sha), format: :json
     end
 
     it { expect(response.status).to eq(200) }
diff --git a/spec/services/ci/create_commit_service_spec.rb b/spec/services/ci/create_commit_service_spec.rb
index 84ab0a615dd603882b22a37ac6e3fefe643131c0..e3a8fe9681b6c87a84c1ff7732deae4d8f9be062 100644
--- a/spec/services/ci/create_commit_service_spec.rb
+++ b/spec/services/ci/create_commit_service_spec.rb
@@ -4,15 +4,19 @@ module Ci
   describe CreateCommitService do
     let(:service) { CreateCommitService.new }
     let(:project) { FactoryGirl.create(:ci_project) }
+    let(:user) { nil }
+
+    before do
+      stub_ci_commit_to_return_yaml_file
+    end
 
     describe :execute do
       context 'valid params' do
         let(:commit) do
-          service.execute(project,
+          service.execute(project, user,
             ref: 'refs/heads/master',
             before: '00000000',
             after: '31das312',
-            ci_yaml_file: gitlab_ci_yaml,
             commits: [ { message: "Message" } ]
           )
         end
@@ -26,11 +30,10 @@ module Ci
 
       context "skip tag if there is no build for it" do
         it "creates commit if there is appropriate job" do
-          result = service.execute(project,
+          result = service.execute(project, user,
             ref: 'refs/tags/0_1',
             before: '00000000',
             after: '31das312',
-            ci_yaml_file: gitlab_ci_yaml,
             commits: [ { message: "Message" } ]
           )
           expect(result).to be_persisted
@@ -38,12 +41,12 @@ module Ci
 
         it "creates commit if there is no appropriate job but deploy job has right ref setting" do
           config = YAML.dump({ deploy: { deploy: "ls", only: ["0_1"] } })
+          stub_ci_commit_yaml_file(config)
 
-          result = service.execute(project,
+          result = service.execute(project, user,
             ref: 'refs/heads/0_1',
             before: '00000000',
             after: '31das312',
-            ci_yaml_file: config,
             commits: [ { message: "Message" } ]
           )
           expect(result).to be_persisted
@@ -51,11 +54,11 @@ module Ci
       end
 
       it 'fails commits without .gitlab-ci.yml' do
-        result = service.execute(project,
+        stub_ci_commit_yaml_file(nil)
+        result = service.execute(project, user,
                                  ref: 'refs/heads/0_1',
                                  before: '00000000',
                                  after: '31das312',
-                                 ci_yaml_file: config,
                                  commits: [ { message: 'Message' } ]
         )
         expect(result).to be_persisted
@@ -64,41 +67,46 @@ module Ci
       end
 
       describe :ci_skip? do
+        let(:message) { "some message[ci skip]" }
+
+        before do
+          allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { message }
+        end
+
         it "skips builds creation if there is [ci skip] tag in commit message" do
-          commits = [{ message: "some message[ci skip]" }]
-          commit = service.execute(project,
+          commits = [{ message: message }]
+          commit = service.execute(project, user,
             ref: 'refs/tags/0_1',
             before: '00000000',
             after: '31das312',
-            commits: commits,
-            ci_yaml_file: gitlab_ci_yaml
+            commits: commits
           )
           expect(commit.builds.any?).to be false
           expect(commit.status).to eq("skipped")
         end
 
         it "does not skips builds creation if there is no [ci skip] tag in commit message" do
-          commits = [{ message: "some message" }]
+          allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { "some message" }
 
-          commit = service.execute(project,
+          commits = [{ message: "some message" }]
+          commit = service.execute(project, user,
             ref: 'refs/tags/0_1',
             before: '00000000',
             after: '31das312',
-            commits: commits,
-            ci_yaml_file: gitlab_ci_yaml
+            commits: commits
           )
 
           expect(commit.builds.first.name).to eq("staging")
         end
 
         it "skips builds creation if there is [ci skip] tag in commit message and yaml is invalid" do
-          commits = [{ message: "some message[ci skip]" }]
-          commit = service.execute(project,
+          stub_ci_commit_yaml_file('invalid: file')
+          commits = [{ message: message }]
+          commit = service.execute(project, user,
                                    ref: 'refs/tags/0_1',
                                    before: '00000000',
                                    after: '31das312',
-                                   commits: commits,
-                                   ci_yaml_file: "invalid: file"
+                                   commits: commits
           )
           expect(commit.builds.any?).to be false
           expect(commit.status).to eq("skipped")
@@ -106,35 +114,36 @@ module Ci
       end
 
       it "skips build creation if there are already builds" do
+        allow_any_instance_of(Ci::Commit).to receive(:ci_yaml_file) { gitlab_ci_yaml }
+
         commits = [{ message: "message" }]
-        commit = service.execute(project,
+        commit = service.execute(project, user,
           ref: 'refs/heads/master',
           before: '00000000',
           after: '31das312',
-          commits: commits,
-          ci_yaml_file: gitlab_ci_yaml
+          commits: commits
         )
         expect(commit.builds.count(:all)).to  eq(2)
 
-        commit = service.execute(project,
+        commit = service.execute(project, user,
           ref: 'refs/heads/master',
           before: '00000000',
           after: '31das312',
-          commits: commits,
-          ci_yaml_file: gitlab_ci_yaml
+          commits: commits
         )
         expect(commit.builds.count(:all)).to eq(2)
       end
 
       it "creates commit with failed status if yaml is invalid" do
+        stub_ci_commit_yaml_file('invalid: file')
+
         commits = [{ message: "some message" }]
 
-        commit = service.execute(project,
+        commit = service.execute(project, user,
                                  ref: 'refs/tags/0_1',
                                  before: '00000000',
                                  after: '31das312',
-                                 commits: commits,
-                                 ci_yaml_file: "invalid: file"
+                                 commits: commits
         )
 
         expect(commit.status).to eq("failed")
diff --git a/spec/services/ci/create_trigger_request_service_spec.rb b/spec/services/ci/create_trigger_request_service_spec.rb
index 525a24cc20048870271926d7589d048d60996c6a..fcafae386448090f89f881b6b5dd5d7508847855 100644
--- a/spec/services/ci/create_trigger_request_service_spec.rb
+++ b/spec/services/ci/create_trigger_request_service_spec.rb
@@ -2,20 +2,20 @@ require 'spec_helper'
 
 describe Ci::CreateTriggerRequestService do
   let(:service) { Ci::CreateTriggerRequestService.new }
-  let(:project) { FactoryGirl.create :ci_project }
-  let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project }
-  let(:trigger) { FactoryGirl.create :ci_trigger, project: project }
+  let(:gl_project) { create(:project) }
+  let(:project) { create(:ci_project, gl_project: gl_project) }
+  let(:trigger) { create(:ci_trigger, project: project) }
+
+  before do
+    stub_ci_commit_to_return_yaml_file
+  end
 
   describe :execute do
     context 'valid params' do
       subject { service.execute(project, trigger, 'master') }
 
-      before do
-        @commit = FactoryGirl.create :ci_commit, gl_project: gl_project
-      end
-
       it { expect(subject).to be_kind_of(Ci::TriggerRequest) }
-      it { expect(subject.commit).to eq(@commit) }
+      it { expect(subject.builds.first).to be_kind_of(Ci::Build) }
     end
 
     context 'no commit for ref' do
@@ -28,26 +28,11 @@ describe Ci::CreateTriggerRequestService do
       subject { service.execute(project, trigger, 'master') }
 
       before do
-        FactoryGirl.create :ci_commit_without_jobs, gl_project: gl_project
+        stub_ci_commit_yaml_file('{}')
+        FactoryGirl.create :ci_commit, gl_project: gl_project
       end
 
       it { expect(subject).to be_nil }
     end
-
-    context 'for multiple commits' do
-      subject { service.execute(project, trigger, 'master') }
-
-      before do
-        @commit1 = FactoryGirl.create :ci_commit, committed_at: 2.hour.ago, gl_project: gl_project
-        @commit2 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, gl_project: gl_project
-        @commit3 = FactoryGirl.create :ci_commit, committed_at: 3.hour.ago, gl_project: gl_project
-      end
-
-      context 'retries latest one' do
-        it { expect(subject).to be_kind_of(Ci::TriggerRequest) }
-        it { expect(subject).to be_persisted }
-        it { expect(subject.commit).to eq(@commit2) }
-      end
-    end
   end
 end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 05bb32baa903e2468517d9e5e2ac9357933c0d3d..2be13bb3e6a0dd95d1c8962724b12e18517011e0 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -43,4 +43,8 @@ RSpec.configure do |config|
   end
 end
 
+FactoryGirl::SyntaxRunner.class_eval do
+  include RSpec::Mocks::ExampleMethods
+end
+
 ActiveRecord::Migration.maintain_test_schema!
diff --git a/spec/support/stub_gitlab_calls.rb b/spec/support/stub_gitlab_calls.rb
index 5e6744afda120a338235015e1f88f8a693e2237b..5b3eb1bfc5f26f9ea7a1140fc439e716014f1068 100644
--- a/spec/support/stub_gitlab_calls.rb
+++ b/spec/support/stub_gitlab_calls.rb
@@ -13,6 +13,14 @@ module StubGitlabCalls
     allow_any_instance_of(Network).to receive(:projects) { project_hash_array }
   end
 
+  def stub_ci_commit_to_return_yaml_file
+    stub_ci_commit_yaml_file(gitlab_ci_yaml)
+  end
+
+  def stub_ci_commit_yaml_file(ci_yaml)
+    allow_any_instance_of(Ci::Commit).to receive(:ci_yaml_file) { ci_yaml }
+  end
+
   private
 
   def gitlab_url